修复:隔离上游数据库连接探测

This commit is contained in:
WuYongJie 2026-07-22 16:12:48 +08:00
parent 204a7f373a
commit a364d857b4

View File

@ -6,6 +6,8 @@ use Illuminate\Support\Facades\Log;
use PDO;
use PDOException;
use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
use Symfony\Component\Process\PhpExecutableFinder;
use Symfony\Component\Process\Process;
/**
* 按来源隔离的 SQL Server PDO_ODBC 连接管理。
@ -41,6 +43,67 @@ class UpstreamOdbcConnection
}
public static function probeConfiguration(array $connection, string $label, string $source = 'custom'): void
{
try {
$payload = json_encode([
'connection' => $connection,
'label' => $label,
'source' => $source,
], JSON_THROW_ON_ERROR);
$php = (new PhpExecutableFinder())->find(false);
if (! is_string($php) || $php === '') {
throw new \RuntimeException('PHP CLI 不可用。');
}
$script = <<<'PHP'
require getcwd() . '/vendor/autoload.php';
$app = require getcwd() . '/bootstrap/app.php';
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$kernel->bootstrap();
$payload = json_decode(stream_get_contents(STDIN), true, 512, JSON_THROW_ON_ERROR);
Modules\EcommerceFinance\Services\UpstreamOdbcConnection::probeConfigurationInProcess(
(array) $payload['connection'],
(string) $payload['label'],
(string) $payload['source'],
);
PHP;
$connectTimeout = min(max((int) ($connection['connect_timeout'] ?? 5), 1), 30);
$process = new Process(
[$php, '-d', 'display_errors=0', '-r', $script],
base_path(),
null,
$payload,
min(max($connectTimeout + 3, 5), 15),
);
$process->run();
if ($process->isSuccessful()) {
return;
}
Log::warning('上游数据库隔离探测失败。', [
'source' => $source,
'source_label' => $label,
'exit_code' => $process->getExitCode(),
]);
} catch (\Throwable $exception) {
Log::warning('上游数据库隔离探测无法完成。', [
'source' => $source,
'source_label' => $label,
'exception' => $exception::class,
]);
}
throw new ServiceUnavailableHttpException(60, '上游数据库连接探测失败,请检查配置。');
}
/**
* 仅由隔离的 PHP CLI 子进程调用,避免 ODBC 原生崩溃拖垮 Web 请求进程。
*
* @internal
*/
public static function probeConfigurationInProcess(array $connection, string $label, string $source = 'custom'): void
{
$pdo = self::open($connection, $source, $label);