|
|
@@ -1043,14 +1043,13 @@ class ExamPdfExportService
|
|
|
|
|
|
$process = new Process([
|
|
|
$chromeBinary,
|
|
|
- '--headless',
|
|
|
+ '--headless=new', // 【优化】使用新渲染引擎
|
|
|
'--disable-gpu',
|
|
|
'--no-sandbox',
|
|
|
'--disable-setuid-sandbox',
|
|
|
'--disable-dev-shm-usage',
|
|
|
- '--no-zygote',
|
|
|
+ '--disable-web-security',
|
|
|
'--disable-features=VizDisplayCompositor',
|
|
|
- '--disable-software-rasterizer',
|
|
|
'--disable-extensions',
|
|
|
'--disable-background-networking',
|
|
|
'--disable-component-update',
|
|
|
@@ -1058,31 +1057,36 @@ class ExamPdfExportService
|
|
|
'--disable-default-apps',
|
|
|
'--disable-domain-reliability',
|
|
|
'--disable-sync',
|
|
|
- '--safebrowsing-disable-auto-update',
|
|
|
'--no-first-run',
|
|
|
'--no-default-browser-check',
|
|
|
'--disable-crash-reporter',
|
|
|
'--disable-print-preview',
|
|
|
- '--disable-features=PrintHeaderFooter',
|
|
|
'--disable-features=TranslateUI',
|
|
|
'--disable-features=OptimizationHints',
|
|
|
'--disable-ipc-flooding-protection',
|
|
|
- '--disable-background-networking',
|
|
|
'--disable-background-timer-throttling',
|
|
|
'--disable-backgrounding-occluded-windows',
|
|
|
'--disable-renderer-backgrounding',
|
|
|
'--disable-features=AudioServiceOutOfProcess',
|
|
|
+ '--single-process', // 【优化】单进程模式,减少资源占用
|
|
|
+ '--disable-gpu-sandbox',
|
|
|
+ '--disable-software-rasterizer',
|
|
|
+ '--disable-background-mode',
|
|
|
+ '--disable-extensions-http-throttling',
|
|
|
+ '--disable-ipc-flooding-protection',
|
|
|
'--user-data-dir=' . $userDataDir,
|
|
|
'--print-to-pdf=' . $tmpPdf,
|
|
|
'--print-to-pdf-no-header',
|
|
|
'--allow-file-access-from-files',
|
|
|
+ '--font-render-hinting=none', // 【优化】禁用字体渲染提示
|
|
|
+ '--disable-font-antialiasing',
|
|
|
'file://' . $htmlPath,
|
|
|
], null, [
|
|
|
'HOME' => $runtimeHome,
|
|
|
'XDG_RUNTIME_DIR' => $runtimeXdg,
|
|
|
]);
|
|
|
|
|
|
- $process->setTimeout(120);
|
|
|
+ $process->setTimeout(60); // 【优化】减少超时到60秒
|
|
|
$killSignal = \defined('SIGKILL') ? \SIGKILL : 9;
|
|
|
|
|
|
try {
|
|
|
@@ -1090,33 +1094,46 @@ class ExamPdfExportService
|
|
|
$process->start();
|
|
|
$pdfGenerated = false;
|
|
|
|
|
|
- // 轮询检测PDF是否生成
|
|
|
+ // 轮询检测PDF是否生成(【优化】减少超时和间隔)
|
|
|
$pollStart = microtime(true);
|
|
|
- $maxPollSeconds = 90;
|
|
|
+ $maxPollSeconds = 50; // 【优化】从90秒减少到50秒
|
|
|
while ($process->isRunning() && (microtime(true) - $pollStart) < $maxPollSeconds) {
|
|
|
if (file_exists($tmpPdf) && filesize($tmpPdf) > 0) {
|
|
|
$pdfGenerated = true;
|
|
|
- $process->stop(5, $killSignal);
|
|
|
+ Log::info('ExamPdfExportService: PDF生成成功,提前终止Chrome进程', [
|
|
|
+ 'elapsed' => round(microtime(true) - $startedAt, 2),
|
|
|
+ 'pdf_size' => filesize($tmpPdf),
|
|
|
+ ]);
|
|
|
+ $process->stop(3, $killSignal); // 【优化】减少停止等待时间
|
|
|
break;
|
|
|
}
|
|
|
- usleep(200_000);
|
|
|
+ usleep(100_000); // 【优化】从200ms减少到100ms
|
|
|
}
|
|
|
|
|
|
if ($process->isRunning()) {
|
|
|
- $process->stop(5, $killSignal);
|
|
|
+ $process->stop(3, $killSignal); // 【优化】减少停止等待时间
|
|
|
}
|
|
|
|
|
|
- $process->wait();
|
|
|
+ // 【优化】删除不必要的wait()调用,避免重复等待
|
|
|
+ // $process->wait();
|
|
|
|
|
|
} catch (ProcessTimedOutException|ProcessSignaledException $e) {
|
|
|
if ($process->isRunning()) {
|
|
|
- $process->stop(5, $killSignal);
|
|
|
+ $process->stop(3, $killSignal); // 【优化】减少停止等待时间
|
|
|
}
|
|
|
+ Log::warning('ExamPdfExportService: Chrome进程超时或被信号中断', [
|
|
|
+ 'elapsed' => round((microtime(true) - $startedAt), 2),
|
|
|
+ 'exception' => get_class($e),
|
|
|
+ ]);
|
|
|
return $this->handleChromeProcessResult($tmpPdf, $userDataDir, $process, $startedAt);
|
|
|
} catch (\Throwable $e) {
|
|
|
if ($process->isRunning()) {
|
|
|
- $process->stop(5, $killSignal);
|
|
|
+ $process->stop(3, $killSignal); // 【优化】减少停止等待时间
|
|
|
}
|
|
|
+ Log::error('ExamPdfExportService: Chrome进程异常', [
|
|
|
+ 'elapsed' => round((microtime(true) - $startedAt), 2),
|
|
|
+ 'error' => $e->getMessage(),
|
|
|
+ ]);
|
|
|
return $this->handleChromeProcessResult($tmpPdf, $userDataDir, $process, null);
|
|
|
}
|
|
|
|
|
|
@@ -1130,28 +1147,36 @@ class ExamPdfExportService
|
|
|
{
|
|
|
$pdfExists = file_exists($tmpPdf);
|
|
|
$pdfSize = $pdfExists ? filesize($tmpPdf) : null;
|
|
|
+ $elapsed = $startedAt ? round((microtime(true) - $startedAt), 2) : null;
|
|
|
+
|
|
|
+ // 【优化】即使进程未成功,只要PDF存在且大小合理就返回
|
|
|
+ if ($pdfExists && $pdfSize > 1000) { // 至少1KB
|
|
|
+ Log::info('ExamPdfExportService: PDF生成成功', [
|
|
|
+ 'elapsed' => $elapsed,
|
|
|
+ 'pdf_size' => $pdfSize,
|
|
|
+ 'exit_code' => $process->getExitCode(),
|
|
|
+ 'is_successful' => $process->isSuccessful(),
|
|
|
+ ]);
|
|
|
|
|
|
- if (!$process->isSuccessful()) {
|
|
|
- if ($pdfExists && $pdfSize > 0) {
|
|
|
- Log::warning('ExamPdfExportService: Chrome进程异常但生成了PDF', [
|
|
|
- 'exit_code' => $process->getExitCode(),
|
|
|
- 'tmp_pdf_size' => $pdfSize,
|
|
|
- ]);
|
|
|
- } else {
|
|
|
- Log::error('ExamPdfExportService: Chrome渲染失败', [
|
|
|
- 'exit_code' => $process->getExitCode(),
|
|
|
- 'error' => $process->getErrorOutput(),
|
|
|
- ]);
|
|
|
- @unlink($tmpPdf);
|
|
|
- File::deleteDirectory($userDataDir);
|
|
|
- return null;
|
|
|
- }
|
|
|
+ $pdfBinary = file_get_contents($tmpPdf);
|
|
|
+ @unlink($tmpPdf);
|
|
|
+ File::deleteDirectory($userDataDir);
|
|
|
+ return $pdfBinary;
|
|
|
}
|
|
|
|
|
|
- $pdfBinary = $pdfExists ? file_get_contents($tmpPdf) : null;
|
|
|
+ // 如果PDF不存在或太小,记录错误
|
|
|
+ Log::error('ExamPdfExportService: Chrome渲染失败', [
|
|
|
+ 'elapsed' => $elapsed,
|
|
|
+ 'pdf_exists' => $pdfExists,
|
|
|
+ 'pdf_size' => $pdfSize,
|
|
|
+ 'exit_code' => $process->getExitCode(),
|
|
|
+ 'error' => $process->getErrorOutput(),
|
|
|
+ 'output' => $process->getOutput(),
|
|
|
+ ]);
|
|
|
+
|
|
|
@unlink($tmpPdf);
|
|
|
File::deleteDirectory($userDataDir);
|
|
|
- return $pdfBinary ?: null;
|
|
|
+ return null;
|
|
|
}
|
|
|
|
|
|
/**
|