Przejdź do treści
DedicatedPHP Kontakt

Priorytety w kolejkach PHP bez blokowania pilnych zadań

Projektuj priorytety w kolejkach PHP, aby izolować pilne zadania, kontrolować przepustowość, unikać zagłodzenia i reagować na szczyty obciążenia.

Redakcyjny diagram kolejek PHP rozdzielonych według priorytetu z workerami dla zadań krytycznych, interaktywnych i masowych

Kolejka asynchroniczna zapobiega oczekiwaniu żądania webowego na zakończenie kosztownych zadań, ale sama w sobie nie rozwiązuje rywalizacji między zadaniami. Problem pojawia się, gdy import, ponowne przetwarzanie lub kampania tworzy tysiące komunikatów i zajmuje wszystkich konsumentów. Akcja o natychmiastowym wpływie — potwierdzenie zamówienia, rezerwacja zapasów, zablokowanie konta lub wysłanie powiadomienia transakcyjnego — trafia za pracę, która może poczekać.

Zarządzanie priorytetami w kolejkach PHP nie polega wyłącznie na dodaniu pola numerycznego do komunikatu. To decyzja architektoniczna, która musi odzwierciedlać przepływ biznesowy, chronić ograniczone zależności i utrzymywać przewidywalne zachowanie wraz ze wzrostem obciążenia.

Klasyfikuj pracę według wpływu, terminu i kosztu

Klasyfikuj pracę według wpływu, terminu i kosztu — guía visual de DedicatedPHP

Przed utworzeniem kolejek przygotuj inwentaryzację zadań asynchronicznych. Dla każdego z nich określ, kto je inicjuje, z jakiej zależności korzysta, ile zwykle trwa, jaki ma termin biznesowy i co się dzieje, jeśli zostanie opóźnione. Pilność nie musi oznaczać ważności: uzgodnienie finansowe może być bardzo ważne, lecz tolerować kilka godzin oczekiwania; walidacja płatności może wymagać szybkiej odpowiedzi, nawet jeśli jej wykonanie jest krótkie.

Przydatna klasyfikacja zwykle obejmuje cztery klasy usług:

  • Krytyczna: działania chroniące pieniądze, bezpieczeństwo, spójność lub natychmiastowe zobowiązania. Powinny mieć bardzo niski docelowy czas oczekiwania i zarezerwowaną przepustowość.
  • Interaktywna: praca inicjowana przez osobę lub niezbędna do ukończenia doświadczenia zbliżonego do czasu rzeczywistego, na przykład wygenerowanie dokumentu zamówionego w aplikacji.
  • Odroczona: zadania konieczne, ale bez natychmiastowego terminu, takie jak okresowe synchronizacje, podsumowania lub aktualizacje indeksów.
  • Masowa: importy, migracje, reindeksacje, kampanie i ponowne przetwarzanie. Ich wolumen lub koszt wymaga ograniczania tempa, nawet gdy nie ma innego obciążenia.

Zapisz także koszt każdego zadania. Komunikat, który wywołuje API z ograniczonym limitem, wykonuje intensywne zapytanie lub przetwarza duży plik, nie powinien konkurować w taki sam sposób jak krótka lokalna aktualizacja. Klasa usługi powinna wyrażać termin oraz rodzaj presji, jaką zadanie wywiera na system.

Rozdziel kolejki, gdy potrzebujesz rzeczywistej izolacji

Pojedyncza kolejka z priorytetami może wystarczyć, jeśli zadania mają jednorodne wykonanie, korzystają z tych samych zależności, a transport zapewnia niezawodny priorytet. Jednak sama kolejność pobierania nie gwarantuje dostępności przepustowości: masowe zadanie, które już się wykonuje, nadal będzie zajmować worker, połączenie lub zewnętrzny limit.

Rozdziel kolejki, gdy występuje któreś z poniższych ograniczeń:

  • Zadania krytyczne i masowe mają wyraźnie różne docelowe czasy oczekiwania.
  • Typ zadania uzyskuje dostęp do wrażliwej zależności lub zależności z limitami liczby żądań, takiej jak API płatności, poczty lub ERP.
  • Czas trwania jest bardzo nierówny, a długie zadania zbyt długo zajmują procesy.
  • Wymagane jest niezależne sterowanie wdrażaniem, wstrzymywaniem, ponawianiem lub skalowaniem.
  • Błąd lub nietypowe dane wejściowe jednego przepływu nie powinny pogarszać działania innego przepływu.

