2011-01-30 2 views
1

J'ai un script php que j'utilise pour faire environ 1 mil. demande chaque jour à un service Web spécifique. Le problème est que dans un flux de travail "normal", le script travaille presque toute la journée pour terminer le travail. J'ai donc travaillé sur un composant supplémentaire. Fondamentalement, j'ai développé un script qui accède au script principal en utilisant la requête GET multi-curl pour générer un tempid aléatoire pour chaque 500 enregistrements et fait finalement une autre requête multi-curl en utilisant POST avec tous les tempids générés. Cependant, je ne pense pas que ce soit la bonne façon, donc je voudrais des conseils/solutions pour ajouter des capacités de multithreading au script principal sans utiliser d'applications supplémentaires/externes (par exemple le script curl que j'utilise actuellement). Voici le script principal: http://pastebin.com/rUQ6pwGSphp multithreading, mysql

Répondre

1

Si vous voulez le faire correctement, vous devez installer une file d'attente de messages. Ma préférence va à redis car c'est un "serveur de structure de données puisque les clés peuvent contenir des chaînes, des hachages, des listes, des ensembles et des ensembles triés". Redis est également extrêmement fast.

Utilisation du blpop (fraie deux threads de travail à l'aide php <yourscript> pour traiter simultanément) le travail pour écouter les nouveaux messages (travail) et rpush pour pousser les nouveaux messages sur la file d'attente. Les processus de ponte sont chers (relatifs) et lors de l'utilisation d'une file d'attente de messages, cela ne doit être fait qu'une seule fois lors de la création du processus.

Je voudrais aller pour phpredis si vous pouviez (besoin d'être recompiler PHP) parce que c'est une extension écrite en C et donc aller beaucoup plus vite que les clients PHP purs. Sinon, PRedis est aussi une bibliothèque assez mature que vous pourriez utiliser.

Vous pouvez également utiliser ce brpop/rpush comme une sorte de verrou (si vous en avez besoin). C'est parce que:

Plusieurs clients peuvent bloquer pour la même clé . Ils sont mis dans une file d'attente, donc le premier à être servi sera le celui qui a commencé à attendre plus tôt, dans un premier mode BLPOP .

Je vous conseille de jeter un oeil à Simon's redis tutorial pour avoir une idée de la puissance pure que redis a à offrir.

1

Ceci est le processus d'arrière-plan, correct? Dans ce cas, vous ne devriez pas l'exécuter via un serveur Web. Exécutez-le à partir de la ligne de commande, soit en tant que démon ou en tant que travail cron.

Ma préférence est un travail "cron" parce que vous obtenez le redémarrage automatique gratuitement. Assurez-vous que vous n'avez pas plus d'instances du programme en cours d'exécution que vous le souhaitez (vous pouvez y parvenir en verrouillant un fichier dans le système de fichiers, en faisant quelque chose d'atomique dans une base de données, etc.).

Ensuite, vous avez juste besoin de démarrer le nombre de processus que vous voulez, et de leur faire lire le travail à partir d'une file d'attente.

Normalement le modèle pour ce faire est d'avoir une table contenant des colonnes pour stocker qui Executing actuellement une tâche donnée:

CREATE TABLE sometasks (
    ID of some kind, 
    Other info required to do task, 
    some data we need to know if the task is due yet or complete, 
    locked_by_host VARCHAR(64) NULL, 
    locked_by_pid INT NULL 
) 

Ensuite, le processus fera l'pseduo-requête suivante pour verrouiller un ensemble de tâches (batch_size est combien par lot, peut être 1)

UPDATE sometasks SET locked_by_host=my_hostname, locked_by_pid=my_pid 
    WHERE not_done_already AND locked_by_host IS NULL ORDER BY ID LIMIT batch_size 

sélectionnez ensuite les lignes arrière à l'aide d'une sélection pour trouver les tâches du processus en cours. Traitez ensuite les tâches et mettez-les à jour comme étant «terminées» et effacez le verrou.

J'opterais pour un travail cron avec un processus de contrôleur qui lance N processus enfants et les surveille. Les processus enfants peuvent mourir périodiquement (rappelez-vous que PHP n'a pas de bons GC, donc il peut facilement fuir la mémoire) et être réapparus pour éviter les fuites de ressources.

Si le travail est terminé, le parent peut quitter et attendre d'être réapparu par cron (l'heure suivante ou quelque chose comme ça). NB: locked_by_host peut stocker le nom d'hôte (les pids ne sont pas uniques sur différents hôtes) pour permettre un traitement distribué, mais peut-être que vous n'en avez pas besoin, donc vous pouvez l'omettre.

Vous pouvez faire cette conception plus robuste en mettant une colonne de locked_time et détecter lorsqu'une tâche est trop long - vous pouvez alerter, tuer le processus, et essayez à nouveau ou quelque chose.