تخطَّ إلى المحتوى

Artisan في الإنتاج

في بيئة الإنتاج، احقن مُصيِّرًا مُهيَّأً ومُسجِّلًا متوافقًا مع توصية معايير ⁨PHP⁩ رقم 3 (⁨PSR-3⁩)، وأعد استخدام عملية ⁨Chrome⁩ الحية بين عمليات التصيير، وحدِّد ارتفاعات صريحة للمستندات متعددة العناصر، وقيِّد مسار التصيير بمهلة زمنية يفرضها المصدر الأعلى.

يُبقي BrowserPool عملية ⁨Chrome⁩ واحدة حية (keepAlive: true) ويعيد تشغيلها كل 100 عملية تصيير للحد من نمو استهلاك الذاكرة، وهو نمط تراكم معروف في عملاء بروتوكول أدوات مطوري ⁨Chrome⁩ (⁨CDP⁩) طويلة العمر. في عامل المعالجة الذي يُصيِّر مستندات كثيرة، استخدم مُصيِّرًا واحدًا طويل العمر بدلًا من مُصيِّر لكل طلب، حتى لا تتحمّل تكلفة بدء تشغيل ⁨Chrome⁩ إلا نادرًا.

نموذج برمجي — بيئة الإنتاج

قسم بعنوان «نموذج برمجي — بيئة الإنتاج»
render-service.php
<?php
declare(strict_types=1);
use NextPDF\Artisan\ChromeHtmlRenderer;
use NextPDF\Artisan\ChromeRendererConfig;
use NextPDF\Artisan\Exception\ChromeNotAvailableException;
use NextPDF\Artisan\Exception\ChromeRenderException;
use Psr\Log\LoggerInterface;
final class ReportRenderer
{
private ChromeHtmlRenderer $renderer;
public function __construct(LoggerInterface $logger)
{
$config = ChromeRendererConfig::fromArray([
'chrome_binary' => getenv('CHROME_BINARY') ?: null,
'render_timeout' => 45,
'max_html_size' => 2_000_000,
'no_sandbox' => (bool) getenv('CHROME_NO_SANDBOX'),
]);
$this->renderer = new ChromeHtmlRenderer($config, $logger);
}
public function render(string $html, float $widthPt, float $heightPt = 0.0): string
{
try {
return $this->renderer->render($html, $widthPt, $heightPt)->getPdfData();
} catch (ChromeNotAvailableException $e) {
// Deployment fault: Chrome runtime missing. Page the on-call owner.
throw $e;
} catch (ChromeRenderException $e) {
// Render-time fault: timeout, crash, empty output. Retryable once.
throw $e;
}
}
public function shutdown(): void
{
$this->renderer->close();
}
}

أنشئ المُصيِّر مرة واحدة، ثم أعد استخدامه. استدعِ close() عند إيقاف عامل المعالجة لتحرير عملية ⁨Chrome⁩ على نحو حتمي بدلًا من انتظار المُدمِّر. يميّز فرعا الالتقاط بين عطل في النشر (بيئة تشغيل مفقودة) وعطل في وقت التصيير (قابل لإعادة المحاولة). لا تستخدم كتل catch فارغة.

اربطه في الحاوية كنسخة وحيدة (⁨singleton⁩):

$container->singleton(ReportRenderer::class, fn ($c) =>
new ReportRenderer($c->get(Psr\Log\LoggerInterface::class)));

عند حذف الارتفاع، يقيس الجسر ارتفاع المحتوى في ⁨Chrome⁩ (max لارتفاعات التمرير والإزاحة لـ ⁨body/document⁩)، ويحوِّله إلى نقاط، ويضيف هامش أمان يقارب 0.2 بوصة (نحو 14.4 نقطة). يغطي هذا الهامش الفجوة بين تخطيط منفذ العرض في ⁨Chrome⁩ وإعادة تدفق تخطيط الطباعة فيه. ومن دونه، قد يدفع printToPDF المحتوى إلى صفحة ثانية يقصها PageImporter (الصفحة 0 فقط). يفرض الجسر حدًا أدنى لارتفاع الورقة يبلغ 0.1 بوصة. تؤكد الاختبارات ChromeHtmlRendererTest::renderUsesAutoFitHeightByDefault، و::renderAutoFitBufferIsAddedNotSubtracted، و::renderAppliesMinimumHeightOf0Point1InchForTinyExplicitHeight هذا السلوك.