W aplikacji PHP najbardziej czytelnym wzorcem jest zwykle kierowanie komunikatów do jawnych kolejek, na przykład critical, interactive, deferred i bulk. Komponentem komunikacyjnym może być Symfony Messenger, Laravel Queues lub własna integracja z wybranym brokerem; zasada nie zależy od frameworka. Priorytet w obrębie kolejki może uzupełniać ten podział, aby porządkować podobne zadania, a nie zastępować izolację między niekompatybilnymi klasami.

Określ zarezerwowaną przepustowość i maksymalną współbieżność

Przydziel konsumentów według klas i ustal zarówno minima, jak i maksima operacyjne. Kolejka krytyczna potrzebuje przepustowości, której nie mogą pochłonąć importy. Z kolei kolejka masowa powinna mieć maksymalną współbieżność, aby nie przeciążyć bazy danych, CPU, magazynu danych ani zewnętrznych dostawców.

Unikaj konfigurowania wszystkich workerów do odczytu ze wszystkich kolejek z bezwzględnym pierwszeństwem dla krytycznej. Takie podejście może pozostawić niewykorzystaną przepustowość, jeśli zarezerwowani konsumenci nie mogą pobierać innych zadań, albo spowodować zagłodzenie, jeśli mogą to robić bez reguł. Praktyczną alternatywą jest połączenie:

  • Workerów dedykowanych klasie krytycznej i interaktywnej.
  • Współdzielonych workerów obsługujących klasę odroczoną i masową według kwot.
  • Limitów według typu zależności, a nie tylko według łącznej liczby procesów.
  • Skalowania opartego na głębokości kolejki i wieku komunikatów, a nie wyłącznie na użyciu CPU.

Właściwa liczba nie jest uniwersalna. Powinna wynikać ze współbieżności tolerowanej przez bazę danych i API, obserwowanego czasu trwania oraz docelowego czasu oczekiwania każdej klasy.

Unikaj zagłodzenia i stosuj backpressure

Nadanie pierwszeństwa pilnym zadaniom nie oznacza, że zadania odroczone nigdy nie powinny się zakończyć. Jeśli komunikaty krytyczne są zawsze obecne, ścisła polityka priorytetów może powodować zagłodzenie: zadania niższego poziomu starzeją się w nieskończoność. Ustal mierzalną regułę fairness, na przykład przetwarzanie kwoty komunikatów odroczonych po ograniczonej liczbie krytycznych albo rezerwowanie niewielkiej części przepustowości dla pracy niepilnej.

Reguła musi uwzględniać ograniczenia zależności. Jeśli zadania krytyczne i masowe zapisują do tej samej tabeli z kosztownymi blokadami, równoległe wykonywanie obu może pogorszyć opóźnienia. W takim przypadku kwota powinna być stosowana do współdzielonego zasobu albo warto przeprojektować pracę na mniejsze partie.

Backpressure pojawia się, gdy napływa więcej pracy, niż można ukończyć. Nie rozwiązuje się go przez nieograniczone zwiększanie liczby workerów. Określ sposób reakcji:

  • Ogranicz rozmiar, częstotliwość lub współbieżność importów u źródła.
  • Podziel partie na wznawialne jednostki i kontroluj, ile z nich jest publikowanych jednocześnie.
  • Odkładaj pracę odroczoną za pomocą jawnego harmonogramu, gdy kolejka lub zależność przekroczy próg.
  • Przestrzegaj odpowiedzi o limicie liczby żądań, stosując przerwy i odroczone ponowienia zamiast natychmiastowych ponowień.
  • Informuj zespół produktowy, kiedy operacja została zaakceptowana do przetwarzania, a kiedy jest rzeczywiście ukończona.

Ważne jest rozróżnienie akceptacji od wykonania: zwrócenie informacji, że import został odebrany, nie oznacza, że może rozpocząć się natychmiast. Ta przejrzystość zapobiega interpretowaniu zmiany technicznej jako obietnicy natychmiastowej dostępności.

Kontroluj ponowienia, powolne zadania i idempotencję

