Wydajne serwery z Swoole
Proszę obsługiwać tysiące połączeń za pomocą Swoole opartego na korutynach.
Wydajne serwery z Swoole to bezpłatna lekcja PHP Academy na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej PHP Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs PHP Academy zawiera 4 lekcji w sumie.
Czym jest Swoole
Swoole (a także jego fork OpenSwoole) to rozszerzenie C, które przekształca PHP w wysokowydajne środowisko uruchomieniowe oparte na korutynach i sterowane zdarzeniami. Zamiast uruchamiać proces dla każdego żądania przez PHP-FPM, serwer Swoole uruchamia aplikację raz i pozostawia ją w pamięci, obsługując żądania za pomocą puli długotrwale działających workerów z wbudowanymi korutynami. Rezultat to wzrost przepustowości o rząd wielkości oraz natywna współbieżność w ramach pojedynczego żądania.
Trwały serwer HTTP
Klasyczny przykład: kompletny serwer HTTP w skrypcie. Nasłuchuje, przekazuje każde żądanie workerowi i nigdy nie inicjalizuje frameworka ponownie. Należy pamiętać, że wymaga to ext-swoole — nie działa ze standardowym PHP CLI.
<?php
$server = new Swoole\Http\Server('0.0.0.0', 9501);
$server->set(['worker_num' => 4]);
$server->on('request', function ($request, $response) {
$response->header('Content-Type', 'application/json');
$response->end(json_encode(['hello' => $request->server['request_uri']]));
});
echo "listening on :9501\n";
$server->start();Korutyny zamiast callbacków
Najważniejszą funkcją Swoole są korutyny: pozwalają pisać sekwencyjny kod w stylu blokującym, który środowisko uruchomieniowe planuje w sposób kooperacyjny. Coroutine\run() otwiera kontekst korutyny, a go() uruchamia nową korutynę. Każda korutyna ma własny stos, podobnie jak Fiber, ale Swoole automatycznie planuje ich wykonywanie w punktach operacji wejścia-wyjścia.
<?php
use Swoole\Coroutine as Co;
Co\run(function () {
go(function () { Co::sleep(1); echo "A done\n"; });
go(function () { Co::sleep(1); echo "B done\n"; });
// Both sleep concurrently -> total ~1s, not 2s
});Przechwytywanie funkcji blokujących
Mechanizmem, który sprawia, że korutyny są wygodne, są haki środowiska uruchomieniowego. Swoole przezroczysto przekształca blokujące wywołania — PDO, Redis, curl, sleep, file_get_contents — w nieblokujące wersje obsługujące korutyny. Zwykłe wywołanie $pdo->query() oddaje sterowanie zamiast zamrażać workera.
<?php
use Swoole\Runtime;
use Swoole\Coroutine as Co;
Runtime::enableCoroutine(SWOOLE_HOOK_ALL); // hook PDO, curl, sleep, etc.
Co\run(function () {
go(function () { sleep(1); echo "task 1\n"; }); // sleep() is now async!
go(function () { sleep(1); echo "task 2\n"; });
}); // finishes in ~1s because sleep was hookedWspółbieżne operacje wejścia-wyjścia z WaitGroup
Do rozdzielania pracy i oczekiwania na jej zakończenie Swoole udostępnia WaitGroup (podobnie jak sync.WaitGroup w Go). Należy uruchomić korutyny, wywołać add() dla każdego zadania, done() po jego zakończeniu oraz wait(), aby współbieżnie zaczekać na ukończenie wszystkich zadań.
<?php
use Swoole\Coroutine as Co;
use Swoole\Coroutine\WaitGroup;
Co\run(function () {
$wg = new WaitGroup();
$results = [];
foreach (['eu', 'us', 'asia'] as $region) {
$wg->add();
go(function () use ($wg, $region, &$results) {
Co::sleep(0.5); // simulated API call
$results[$region] = "ok-$region";
$wg->done();
});
}
$wg->wait(); // all three ran in parallel (~0.5s)
var_dump($results);
});Kanały komunikacji
Korutyny koordynują pracę za pomocą Channels — typowanych kolejek o ograniczonym rozmiarze, które wstrzymują producenta, gdy są pełne, i konsumenta, gdy są puste. Jest to komunikacja w stylu CSP, która całkowicie eliminuje potrzebę blokad współdzielonego stanu.
<?php
use Swoole\Coroutine as Co;
use Swoole\Coroutine\Channel;
Co\run(function () {
$chan = new Channel(2);
go(function () use ($chan) {
foreach (range(1, 3) as $n) $chan->push($n);
$chan->push(null); // sentinel
});
go(function () use ($chan) {
while (($v = $chan->pop()) !== null) echo "got $v\n";
});
});Pule połączeń
Trwały serwer nie powinien otwierać połączenia z bazą danych dla każdego żądania. Swoole udostępnia bezpieczne dla korutyn pule połączeń, dzięki którym workery ponownie wykorzystują stały zestaw połączeń w tysiącach współbieżnych korutyn, eliminując ciągłe otwieranie i zamykanie połączeń.
<?php
use Swoole\Database\PDOConfig;
use Swoole\Database\PDOPool;
use Swoole\Coroutine as Co;
Co\run(function () {
$pool = new PDOPool((new PDOConfig())
->withHost('127.0.0.1')->withDbname('app')
->withUsername('u')->withPassword('p'), 8);
go(function () use ($pool) {
$pdo = $pool->get(); // borrow
$row = $pdo->query('SELECT NOW()')->fetch();
$pool->put($pdo); // return to pool
var_dump($row);
});
});Wycieki stanu: pułapka trwałej pamięci
Ponieważ proces działa przez wiele żądań, stan globalny i statyczny pozostaje zachowany. Rozrastająca się bez ograniczeń pamięć podręczna, singleton przechowujący dane konkretnego żądania lub niezakończona transakcja DB mogą zakłócać obsługę kolejnych żądań i wyczerpać pamięć. Zasady są następujące: należy unikać przechowywania danych związanych z żądaniem w statycznych właściwościach i singletonach, resetować kontekst dla każdego żądania oraz ustawić max_request, aby workery były okresowo ponownie uruchamiane.
<?php
$server = new Swoole\Http\Server('0.0.0.0', 9501);
$server->set([
'worker_num' => 8,
'max_request' => 10000, // recycle a worker after N requests to bound leaks
]);
// Never store $request data in a static property between requests!Kontekst lokalny dla korutyny
Ponieważ jeden worker przeplata wykonywanie wielu korutyn, nie można używać zwykłych właściwości statycznych do przechowywania danych konkretnego żądania. Swoole udostępnia każdej korutynie kontekst (Coroutine::getContext()) — obiekt, którego czas życia odpowiada czasowi życia korutyny, idealny do przechowywania bieżącego użytkownika, identyfikatora żądania lub uchwytu transakcji.
<?php
use Swoole\Coroutine as Co;
Co\run(function () {
go(function () {
$ctx = Co::getContext();
$ctx['request_id'] = 'req-123';
// ... deep call chain can read Co::getContext()['request_id']
echo Co::getContext()['request_id'], PHP_EOL;
});
});Model procesów i task workers
Swoole uruchamia proces master (zarządza wątkami pętli zdarzeń), proces manager (uruchamia i monitoruje workery), procesy worker (wykonują handlery żądań i korutyny) oraz opcjonalne task workers do ciężkich, blokujących lub obciążających CPU zadań przekazywanych za pomocą $server->task(). Task workers należy stosować do przetwarzania obrazów lub długich operacji obciążających CPU, aby nie spowalniały korutyn obsługujących żądania — zapewnia to zarówno współbieżność, jak i równoległość.
Odroczenie i timery
Swoole pozwala zaplanować pracę po zakończeniu bieżącej korutyny za pomocą Coroutine::defer() — jest to idealne rozwiązanie do niezawodnego zwalniania zasobów z puli, nawet jeśli zostanie rzucony wyjątek. Timery działające na poziomie całego serwera (Timer::tick) wykonują okresowe zadania, takie jak odświeżanie pamięci podręcznej, wewnątrz workera bez zewnętrznego crona.
<?php
use Swoole\Coroutine as Co;
Co\run(function () {
go(function () {
$conn = 'borrowed-connection';
Co::defer(function () use ($conn) {
echo "released $conn\n"; // runs when coroutine exits, even on error
});
echo "using $conn\n";
});
});Szybkie sprawdzenie
Dlaczego należy unikać przechowywania danych żądania we właściwościach statycznych w serwerze Swoole?
Podsumowanie
Swoole przekształca PHP w rezydentny serwer oparty na korutynach:
- Aplikacja uruchamia się raz, a pula workerów obsługuje żądania za pomocą wbudowanych korutyn (
Co\run/go()). - Haki środowiska uruchomieniowego sprawiają, że zwykłe operacje PDO, curl i sleep są nieblokujące; WaitGroup i Channels koordynują współbieżną pracę.
- Pule połączeń ponownie wykorzystują połączenia z bazą danych w wielu korutynach.
- Trwała pamięć oznacza konieczność unikania wycieków stanu: należy używać
Coroutine::getContext()do przechowywania danych konkretnego żądania, ustawićmax_requesti przekazywać ciężką pracę do task workers.
Często zadawane pytania
Czy lekcja „Wydajne serwery z Swoole” jest bezpłatna?
Tak — pełny tekst „Wydajne serwery z Swoole” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu PHP Academy, przejdź na CoddyKit PRO. Kurs PHP Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Wydajne serwery z Swoole”?
Proszę obsługiwać tysiące połączeń za pomocą Swoole opartego na korutynach. Ćwiczysz PHP Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć PHP Academy?
Nie wymagamy żadnego doświadczenia. PHP Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.
Ile czasu zajmuje lekcja „Wydajne serwery z Swoole”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji PHP Academy?
Tak. Każda lekcja PHP Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Model współbieżności PHP
- Fibers w PHP 8.1
- Pętle zdarzeń z ReactPHP
- Wydajne serwery z Swoole