بالنسبة إلى المستندات ذات التخطيط الثابت (مثل الفواتير والشهادات)، مرِّر ارتفاعًا صريحًا بالنقاط. عندما يكون الارتفاع صريحًا، لا يُضاف أي هامش، ويطابق الخرج حجم الورقة المطلوب تمامًا (وهو ما يؤكده ::renderHonorsExplicitHeightWithoutAutoBuffer).

  • أنشئ مُصيِّرًا واحدًا لكل عامل معالجة، وأعد استخدامه. يعيد BrowserPool استخدام المتصفح الحي ويعيد التشغيل تلقائيًا عند حد الـ 100 عملية تصيير.
  • استدعِ close() عند إيقاف عامل المعالجة وبين الدُفعات الكبيرة عندما تريد عملية ⁨Chrome⁩ جديدة قبل بلوغ حد الـ 100 عملية تصيير.
  • يستدعي المُدمِّر close()، لكن استدعاء close() الصريح حتمي ومُفضَّل في العمليات طويلة التشغيل.
  • تُسجَّل إشعارات إعادة التشغيل عند مستوى notice مع عدد عمليات التصيير؛ أطلق تنبيهًا عند ارتفاع معدل إعادة التشغيل لأنه قد يشير إلى مستندات أثقل من المتوقع.

احقن مُسجِّلًا متوافقًا مع ⁨PSR-3.⁩ يُصدر المُصيِّر الأحداث والمستويات الآتية:

الحدثالمستوىالسياق
بدء التصييرdebugsize، width، height
اكتمال التصييرdebugpdfSize، contentHeight
تشغيل المتصفحinfobinary
إعادة تشغيل المتصفحnoticecount
إغلاق المتصفحdebugrenderCount

لا تُسجَّل أي بايتات ⁨HTML⁩ أو ⁨PDF⁩ أو نص مُستخرَج. يُبقي ذلك الحمولات خارج سجلات التشغيل، ويتوافق مع إرشادات محتوى السجلات في المنشور الخاص للمعهد الوطني للمعايير والتقنية (⁨NIST SP⁩) 800-92. أنشئ أهداف مستوى الخدمة (⁨SLOs⁩) لزمن الاستجابة من زوج ⁨start/complete⁩، وأنشئ تنبيهًا لمعدل إعادة التشغيل من أحداث notice.

  • ⁨Sidecar Chrome⁩: شغِّل ⁨Chrome⁩ في الحاوية نفسها التي يعمل فيها عامل المعالجة ⁨PHP⁩؛ ثبِّت chrome_binary. جهِّز حاوية قادرة على تشغيل بيئة معزولة (⁨sandbox⁩)؛ راجع /⁨integrations/artisan/chrome-renderer-setup/.⁩
  • بدون حاوية / ⁨CLI⁩: لا تتوفر لدى ⁨Artisan⁩ حاوية لحقن التبعيات. استخدم EInvoiceServiceFactory لعقود الفوترة الإلكترونية في إصدار ⁨Premium⁩ ضمن مشغِّلات واجهة سطر الأوامر (⁨CLI⁩)؛ راجع /⁨integrations/artisan/boot-and-discovery/.⁩
  • تقييد الموارد: اقرن render_timeout بميزانية طلب من المصدر الأعلى وبأداة على المضيف من نوع ⁨cgroup/ulimit.⁩ راجع نموذج التهديد في /⁨integrations/artisan/security-and-operations/.⁩
  • المُصيِّر الذي يُقاطَع في منتصف التصيير يغلق صفحة ⁨Chrome⁩ مع ذلك (finally)، ويظلّ المجمع قابلًا للاستخدام.
  • إعادة استخدام مُصيِّر واحد عبر ⁨threads/processes⁩ غير مدعومة؛ فكل مُصيِّر يملك عملية ⁨Chrome⁩ واحدة.
  • إعادة التشغيل عند الـ 100 عملية تصيير ثابتة؛ فحدِّد أحجام الدُفعات مع وضع ذلك في الاعتبار حتى تبقى ارتفاعات زمن الاستجابة قابلة للتنبؤ.

التكلفة في الحالة المستقرة هي تخطيط ⁨Chrome⁩ للمدخل إضافةً إلى printToPDF، وليست عبء الجسر. يوزِّع keepAlive تكلفة بدء التشغيل على عمليات التصيير. توقَّع ارتفاعًا في زمن الاستجابة عند كل عملية تصيير رقم 100 (إعادة تشغيل العملية)؛ فأبرِزه في أهداف مستوى الخدمة (⁨SLOs⁩) بدلًا من التعامل معه كحادث تشغيلي.

تستقبل مسارات الإنتاج محتوى ⁨HTML⁩ غير موثوق. أعد قراءة /⁨integrations/artisan/security-and-operations/.⁩ تظل حواجز الشبكة فعّالة بغض النظر عن التهيئة، لكن no_sandbox: true يزيل عزل عملية ⁨Chrome⁩ ويرفع متطلب الثقة المفروض على المدخل.

في عمال المعالجة بدون حاوية، يُرجِع EInvoiceServiceFactory القيمة null عند عدم تثبيت إصدار ⁨Premium⁩، فيستمر مسار التصيير مفتوح المصدر دون تغيير. ثبِّت ⁨Pro/Enterprise⁩ لتفعيل تضمين الفاتورة الإلكترونية والتحقق منها داخل المستند المُصيَّر.

  • /integrations/artisan/quickstart/
  • /integrations/artisan/configuration/
  • /integrations/artisan/security-and-operations/
  • /integrations/artisan/chrome-renderer-setup/
  • /integrations/artisan/troubleshooting/