Zpět na domů

Konkurence v Laravelu s TrueAsync — technický návod

Článek popisuje integraci Laravelu s TrueAsync pro dosažení skutečné konkurence. Podrobně jsou rozebrány mechanismy kontextové izolace, přizpůsobení stateful služeb a práce s PDO Pool. Řešení umožňuje zpracovávat stovky požadavků v jednom vlákně bez Swoole.

TrueAsync místo Swoole: architektura konkurence v Laravelu
Advertisement 728x90

# TrueAsync v Laravelu: architektura skutečné konkurence bez Swoole

Tradiční řešení pro asynchronnost v PHP, jako Swoole nebo RoadRunner, vyžadují hlubokou modifikaci kódu a zavedení barevných funkcí. TrueAsync nabízí zásadně jiný přístup — modifikaci jádra Zend pro průhlednou asynchronnost. Probereme, jak přizpůsobit Laravel tomuto modelu, překonat problémy stateful služeb a zajistit konkurenční zpracování operací vázaných na I/O.

Základy TrueAsync: jak funguje průhledná asynchronnost

TrueAsync je alternativní jádro PHP, které modifikuje Zend Engine, I/O knihovny a práci s DB. Jeho klíčová výhoda spočívá v absenci problému barevných funkcí. Na rozdíl od Node.js nebo Python asyncio běžné funkce PHP (PDO::query(), file_get_contents()) automaticky předají řízení plánovači v okamžiku čekání na I/O. Korutiny se pozastavují na úrovni C funkcí, což umožňuje zpracovávat stovky požadavků v jednom vlákně.

Důležité je pochopit: nejde o vícevláknovost. Model je založen na cooperative multitasking — přepínání kontextů při čekání na externí zdroje. Pro úlohy vázané na CPU (náročné výpočty) není efektivní, ale ideální pro typické webové scénáře s dotazy k DB a API. Příklad základního použití:

Google AdInline article slot
use function Async\spawn;
use function Async\await;
use function Async\delay;

$a = spawn(function() {
    delay(100);
    return 'result A';
});

$b = spawn(function() {
    delay(100);
    return 'result B';
});

// Obecný čas provedení ~100 ms
// Celkový čas provedení ~100 ms
echo await($a); 
echo await($b);

Problém stateful služeb v Laravelu

Laravel byl původně navržen pro model FPM, kde se každý požadavek zpracovává v izolovaném procesu. Stateful služby (AuthManager, Session, Router) ukládají stav do vlastností objektů, které se po zpracování požadavku zničí. V async režimu proces běží neustále, což vede k únikům stavu:

// Korutina A: požadavek od user_1
Auth::loginUsingId(1);
delay(200);

// Korutina B: požadavek od user_2  
Auth::loginUsingId(2);

// Návrat do korutiny A
echo Auth::id(); // Vrátí 2 místo 1

Statické vlastnosti (Model::$dispatcher, Facade::$resolvedInstance) a služby v IoC kontejneru se stávají společnými pro všechny požadavky. Octane tento problém řeší klonováním kontejneru, ale to přináší režii a neřeší statické vlastnosti.

Kontextová izolace: architektura řešení

TrueAsync poskytuje current_context() — úložiště dat vázané na životní cyklus požadavku. Při ukončení korutiny se kontext automaticky vyčistí. Architektura dělí Laravel na dvě zóny:

Google AdInline article slot
  • Statické jádro: router, konfigurace, IoC kontejner. Načte se jednou, společné pro všechny požadavky.
  • Služby na požadavek: Auth, Session, Cookie. Vytvářejí se v kontextu každé korutiny.

Klíčový mechanismus — zachycení resolvinu služeb přes AsyncApplication::resolve():

protected function resolve($abstract, ...)
{
    if ($this->asyncMode && $this->isScoped($abstract)) {
        $ctx = current_context();
        
        if ($instance = $ctx->find($abstract)) {
            return $instance;
        }
        
        $instance = $this->buildFresh($abstract);
        $ctx->set($abstract, $instance);
        return $instance;
    }
    
    return parent::resolve($abstract, ...);
}

Kontextové klíče přes enum a řešení pro Facades

Pro vyhnutí se kolizím a zajištění bezpečného přístupu se používají enum klíče:

enum ScopedService: string
{
    case REQUEST     = 'request';
    case SESSION     = 'session';
    case AUTH        = 'auth';
    case AUTH_DRIVER = 'auth.driver';
    case COOKIE      = 'cookie';
}

To poskytuje:

Google AdInline article slot
  • Izolaci přístupu (pouze vlastník enum může číst/psát data)
  • Záruku absence kolizí (i při stejných řetězcových hodnotách)
  • Podporu statické analýzy (PHPStan/IDE sledují použití)

Facades vyžadovaly samostatné řešení kvůli cachování resolvedInstance ve statickém poli. Implementován ScopedServiceProxy:

class ScopedServiceProxy
{
    public function __call($method, $args)
    {
        return ($this->resolver)()->$method(...$args);
    }
}

Proxy přesměrovává volání do kontextu aktuální korutiny a udržuje cache na úrovni proxy.

Kritické adaptace a PDO Pool

Po základní izolaci se projevily služby ukládající stav na požadavek uvnitř sebe. Byly potřeba specializované async verze:

  • AsyncRouter (izolace aktuální trasy)
  • AsyncDispatcher (odložené události na korutinu)
  • AsyncTranslator (lokalizace překladů)
  • AsyncViewFactory (View::share())
  • AsyncConfig (změny konfigurace za běhu)
  • AsyncDatabaseSessionHandler (atomární zápis session)
  • Async\*Connection (počítadlo transakcí)

Osobitá pozornost věnována práci s DB. Bez poolu spojení souběžné dotazy z korutin porušují protokol PostgreSQL. TrueAsync to řeší vestavěným PDO Pooolem:

$pdo = new PDO($dsn, $user, $password, [
    PDO::ATTR_POOL_ENABLED              => true,
    PDO::ATTR_POOL_MIN                  => 0,
    PDO::ATTR_POOL_MAX                  => 10,
    PDO::ATTR_POOL_HEALTHCHECK_INTERVAL => 30,
]);

Pool dynamicky rozděluje spojení: po obdržení výsledku SELECT se spojení vrátí do poolu a obslouží více korutin, než je maximální velikost poolu. Pro transakce se spojení fixuje do commit/rollback.

Co je důležité

  • TrueAsync eliminuje problém barevných funkcí modifikací jádra PHP
  • Stateful služby Laravel vyžadují kontextovou izolaci přes current_context()
  • PDO Pool je klíčový pro správnou práci s DB v konkurenčním prostředí
  • Enum klíče zajišťují bezpečnost a sledovatelnost kontextových dat
  • Řešení nenahrazuje vícevláknovost pro úlohy vázané na CPU

— Editorial Team

Advertisement 728x90

Číst dál