Pracuję nad dość dużą aplikacją internetową, a backend jest głównie w PHP. W kodzie jest kilka miejsc, w których muszę wykonać jakieś zadanie, ale nie chcę, aby użytkownik czekał na wynik. Na przykład podczas tworzenia nowego konta muszę wysłać im powitalną wiadomość e-mail. Ale kiedy klikną przycisk „Zakończ rejestrację”, nie chcę zmuszać ich do czekania, aż wiadomość e-mail zostanie faktycznie wysłana, po prostu chcę rozpocząć proces i od razu zwrócić wiadomość do użytkownika.
Do tej pory w niektórych miejscach używałem czegoś, co wygląda jak hack z exec (). Zasadniczo robienie rzeczy takich jak:
exec("doTask.php $arg1 $arg2 $arg3 >/dev/null 2>&1 &");
Co wydaje się działać, ale zastanawiam się, czy istnieje lepszy sposób. Rozważam napisanie systemu, który kolejkowałby zadania w tabeli MySQL, oraz osobnego, długo działającego skryptu PHP, który raz na sekundę odpytuje tę tabelę i wykonuje nowe zadania, które znajdzie. Miałoby to również tę zaletę, że pozwoliłoby mi w przyszłości podzielić zadania między kilka maszyn roboczych, jeśli zajdzie taka potrzeba.
Czy wymyślam koło na nowo? Czy istnieje lepsze rozwiązanie niż hack exec () lub kolejka MySQL?
źródło
Jeśli chcesz tylko wykonać jedno lub kilka żądań HTTP bez czekania na odpowiedź, istnieje również proste rozwiązanie PHP.
W skrypcie wywołującym:
W nazwie script.php możesz wywołać te funkcje PHP w pierwszych wierszach:
Powoduje to kontynuowanie działania skryptu bez ograniczeń czasowych po zamknięciu połączenia HTTP.
źródło
Innym sposobem na rozwidlenie procesów jest zawijanie. Możesz skonfigurować swoje wewnętrzne zadania jako usługę sieciową. Na przykład:
Następnie w skryptach, do których użytkownik uzyskuje dostęp, wywołuje usługę:
Twoja usługa może śledzić kolejkę zadań za pomocą mysql lub cokolwiek chcesz: wszystko jest opakowane w usługę, a Twój skrypt po prostu zużywa adresy URL. Dzięki temu możesz w razie potrzeby przenieść usługę na inny komputer / serwer (tj. Łatwo skalowalną).
Dodanie autoryzacji http lub niestandardowego schematu autoryzacji (takiego jak usługi internetowe Amazon) umożliwia otwarcie zadań do wykorzystania przez inne osoby / usługi (jeśli chcesz) i możesz pójść dalej i dodać usługę monitorowania na wierzchu, aby śledzić stan kolejki i zadania.
To wymaga trochę pracy związanej z konfiguracją, ale jest wiele korzyści.
źródło
Użyłem Beanstalkd w jednym projekcie i planowałem ponownie. Odkryłem, że to doskonały sposób na uruchamianie procesów asynchronicznych.
Kilka rzeczy, które z nim zrobiłem, to:
Napisałem system oparty na Zend-Framework, aby zdekodować „ładny” adres URL, na przykład, aby zmienić rozmiar obrazu, który by wywołał
QueueTask('/image/resize/filename/example.jpg')
. Adres URL został najpierw zdekodowany do tablicy (moduł, kontroler, akcja, parametry), a następnie przekonwertowany na format JSON w celu wstrzyknięcia do samej kolejki.Długo działający skrypt CLI odebrał zadanie z kolejki, uruchomił je (przez Zend_Router_Simple) i, jeśli było to wymagane, umieścił informacje w memcached, aby PHP mogło je pobrać zgodnie z wymaganiami po zakończeniu.
Jedną z pomyłek, które również zrobiłem, było to, że skrypt cli działał tylko przez 50 pętli przed ponownym uruchomieniem, ale jeśli chciałby uruchomić ponownie zgodnie z planem, zrobiłby to natychmiast (uruchamiany przez skrypt bash). Jeśli wystąpił problem, a ja to zrobiłem
exit(0)
(domyślna wartośćexit;
lubdie();
), najpierw zatrzymywał się na kilka sekund.źródło
Jeśli chodzi tylko o zapewnienie drogich zadań, w przypadku gdy obsługiwane jest php-fpm, dlaczego nie skorzystać z
fastcgi_finish_request()
funkcji?Tak naprawdę nie używasz asynchroniczności w ten sposób:
fastcgi_finish_request()
.Po raz kolejny potrzebne jest php-fpm.
źródło
Oto prosta klasa, którą zakodowałem dla mojej aplikacji internetowej. Pozwala na rozwidlanie skryptów PHP i innych skryptów. Działa na systemach UNIX i Windows.
źródło
To ta sama metoda, której używam od kilku lat i nie widziałem ani nie znalazłem nic lepszego. Jak ludzie powiedzieli, PHP jest jednowątkowy, więc niewiele więcej możesz zrobić.
Właściwie dodałem do tego jeden dodatkowy poziom, czyli pobieranie i przechowywanie identyfikatora procesu. To pozwala mi przekierować na inną stronę i pozwolić użytkownikowi usiąść na tej stronie, używając AJAX do sprawdzenia, czy proces się zakończył (identyfikator procesu już nie istnieje). Jest to przydatne w przypadkach, gdy długość skryptu spowodowałaby przekroczenie limitu czasu przeglądarki, ale użytkownik musi poczekać na zakończenie skryptu przed następnym krokiem. (W moim przypadku było to przetwarzanie dużych plików ZIP z plikami podobnymi do CSV, które dodają do bazy danych do 30 000 rekordów, po czym użytkownik musi potwierdzić pewne informacje.)
Użyłem również podobnego procesu do generowania raportów. Nie jestem pewien, czy użyłbym „przetwarzania w tle” do czegoś takiego jak e-mail, chyba że istnieje prawdziwy problem z powolnym SMTP. Zamiast tego mógłbym użyć tabeli jako kolejki, a następnie uruchomić proces, który uruchamia się co minutę, aby wysłać e-maile w kolejce. Musisz być ostrożny w wysyłaniu e-maili dwa razy lub innych podobnych problemach. Rozważałbym podobny proces kolejkowania również dla innych zadań.
źródło
PHP MA wielowątkowość, po prostu nie jest domyślnie włączona, istnieje rozszerzenie o nazwie pthreads, które robi dokładnie to. Będziesz jednak potrzebował php skompilowanego z ZTS. (Bezpieczne wątki) Linki:
Przykłady
Kolejny samouczek
Rozszerzenie pthreads PECL
źródło
To świetny pomysł, aby użyć cURL zgodnie z sugestią rojoca.
Oto przykład. Możesz monitorować text.txt, gdy skrypt działa w tle:
źródło
Niestety PHP nie ma żadnych natywnych możliwości obsługi wątków. Więc myślę, że w tym przypadku nie masz innego wyjścia, jak tylko użyć jakiegoś niestandardowego kodu, aby zrobić to, co chcesz.
Jeśli szukasz w sieci rzeczy związanych z wątkami PHP, niektórzy wymyślili sposoby na symulowanie wątków w PHP.
źródło
Jeśli ustawisz nagłówek HTTP Content-Length w odpowiedzi „Dziękujemy za rejestrację”, przeglądarka powinna zamknąć połączenie po odebraniu określonej liczby bajtów. Powoduje to, że proces po stronie serwera działa (zakładając, że ustawiono ignore_user_abort), dzięki czemu może zakończyć pracę bez czekania użytkownika końcowego.
Oczywiście będziesz musiał obliczyć rozmiar zawartości odpowiedzi przed renderowaniem nagłówków, ale jest to całkiem łatwe w przypadku krótkich odpowiedzi (zapisywanie danych wyjściowych do ciągu znaków, wywołanie strlen (), wywołanie nagłówka (), renderowanie ciągu).
To podejście ma tę zaletę, że nie zmusza cię do zarządzania kolejką „front-end” i chociaż może być konieczne wykonanie pewnych prac na zapleczu, aby zapobiec nadeptywaniu na siebie wyścigowych procesów potomnych HTTP, to już trzeba było zrobić , tak czy siak.
źródło
header('Content-Length: 3'); echo '1234'; sleep(5);
tego, mimo że przeglądarka przyjmuje tylko 3 znaki, nadal czeka 5 sekund przed wyświetleniem odpowiedzi. czego mi brakuje?phpinfo()
. Jedyną inną rzeczą, jaką mogę sobie wyobrazić, jest to, że najpierw muszę osiągnąć minimalny rozmiar bufora, np. 256 lub więcej bajtów.Jeśli nie chcesz pełnego ActiveMQ, polecam rozważyć RabbitMQ . RabbitMQ to uproszczona obsługa wiadomości korzystająca ze standardu AMQP .
Polecam również zajrzeć do php-amqplib - popularnej biblioteki klienta AMQP, aby uzyskać dostęp do brokerów wiadomości opartych na AMQP.
źródło
Myślę, że powinieneś wypróbować tę technikę, pomoże ona wywołać dowolną liczbę stron, wszystkie strony będą działać jednocześnie niezależnie, bez czekania na każdą odpowiedź strony jako asynchroniczną.
cornjobpage.php // mainpage
testpage.php
PS: jeśli chcesz wysłać parametry adresu URL jako pętlę, postępuj zgodnie z tą odpowiedzią: https://stackoverflow.com/a/41225209/6295712
źródło
Odradzanie nowych procesów na serwerze przy użyciu
exec()
lub bezpośrednio na innym serwerze przy użyciu curl wcale nie skaluje się zbyt dobrze, jeśli zdecydujemy się na exec, w zasadzie wypełniasz swój serwer długotrwałymi procesami, które mogą być obsługiwane przez inne serwery niezwiązane z siecią, a użycie curl wiąże inny serwer, chyba że zastosujesz jakiś rodzaj równoważenia obciążenia.Użyłem Gearmana w kilku sytuacjach i uważam, że jest to lepsze dla tego rodzaju przypadków użycia. Mogę użyć pojedynczego serwera kolejki zadań, aby w zasadzie obsłużyć kolejkowanie wszystkich zadań, które muszą być wykonane przez serwer, i uruchomić serwery robocze, z których każdy może uruchamiać dowolną liczbę wystąpień procesu roboczego, i skalować w górę liczbę serwery robocze w razie potrzeby i wyłączaj je, gdy nie są potrzebne. Pozwala mi również całkowicie zamknąć procesy robocze w razie potrzeby i kolejkować zadania, aż pracownicy wrócą do trybu online.
źródło
PHP jest językiem jednowątkowym, więc nie ma innego oficjalnego sposobu na rozpoczęcie procesu asynchronicznego niż użycie
exec
lubpopen
. Jest blogu o tym tutaj . Twój pomysł na kolejkę w MySQL jest również dobrym pomysłem.Twoim konkretnym wymaganiem jest wysłanie wiadomości e-mail do użytkownika. Ciekawi mnie, dlaczego próbujesz to zrobić asynchronicznie, ponieważ wysłanie wiadomości e-mail jest dość banalnym i szybkim zadaniem do wykonania. Przypuszczam, że jeśli wysyłasz mnóstwo e-maili, a Twój dostawca usług internetowych blokuje Cię podejrzenie spamowania, może to być jeden z powodów do kolejki, ale poza tym nie mam żadnego powodu, aby robić to w ten sposób.
źródło