2009-10-22 7 views
8

Sur mon application Ruby on Rails, j'ai besoin d'exécuter 50 tâches d'arrière-plan en parallèle. Chaque travail crée une connexion TCP à un serveur différent, récupère certaines données et met à jour un objet d'enregistrement actif.Exécution de plusieurs tâches parallèles en arrière-plan avec Rails

Je connais différentes solutions pour effectuer cette tâche mais aucune d'entre elles en parallèle. Par exemple, delayed_job (DJ) pourrait être une excellente solution si seulement il pouvait exécuter toutes les tâches en parallèle.

Des idées? Merci.

+0

Une autre solution ici: http://stackoverflow.com/questions/16551466/parallelizing-methods-in-rails/16676981#16676981 – Subhas

+0

Intéressant, merci @RDX – fjyaniez

Répondre

1

Quelques réflexions ...

  • Tout simplement parce que vous avez besoin de lire 50 sites et veulent naturellement un travail parallèle ne pas moyenne que vous avez besoin de 50 processus ou threads. Vous devez équilibrer le ralentissement et les frais généraux. Que diriez-vous d'avoir 10 ou 20 processus chacun lire quelques sites?

  • En fonction de Ruby que vous utilisez, faites attention sur les fils verts, vous ne pouvez pas obtenir le résultat parallèle que vous voulez

  • Vous pouvez structurer comme un revers, inetd côté client, et Utilisez connect_nonblock et IO.select pour obtenir les connexions parallèles souhaitées en faisant en sorte que tous les serveurs répondent en parallèle. Vous n'avez pas vraiment besoin d'un traitement parallèle des résultats, vous avez juste besoin de vous mettre en ligne sur tous les serveurs en parallèle, car c'est là que se situe réellement la latence.

Donc, quelque chose comme ça de la bibliothèque socket ... prolonger pour plusieurs connexions en cours ...

require 'socket' 
include Socket::Constants 
socket = Socket.new(AF_INET, SOCK_STREAM, 0) 
sockaddr = Socket.sockaddr_in(80, 'www.google.com') 
begin 
    socket.connect_nonblock(sockaddr) 
    rescue Errno::EINPROGRESS 
    IO.select(nil, [socket]) 
    begin 
    socket.connect_nonblock(sockaddr) 
    rescue Errno::EISCONN 
    end 
end 
socket.write("GET/HTTP/1.0\r\n\r\n") 
# here perhaps insert IO.select. You may not need multiple threads OR multiple 
# processes with this technique, but if you do insert them here 
results = socket.read 
+0

IO.select pourrait être utile dans ce cas, je vais essayer. Je vous remercie. – fjyaniez

6

Il est en fait possible d'exécuter plusieurs tâches delayed_job.

De http://github.com/collectiveidea/delayed_job:

# Runs two workers in separate processes. 
$ RAILS_ENV=production script/delayed_job -n 2 start 
$ RAILS_ENV=production script/delayed_job stop 

Donc, en théorie, vous pouvez simplement exécuter:

$ RAILS_ENV=production script/delayed_job -n 50 start 

Cela frayer 50 processus, mais je ne suis pas sûr que ce serait recommandé selon le les ressources du système que vous utilisez. Une option alternative serait d'utiliser threads. Créez simplement un nouveau fil pour chacun de vos travaux. Une chose à garder à l'esprit est qu'avec cette méthode, ActiveRecord n'est pas adaptée aux threads. Vous pouvez le faire thread-safe en utilisant le paramètre suivant:

ActiveRecord::Base.allow_concurrency = true 
+0

Vous pouvez également exécuter delayed_job travailleurs sur plusieurs machines. Je doute que vous ayez beaucoup d'avantages à faire tourner plus de travailleurs sur une seule machine que de coeurs de CPU, mais vous pouvez répartir la charge en l'exécutant sur plusieurs boîtes. Si vous devez exécuter 50 simultanément, je pense que vous devrez distribuer le travail. –

+2

Je vais bénéficier de plusieurs travailleurs sur une seule machine parce que la plupart des travailleurs seront bloqués par IO – fjyaniez

0

Puisque vous travaillez avec des rails, je vous conseille d'utiliser delayed_job pour ce faire plutôt que de se diviser en fils ou en fourches. Raison d'être - traitant des délais d'attente et des choses lorsque le navigateur est en attente peut être une vraie douleur. Il y a deux approches que vous pouvez prendre avec DJ

Le premier est - spawn 50+ travailleurs. En fonction de votre environnement, cela peut être une solution assez lourde, mais cela fonctionne très bien.Ensuite, lorsque vous devez exécuter votre travail, assurez-vous de créer 50 tâches uniques. S'il y a trop de bouffées de mémoire et que vous voulez faire les choses de cette façon, faites un environnement séparé qui est dépouillé, spécifiquement pour vos travailleurs.

La deuxième méthode consiste à créer un seul travail qui utilise Curl :: Multi pour exécuter vos 50 requêtes TCP simultanées. Vous pouvez en savoir plus à ce sujet ici: http://curl-multi.rubyforge.org/ De cette façon, vous pourriez avoir un processeur d'arrière-plan exécutant toutes vos demandes TCP en parallèle.