155 votes

Exécutez PHP Tâche Asynchrone

Je travaille 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.

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?

85voto

Paul Dixon Points 122033

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.

Rouler votre propre n'est pas trop difficile, voici quelques autres options pour vérifier:

  • ActiveMQ si vous voulez une véritable open source file d'attente de messages.
  • ZeroMQ - c'est une super bibliothèque de socket qui permet d'écrire facilement distribué code sans avoir à trop se soucier de la prise de la programmation elle-même. Vous pouvez l'utiliser pour message queuing sur un seul hôte - vous serait tout simplement avoir votre webapp pousser quelque chose à une file d'attente d'un fonctionnement continu de la console d'application de la consommer à la prochaine occasion
  • beanstalkd - seulement trouvé celui-ci lors de l'écriture de cette réponse, mais semble intéressant
  • dropr est un PHP de message sur la file d'attente de projet, mais n'a pas été activement maintenu depuis Sep 2010
  • Enfin, un blog sur l'utilisation de memcached pour message queuing

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.

23voto

Markus Points 71

Lorsque vous voulez exécuter une ou plusieurs requêtes HTTP sans avoir à attendre la réponse, il y a une simple solution PHP.

Dans le script d'appel:

$socketcon = fsockopen($host, 80, $errno, $errstr, 10);
if($socketcon) {   
   $socketdata = "GET $remote_house/script.php?parameters=... HTTP 1.1\r\nHost: $host\r\nConnection: Close\r\n\r\n";      
   fwrite($socketcon, $socketdata); 
   fclose($socketcon);
}
// repeat this with different parameters as often as you like

Sur le script.php vous pouvez appeler ces fonctions PHP dans les premières lignes:

ignore_user_abort(true);
set_time_limit(0);

Cela provoque la poursuite du script en cours d'exécution sans limite de temps lors de la connexion HTTP est fermé.

18voto

rojoca Points 5454

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:

$service->addTask('t1', $data); // post data to URL via curl

Votre service peut 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).

Ajouter 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.

7voto

Alister Bulman Points 12913

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:

  • Redimensionnement d'Image - et avec un peu chargé file d'attente de passer à une interface de ligne de commande basé sur un script PHP, le redimensionnement de large (2 mo+) des images a très bien fonctionné, mais essayez de redimensionner les mêmes images dans un mod_php instance a été régulièrement en cours d'exécution dans l'espace mémoire de questions (j'ai limité le processus PHP à 32 MO, et le redimensionnement a pris plus que cela)
  • dans un avenir proche, les chèques - beanstalkd a des retards sont disponibles (faire ce travail à exécuter, qu'après X secondes) - donc, je peux incendie au large de 5 ou 10 chèques pour un événement, un peu plus tard dans le temps

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' exit(0) (la valeur par défaut de exit; ou die();), il faudrait d'abord mettre en pause pour un couple de secondes.

4voto

Darryl Hein Points 33819

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.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X