Fungování Nette ve Worker modelu – kompatibilita?
- DefenestrationPraha
- Člen | 127
Existuje prostředí jménem FrankenPHP, které podporuje i worker režim, ve kterém běží několik paralelních threadů – workerů, jež servírují obsah „na střídačku“. Mám poměrně velkou aplikaci, kterou bych chtěl zrychlit.
Vyšel jsem tedy z následujícího demoprojektu:
https://github.com/…w/worker.php
a napsal jsem si vlastní worker. Chodí mi to už poměrně dobře, a zrychlení proti běžnému Apache / Nginx je masivní, ze jinak stejného docker prostředí cca 2x – 3×.
Můj worker vypadá zhruba takto:
// This is a worker script for FrankenPHP
// Prevent worker script termination when a client connection is interrupted
ignore_user_abort(true);
require __DIR__ . '/../vendor/autoload.php';
// Create container, but not initialize it yet.
// Initialization will be done inside the loop, because of nette/http and sessions.
$configurator = KrakenControl\Bootstrap::boot();
$container = $configurator->createContainer(false);
// Handler outside the loop for better performance (doing less work)
$handler = static function () use ($configurator, $container) {
$container = clone $container;
$container->removeService('security.user');
$container->removeService('latte.templateFactory');
$container->removeService('security.userStorage');
$container->removeService('session.session');
$container->removeService('http.response');
$container->removeService('http.request');
$container->addService('http.request', $container->getByType(RequestFactory::class)->fromGlobals());
// Run Nette app
try {
$container->initialize();
$container->getByType(Application::class)->run();
} catch (Throwable $e) {
Debugger::getLogger()->log($e->getMessage(), ILogger::ERROR);
}
};
Jaký je problém? V podstatě dva.
Za prvé se mi takto nepodařilo rozchodit Tracy. Už jsem zjistil, v čem je asi problém: ona ten svůj výpis realizuje pomocí shutdownHandleru, a ten se prostě nezavolá. Ony ty workery za normálních okolností výkonem jednoho requestu nekončí…
Za druhé nevím, zda tím nebourám něco uvnitř Nette. Ono je poměrně hodně založené na tom, že se kontejner vytváří znovu.
Ale to zlepšení výkonu ve Worker modelu je tak masivní, že bych s tím rád dál experimentoval.
Proto bych ocenil názory veteránů, zda je to vůbec možné, zda by to stálo za to, atd.
- Marek Bartoš
- Nette Blogger | 1275
Bylo by dobré tohle podporovat, ale chce to dobře promyslet. Většina rozšíření nepočítá s tím, že by služba byla odebrána a znova přidána a s tím, že by měly běžet přes více requestů též ne.
Odebrat a přidat službu zafunguje jen, když na ní při kompilaci nenastavuješ dodatečný setup. Též už může být odkazovaná jinou službou, která se vytvořila a vytvořenou instanci uloženou v property takto nenahradíš. Je třeba resetovat interní stav služeb.
Symfony DI na to má ResetInterface. Bylo by dobré něco takového přidat i do Nette DI.
Šel by nahradit i ten shutdown handler. Tracy specificky ale dělá hodně „magie“, aby se dovedla přidat do stránky za jakékoli situace. Tam bude nejspíš třeba hlubší integrace do nette/application, symfony/console a jiných vstupních bodů aplikace
Editoval Marek Bartoš (26. 11. 1:41)
- DefenestrationPraha
- Člen | 127
Marek Bartoš napsal(a):
Bylo by dobré tohle podporovat, ale chce to dobře promyslet. Většina rozšíření nepočítá s tím, že by služba byla odebrána a znova přidána a s tím, že by měly běžet přes více requestů též ne.
Odebrat a přidat službu zafunguje jen, když na ní při kompilaci nenastavuješ dodatečný setup. Též už může být odkazovaná jinou službou, která se vytvořila a vytvořenou instanci uloženou v property takto nenahradíš. Je třeba resetovat interní stav služeb.
Symfony DI na to má ResetInterface. Bylo by dobré něco takového přidat i do Nette DI.
V případě že potřeba by nahradil i ten shutdown handler.** Tracy specificky ale dělá hodně „magie“, aby se dovedla přidat do stránky za jakékoli situace.** Tam bude nejspíš třeba hlubší integrace do nette/application, symfony/console a jiných vstupních bodů aplikace
Jo, to musím potvrdit! Je to velmi magický a sofistikovaný nástroj.
Mezitím jsem se do toho ponořil, podařilo se mi přidat <script> s Tracy na závěr stránky pomocí relativně jednoduchého Latte:
{var $frankenConfig = $_SERVER['FRANKENPHP_CONFIG'] ?? 'none'}
{if str_contains($frankenConfig, 'worker') && Tracy\Debugger::isEnabled() && Tracy\Debugger::$showBar}
{\Tracy\Debugger::getStrategy()->renderBar()}
{/if}
které má tedy tu slabinu, že spoléhá na metodu označenou jako internal … čili jen experimentální postup. Ale zase jsem narazil na další problém. Ten skript spoléhá na metodu DeferredContent::sendAssets, že mu pošle ten JS k vykreslení:
$asset = $_GET['_tracy_bar'] ?? null;
if ($asset === 'js') {
header('Content-Type: application/javascript; charset=UTF-8');
header('Cache-Control: max-age=864000');
header_remove('Pragma');
header_remove('Set-Cookie');
$str = $this->buildJsCss();
header('Content-Length: ' . strlen($str));
echo $str;
flush();
return true;
}
No, a to se z nějakého důvodu nevykoná, a zatím nevím proč. Bohužel Xdebug v Dockeru je peklo.
Editoval DefenestrationPraha (24. 11. 7:07)