Ponowienia zużywają przepustowość i mogą przypadkowo stać się obciążeniem priorytetowym. Klasyfikuj błędy jako przejściowe i trwałe. Tymczasowa awaria sieci może uzasadniać ponowienie z rosnącym czasem oczekiwania i rozproszeniem w czasie; błąd walidacji, nieistniejący zasób lub unieważnione poświadczenie powinny trafić do procesu weryfikacji, a nie być powtarzane bez końca.

Ustal maksymalny czas wykonania według typu zadania. Powolny job nie powinien bezterminowo zajmować workera. Jeśli można go podzielić, przetwarzaj strony, pliki lub segmenty w niezależnych komunikatach, które zapisują postęp. Jeśli nie, stosuj ścisłe limity, bezpieczne anulowanie i procedurę weryfikacji wyczerpanych zadań.

Priorytet zwiększa ryzyko powtórzenia efektów, gdy producent ponownie wysyła komunikat lub konsument ulega awarii po wywołaniu zewnętrznego API. Projektuj idempotentne handlery: używaj stabilnego klucza operacji, zapisuj stan przejścia i zapewnij, aby dwukrotne przetworzenie miało taki sam efekt biznesowy jak jednokrotne przetworzenie. Deduplikacja brokera może ograniczyć duplikaty, ale nie zastępuje idempotencji w aplikacji ani w integracjach zewnętrznych.

Obserwuj czas oczekiwania, nie tylko rozmiar kolejki

Krótka kolejka może ukrywać problem, jeśli jej najstarsze komunikaty czekają zbyt długo lub konsumenci stale zawodzą. Mierz dla każdej klasy usług wiek najstarszego komunikatu, czas od publikacji do rozpoczęcia, czas wykonania, odsetek błędów, ponowień i zadań wysłanych do weryfikacji.

Uzupełnij te metryki o aktywną współbieżność, głębokość, tempo napływu i odpływu, użycie połączeń, czasy zależności oraz otrzymane limity liczby żądań. Najbardziej użyteczne sygnały to: oczekiwanie krytyczne przekracza swój cel, kolejka masowa rośnie, gdy jej kwota jest ograniczona, ponowienia dominują ruch lub zarezerwowana przepustowość pozostaje niewykorzystana podczas szczytów innej klasy.

Konfiguruj alerty dotyczące trendów i celów usługowych, a nie tylko stałej liczby komunikatów. Tysiąc komunikatów może być normalne podczas importu; dziesięć może być poważnym problemem, jeśli dotyczy potwierdzeń zamówień, które czekają już kilka minut.

Przykład współdzielonego przepływu i lista wdrożeniowa

Przykład współdzielonego przepływu i lista wdrożeniowa — guía visual de DedicatedPHP

Wyobraź sobie platformę przetwarzającą pilne zamówienia, powiadomienia i masowy import katalogu. Zamówienia są kierowane do critical; powiadomienia transakcyjne do interactive; a import jest dzielony na strony wysyłane do bulk. Workery zamówień mają zarezerwowaną przepustowość. Import ma ograniczoną współbieżność i zmniejsza tempo, gdy rośnie opóźnienie bazy danych. Powiadomienia przestrzegają limitu dostawcy, stosując odroczone ponowienia. Jeśli proces się powtórzy, klucz operacji zapobiega utworzeniu dwóch rezerwacji lub wysłaniu dwóch zmian stanu.

Aby wprowadzić ten model do istniejącej aplikacji:

  1. Sporządź inwentaryzację handlerów i przypisz klasę usługi na podstawie terminu, wpływu i zależności.
  2. Zmierz czas trwania, oczekiwanie i błędy przed zmianą routingu.
  3. Najpierw rozdziel przepływy krytyczne od masowych i zarezerwuj minimalną przepustowość.
  4. Określ limity współbieżności według zależności oraz polityki backpressure.
  5. Zapewnij idempotencję efektów biznesowych oraz ogranicz ponowienia i czasy wykonania.
  6. Przetestuj szczyty obciążenia, awarię dostawców i masowy napływ danych przed aktywacją nowego podziału.
  7. Regularnie weryfikuj kwoty i klasy: priorytet jest polityką biznesową, która zmienia się wraz z produktem.

Pożądanym rezultatem nie jest to, aby wszystko było priorytetowe, lecz aby każde zadanie otrzymywało spójną przepustowość i termin, bez przekształcania operacji o dużym wolumenie w blokadę dla reszty biznesu.

Chcesz zastosować te pomysły w swoim projekcie?Omówmy Twoją platformę PHP.
Zobacz powiązaną usługę