Dlaczego warto używać komunikacji asynchronicznej
Proszę zobaczyć, jak kolejki rozdzielają producentów od konsumentów.
Dlaczego warto używać komunikacji asynchronicznej to bezpłatna lekcja PHP Academy na CoddyKit. To lekcja 1 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.
Dlaczego komunikacja asynchroniczna
W synchronicznym żądaniu proces PHP jest blokowany podczas komunikacji z bramkami pocztowymi, operatorami płatności lub usługami zależnymi. Przy dużym obciążeniu wiąże to opóźnienie i dostępność aplikacji z każdą wywoływaną zależnością. Komunikacja asynchroniczna zrywa ten łańcuch: producent umieszcza wiadomość w kolejce i natychmiast zwraca wynik, a konsumenci przetwarzają ją później, we własnym tempie.
W tej lekcji omówiono dlaczego — rozdzielenie zależności, buforowanie oraz kompromisy związane z gwarancjami dostarczenia, które należy przeanalizować przed rozpoczęciem pracy z RabbitMQ lub Kafka.
Odsprzężenie przestrzenne i czasowe
Kolejka odsprzęga producentów i konsumentów w dwóch wymiarach:
- Przestrzennym — żadna ze stron nie potrzebuje adresu drugiej; obie znają tylko brokera.
- Czasowym — konsument może być niedostępny w chwili publikowania wiadomości przez producenta; wiadomość czeka.
Poniższa wersja synchroniczna wiąże żądanie internetowe z dostępnością i szybkością serwera pocztowego.
<?php
// Synchronous: the HTTP request blocks on SMTP
function registerUser(string $email): void {
saveUser($email);
// If SMTP is slow or down, the user waits or the request fails
sendWelcomeEmail($email); // 800ms blocking call
echo "Registered\n";
}
function saveUser(string $e): void { /* ... */ }
function sendWelcomeEmail(string $e): void { usleep(1); }
registerUser('dev@example.com');Opublikuj i działaj dalej
Wersja asynchroniczna zapisuje użytkownika, publikuje wiadomość UserRegistered i zwraca wynik. Osobny worker wysyła wiadomość e-mail. Opóźnienie HTTP zależy teraz wyłącznie od szybkiego opublikowania wiadomości w lokalnym brokerze, a nie od pełnej wymiany z serwerem SMTP.
<?php
function registerUser(string $email): void {
saveUser($email);
// Publish a lightweight event; worker handles email later
publish('user.registered', json_encode(['email' => $email]));
echo "Registered (email queued)\n";
}
function saveUser(string $e): void { /* ... */ }
function publish(string $routingKey, string $payload): void {
echo "-> queued $routingKey: $payload\n";
}
registerUser('dev@example.com');Wyrównywanie obciążenia (buforowanie)
Skoki ruchu są nierównomierne, a zdolność przetwarzania jest stała. Kolejka działa jak bufor: pochłania serię 10 000 wiadomości i pozwala puli workerów opróżniać ją w możliwym do utrzymania tempie. Bez kolejki taki skok bezpośrednio przeciążyłby bazę danych lub dalsze API.
To właśnie wyrównywanie obciążenia — w zamian za stabilność (system się nie przeciąża) akceptuje się większe opóźnienie (wiadomości mogą przez krótki czas czekać).
Gwarancje dostarczenia
Każdy system komunikatów składa określoną obietnicę dotyczącą dostarczenia. Należy wiedzieć, którą z nich zapewnia:
- Co najwyżej raz — wyślij i zapomnij; wiadomości mogą zostać utracone, ale nigdy nie są duplikowane.
- Co najmniej raz — wiadomość jest dostarczana ponownie aż do potwierdzenia; duplikaty są możliwe. To najczęstsze ustawienie domyślne.
- Dokładnie raz — brak utraty i duplikatów; rozwiązanie kosztowne, a na poziomie aplikacji często będące tylko częściową realizacją tego założenia.
Ponieważ większość rzeczywistych systemów zapewnia dostarczenie co najmniej raz, konsumenci muszą tolerować otrzymanie tej samej wiadomości dwukrotnie.
Idempotentni konsumenci
Rozwiązaniem problemu duplikatów przy dostarczeniu co najmniej raz jest idempotencja: dwukrotne przetworzenie wiadomości daje taki sam wynik jak przetworzenie jej raz. Typową techniką jest klucz deduplikacji (identyfikator wiadomości) przechowywany w unikatowym indeksie.
<?php
function handle(array $msg, PDO $pdo): void {
$pdo->beginTransaction();
try {
// Unique constraint on message_id makes the insert the dedup gate
$stmt = $pdo->prepare(
'INSERT INTO processed_messages (id) VALUES (?)'
);
$stmt->execute([$msg['id']]);
} catch (PDOException $e) {
$pdo->rollBack();
echo "Duplicate {$msg['id']} skipped\n";
return; // already handled
}
chargeCustomer($msg['amount']);
$pdo->commit();
}
function chargeCustomer(int $a): void {}Potwierdzenia i ponowne dostarczenie
Konsument sygnalizuje sukces za pomocą ack. Jeśli ulegnie awarii przed wysłaniem potwierdzenia, broker dostarczy wiadomość ponownie innemu konsumentowi. Dzięki temu działa dostarczenie co najmniej raz — oznacza to jednak, że ack musi nastąpić po zatwierdzeniu skutku ubocznego, nigdy przed nim.
Zbyt wczesne wysłanie ack i awaria powodują utratę wiadomości. Zbyt późne wysłanie ack i awaria powodują otrzymanie duplikatu — ten przypadek obsłuży warstwa idempotencji.
<?php
// Pseudocode of the consumer contract
function consumeLoop($channel): void {
while ($msg = $channel->get()) {
try {
processSideEffect($msg); // commit DB write first
$channel->ack($msg); // only then ack
} catch (\Throwable $e) {
$channel->nack($msg, requeue: true); // let it redeliver
}
}
}
function processSideEffect($m): void {}Zachowanie kolejności ma swoją cenę
Kolejki nie gwarantują globalnej kolejności po zwiększeniu liczby konsumentów. Dwóch workerów pobierających wiadomości z tej samej kolejki przetwarza je współbieżnie, więc wiadomość B może zakończyć przetwarzanie przed wiadomością A.
Jeśli kolejność ma znaczenie (np. w przypadku zmian salda konta), należy partycjonować według klucza, aby wszystkie powiązane wiadomości trafiały kolejno do jednego konsumenta. Kafka obsługuje to natywnie za pomocą partycji; w RabbitMQ należy kierować wiadomości za pomocą spójnego haszowania do kolejek przypisanych do poszczególnych kluczy.
Kolejki dead-letter
Niektóre wiadomości nigdy nie mogą zostać pomyślnie przetworzone — na przykład z powodu niepoprawnych danych lub odwołań do usuniętych wierszy. Ponawianie prób w nieskończoność blokuje kolejkę (wiadomość trująca). Stosuje się wtedy wzorzec kolejki dead-letter (DLQ): po N nieudanych próbach wiadomość jest kierowana na bok do sprawdzenia, zamiast być dostarczana ponownie.
<?php
function consume(array $msg, $channel): void {
$attempts = ($msg['headers']['x-attempt'] ?? 0) + 1;
try {
process($msg);
$channel->ack($msg);
} catch (\Throwable $e) {
if ($attempts >= 5) {
$channel->deadLetter($msg); // park in DLQ
} else {
$channel->republish($msg, ['x-attempt' => $attempts]);
}
}
}
function process(array $m): void {}Kiedy NIE używać kolejki
Komunikacja asynchroniczna wiąże się z rzeczywistymi kosztami operacyjnymi: trzeba uruchomić brokera, wyjaśnić zespołowi produktowemu spójność ostateczną oraz utrzymywać trudniejsze debugowanie między granicami procesów.
Po kolejkę należy sięgać, gdy praca jest wolna, skokowa, możliwa do ponowienia lub wykonywana bez oczekiwania na wynik. Należy zachować komunikację synchroniczną, gdy wywołujący rzeczywiście potrzebuje wyniku teraz (np. wiążącej ceny, którą użytkownik musi zobaczyć) — umieszczenie w kolejce wywołania wymagającego wyniku tylko zwiększa opóźnienie i złożoność.
Kolejka a log
Za tymi wzorcami stoją dwa ogólne typy brokerów, z których oba są wykorzystywane w dalszej części kursu:
- Kolejka zadań (RabbitMQ) usuwa wiadomość po jej potwierdzeniu. Doskonale nadaje się do rozdzielania pracy między pulę współzawodniczących konsumentów, z routingiem poszczególnych wiadomości i ustawieniami TTL.
- Log zatwierdzeń (Kafka) przechowuje wiadomości zgodnie z polityką retencji; każdy konsument śledzi własny offset i może odtworzyć historię, a wiele niezależnych grup konsumentów może odczytywać ten sam strumień.
Kolejkę należy wybierać do rozdzielania pracy, a log — do wysokowydajnych strumieni zdarzeń i ich odtwarzania.
<?php
$useCase = 'replay events for a new analytics service';
$broker = str_contains($useCase, 'replay') || str_contains($useCase, 'stream')
? 'Kafka (commit log)'
: 'RabbitMQ (task queue)';
echo $broker . "\n";Szybkie sprawdzenie
Rozumowanie o gwarancjach dostarczenia.
Podsumowanie
Znają już Państwo model mentalny stojący za komunikacją asynchroniczną:
- Kolejki zapewniają odsprzężenie przestrzenne i czasowe oraz działają jak bufor wyrównujący obciążenie.
- Większość systemów działa w modelu co najmniej raz, dlatego konsumenci muszą być idempotentni.
- Wysyłaj ack dopiero po zatwierdzeniu skutku ubocznego; niech błędy powodują ponowne dostarczenie.
- Zachowanie kolejności wymaga partycjonowania według klucza, a wiadomości trujące wymagają DLQ.
- Nie należy umieszczać w kolejce pracy, której wynik wywołujący musi otrzymać synchronicznie.
W następnej części zostanie to przećwiczone w RabbitMQ w PHP.
Często zadawane pytania
Czy lekcja „Dlaczego warto używać komunikacji asynchronicznej” jest bezpłatna?
Tak — pełny tekst „Dlaczego warto używać komunikacji asynchronicznej” 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 „Dlaczego warto używać komunikacji asynchronicznej”?
Proszę zobaczyć, jak kolejki rozdzielają producentów od konsumentów. Ć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 1 z 4.
Ile czasu zajmuje lekcja „Dlaczego warto używać komunikacji asynchronicznej”?
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
- Dlaczego warto używać komunikacji asynchronicznej
- Praca z RabbitMQ w PHP
- Apache Kafka z PHP
- Tworzenie przepływów pracy sterowanych zdarzeniami