Exécutez PHP Tâche Asynchrone
Je travail sur un assez grand nombre d'applications web, et le backend est principalement en PHP. Il y a plusieurs endroits dans le code où j'ai besoin de remplir une certaine tâche, mais je ne veux pas faire de l'utilisateur d'attendre le résultat. Par exemple, lors de la création d'un nouveau compte, j'ai besoin de leur envoyer un e-mail de bienvenue. Mais quand ils ont frappé les "Terminer l'Inscription" du bouton, je ne veux pas les faire attendre jusqu'à ce que le mail est envoyé, je veux juste de commencer le processus, et de renvoyer un message à l'utilisateur immédiatement.
Jusqu'à maintenant, dans certains endroits, j'ai été en utilisant ce qui se sent comme un hack avec exec(). Fondamentalement, faire des choses comme:
exec("doTask.php $arg1 $arg2 $arg3 >/dev/null 2>&1 &");
Qui semble fonctionner, mais je me demandais si il ya une meilleure façon. J'envisage l'écriture d'un système de files d'attente de tâches dans une table MySQL, et une exécution de script PHP qui interroge la table une fois par seconde, et exécute toutes les nouvelles tâches qu'il trouve. Cela aurait également l'avantage de me permettre de diviser les tâches entre plusieurs travailleurs machines dans le futur si je devais le faire.
Suis-je ré-inventer la roue? Est-il une meilleure solution que la méthode exec() hack ou le MySQL file d'attente?
Vous devez vous connecter pour publier un commentaire.
J'ai utilisé les files d'attente approche, et il fonctionne bien comme vous pouvez différer le traitement jusqu'à ce que votre charge de serveur est inactif, vous permettant de gérer votre charge de façon très efficace si vous pouvez partition off "à des tâches qui ne sont pas urgentes" facilement.
Roulement de votre propre n'est pas trop difficile, voici quelques autres options pour vérifier:
Un autre, peut-être plus simple consiste à utiliser ignore_user_abort - une fois que vous avez envoyé la page de l'utilisateur, vous pouvez faire votre traitement final sans crainte de cessation prématurée, même si ce ne sont l'effet de l'apparition de prolonger le chargement de la page à partir du point de vue utilisateur.
fastcgi_finish_request()
serait plus approprié queignore_user_abort()
; il requiert php-fpm, cependant. Au moins la réponse est envoyée à l'utilisateur, la connexion est fermée, mais PHP exécution se poursuit.Quand vous voulez juste pour exécuter une ou plusieurs requêtes HTTP sans avoir à attendre la réponse, il y a une simple solution PHP, ainsi.
Dans le script d'appel:
Sur le script.php vous pouvez appeler ces fonctions PHP dans les premières lignes:
Cela provoque la poursuite du script en cours d'exécution sans limite de temps lors de la connexion HTTP est fermé.
Une autre façon de fourche processus est via curl. Vous pouvez configurer vos tâches internes comme un service web. Par exemple:
Puis dans votre utilisateur a accédé scripts d'effectuer des appels vers le service:
Votre service pouvez garder une trace de la file d'attente de tâches avec mysql ou ce que vous voulez, le point est: c'est tout enveloppé à l'intérieur du service et votre script est juste de consommer de l'Url. Cela vous permet de déplacer le service sur une autre machine/serveur si nécessaire (c'est à dire facilement évolutive).
Ajoutant http autorisation ou d'une coutume régime d'autorisation (comme Amazon web services) vous permet d'ouvrir vos tâches à être consommés par d'autres personnes, des services (si vous voulez) et vous pourriez aller plus loin et ajouter un service de surveillance sur le dessus pour garder une trace de la file d'attente et le statut de la tâche.
Il prend un peu de travail, mais il y a beaucoup d'avantages.
J'ai utilisé Beanstalkd pour un projet, et prévu de nouveau. Je l'ai trouvé pour être une excellente façon d'exécuter les processus asynchrones.
Un couple de choses que j'ai fait avec elle sont les suivants:
J'ai écrit un Zend-Framework de base de système pour décoder un "gentil" de l'url, par exemple, pour redimensionner une image, il ferait appel
QueueTask('/image/resize/filename/example.jpg')
. L'URL a été d'abord décodé dans un tableau(module de contrôleur de l'action,les paramètres), puis converti en JSON pour l'injection de la file d'attente.Une longue course script cli pris le travail à partir de la file d'attente, il a couru (via Zend_Router_Simple), et, si nécessaire, placer des informations dans memcached pour le site web en PHP pour ramasser comme nécessaire quand il a été fait.
Une ride je n'ai également mis en a été que l'interface de ligne de commande script a seulement fonctionné pendant 50 tours avant de redémarrer, mais si elle ne veux redémarrer comme prévu, il le ferait immédiatement (en cours d'exécution par l'intermédiaire d'un bash-script). Si il y avait un problème et je n'ai
exit(0)
(la valeur par défaut pourexit;
oudie();
), il faudrait d'abord mettre en pause pour un couple de secondes.Si c'était seulement une question de la fourniture d'une tâche coûteuse, dans le cas de php-fpm est pris en charge, pourquoi ne pas utiliser
fastcgi_finish_request()
fonction?Vous n'avez pas vraiment utiliser l'asynchronicité de cette façon:
fastcgi_finish_request()
.Une fois de plus, php-fpm est nécessaire.
Ici est une simple classe j'ai codé pour mon application web. Il permet de faire un fork de scripts PHP et d'autres scripts. Fonctionne sur UNIX et Windows.
C'est la même méthode que j'ai été de les utiliser pour un couple d'années maintenant et je n'ai pas vu ou trouvé quelque chose de mieux. Comme les gens l'ont dit, PHP est mono-thread, donc il n'y a pas grand chose d'autre que vous pouvez faire.
J'ai effectivement ajouté un niveau supplémentaire à l'obtenir et stocker l'id de processus. Cela me permet de rediriger vers une autre page et demandez à l'utilisateur de s'asseoir sur cette page, à l'aide d'AJAX pour vérifier si le processus est terminé (id de processus n'existe plus). C'est utile pour les cas où la longueur du script serait la cause de la navigateur de délai d'attente, mais l'utilisateur doit attendre que le script pour terminer avant la prochaine étape. (Dans mon cas, c'était le traitement de gros fichiers ZIP avec CSV comme les fichiers d'ajouter jusqu'à 30 000 enregistrements de la base de données après que l'utilisateur doit confirmer certaines informations.)
J'ai également utilisé un processus similaire pour la génération de rapports. Je ne suis pas sûr que je serais utilisation "traitement de fond" pour quelque chose comme un e-mail, sauf si il y a un réel problème avec la lenteur de la SMTP. Au lieu de cela je peut utiliser un tableau comme une file d'attente et ensuite avoir un processus qui s'exécute toutes les minutes pour envoyer les e-mails dans la file d'attente. Vous devez être warry de l'envoi d'e-mails deux fois ou autres problèmes similaires. Je le considère comme un analogue des files d'attente des processus pour d'autres tâches en tant que bien.
PHP A multithreading, c'est juste pas activé par défaut, il existe une extension appelée pthreads qui fait exactement cela.
Vous aurez besoin de php est compilé avec ZTS bien. (Thread-Safe)
Liens:
Exemples
Un autre tutoriel
pthreads Extension PECL
C'est une bonne idée d'utiliser cURL comme suggéré par rojoca.
Ici est un exemple. Vous pouvez surveiller text.txt alors que le script est en cours d'exécution en arrière-plan:
Malheureusement, PHP n'a pas de genre de natif de capacités. Donc, je pense que dans ce cas, vous n'avez pas le choix d'utiliser une sorte de code personnalisé pour faire ce que vous voulez faire.
Si vous effectuez une recherche sur le net pour PHP filetage choses, des gens sont venus avec des moyens pour simuler les threads sur PHP.
Si vous définissez la Longueur du Contenu d'en-tête HTTP dans votre "Merci De votre Inscription", le navigateur doit fermer la connexion après le nombre spécifié d'octets reçus. Cela laisse le de côté de serveur de processus en cours d'exécution (en supposant que ignore_user_abort est) de sorte qu'il peut terminer le travail sans faire l'utilisateur final d'attente.
Bien sûr, vous aurez besoin de calculer la taille de votre contenu de la réponse avant de rendre les en-têtes, mais c'est assez facile pour de courtes réponses (écrire la sortie d'une chaîne de caractères, appel strlen(), l'appel de l'en-tête(), le rendu de la chaîne).
Cette approche a l'avantage de pas vous forçant à gérer un "front end" file d'attente, et bien que vous devrez peut-être faire un peu de travail sur le back-end pour éviter de course HTTP enfant processus de marcher les uns sur les autres, c'est quelque chose que vous besoin de faire déjà, de toute façon.
header('Content-Length: 3'); echo '1234'; sleep(5);
alors même si le navigateur ne prend que 3 caractères, il attend encore pendant 5 secondes avant d'afficher la réponse. Ce qui me manque?phpinfo()
. La seule autre chose que j'ai pu imaginer, c'est que j'ai besoin pour atteindre un minimum de taille de la mémoire tampon d'abord, par exemple, 256 ou alors octets.ini_set("zlib.output_compression", 0); ini_set("implicit_flush", 1); header('Content-Type: text/plain; charset=utf-8' ); header('Content-Length: 3'); echo '1234'; ob_flush(); flush(); sleep(10);
Même l'ajout de plus d'écho des appels de sortie > 512 caractères avant la chasse d'eau ne l'aide pas.Si vous ne voulez pas la pleine soufflé ActiveMQ, je vous recommande de considérer RabbitMQ. RabbitMQ est léger de messagerie qui utilise le AMQP standard.
Je vous recommande de regarder aussi dans php-amqplib populaire AMQP de la bibliothèque du client pour l'accès AMQP de message sur les courtiers.
je pense que vous devriez essayer cette technique, il aidera à appeler autant de pages que vous le souhaitez toutes les pages s'exécuter à la fois de manière indépendante, sans attente pour chaque page de réponse asynchrone.
cornjobpage.php //la page d'accueil
testpage.php
PS:si vous voulez envoyer des paramètres d'url comme boucle puis suivez cette réponse :https://stackoverflow.com/a/41225209/6295712
Naître de nouveaux processus sur le serveur à l'aide de
exec()
ou directement sur un autre serveur à l'aide de curl n'a pas d'échelle de tout ce que le bien à tous, si nous aller pour exec vous êtes essentiellement de remplissage de votre serveur avec de longs processus en cours d'exécution qui peut être traitée par d'autres non web serveurs, et à l'aide de curl liens d'un autre serveur, à moins que vous construisez dans une sorte d'équilibrage de charge.J'ai utilisé Gearman dans quelques situations et j'ai trouver de mieux pour ce genre de cas d'utilisation. Je peux utiliser une seule tâche de la file d'attente du serveur pour gérer la mise en attente de tous les travaux devant être effectué par le serveur et faire tourner travailleur serveurs, chacun de qui peut fonctionner en de nombreux cas, des processus de travail en tant que de besoin, et d'accroître le nombre de travailleurs de serveurs que nécessaire et de spin vers le bas lorsqu'il n'est pas nécessaire. Aussi laissez-moi arrêter le processus de travail entièrement en cas de besoin et les files d'attente des tâches vers le haut jusqu'à ce que les travailleurs se remettre en ligne.
PHP est un single-threaded langue, donc il n'y a pas de manière officielle pour démarrer un processus asynchrone avec d'autres que l'aide
exec
oupopen
. Il y a un post de blog à ce sujet ici. Votre idée pour une file d'attente dans le serveur MySQL est une bonne idée aussi.Votre condition spécifique est ici pour envoyer un email à l'utilisateur. Je suis curieux de savoir pourquoi vous essayez de le faire de manière asynchrone depuis l'envoi d'un e-mail est assez trivial et rapide de la tâche à effectuer. Je suppose que si vous envoyez des tonnes de courrier électronique et votre FAI bloque vous sur la suspicion de spam, qui pourrait être une des raisons de la file d'attente, mais d'autres que je ne peux pas penser à une raison pour faire de cette façon.