Viens un moment dans le développement d’une application (qu’elle soit Web ou pas) ou la nécessité d’avoir des tâches qui s’exécutent de façon transparente, mais surtout qui ne ralentissent pas l’application ! Le cauchemard pour un utilisateur que d’attendre devant une page blanche le temps que tout les traitements côté serveur soit terminés.

Afin de pallier à ce problème, les tâches de fond sont la solution… Dans le monde (merveilleux) de Ruby on Rails, il existe plusieurs solutions
Pour UpShot nous avons choisi DelayedJob, pas l’original de Tobi, mais la version de ColectiveIdea.
Tout d’abord : pourquoi DelayedJob plutôt qu’un autre ?
Et bien après avoir lu les différents comparatifs, et constaté que Git lui même y était passé pendant un très long moment. Même si Git a récemment changé pour Resque, lui-même basé sur DelayedJob, en lisant l’excellent readme de Resque sur la comparaison entre Resque et DelayedJob, vous comprendrez pourquoi nous avons choisi DelayedJob.
Voyons maintenant le pourquoi du comment :
Les utilisateurs d’UpShot travaillent souvent avec des images haute résolution. Lorsqu’ils placent une note sur une image, ils provoquent le chargement de l’image depuis un service distant, que nous récupérons pour ensuite la re-tailler (crop) et afficher une miniature de la zone commentée. Ce traitement tel qu’il se fait actuellement prend 7 secondes. 6, 8 secondes de trop par rapport à ce qui est acceptable donc ;)
Intervention de delayed_job
Nous créons donc une tache de fond pour la récupération de l’image, le ‘cropping’, et l’enregistrement de la miniature. La ressource Git du projet explique parfaitement l’utilisation, après avoir installé le plugin (ou le gem, as you want), il suffit de créer sa propre tâche dans le répertoire lib de votre application Rails.
- Installation de delayed_job
- Lancement de la commande de migration (vous devriez voir une table delayed_job apparaitre dans votre BDD).
- Nous créons donc un fichier my_cropping_job.rb dans le repertoire /lib de notre projet.
- Puis nous y plaçons le code suivant :
-
require ‘RMagick’
-
-
class MyCroppingJob <</span> Struct.new(:my_image_object)
-
include Magick
-
-
def perform
-
-
@img = MyImageObject.find(my_image_object.id)
-
-
# Open the pic
-
img_temp = Magick::Image::read(@img.asset.file.url).first
-
-
# Get the zone
-
cropped = img_temp.crop(@img.x, @img.y, @img.w, @img.h)
-
-
#… other treaments
-
#.. but not interesting for you
-
-
@img.save!
-
-
end
-
-
end
- Pour finir il suffira de placer l’appel à ce “job” lorsque cela est nécéssaire dans votre code :
-
Delayed::Job.enqueue(NoteThumbnailJob.new(@note.reload))
Et surtout : N’oubliez pas de lancer la commande rake jobs:works !
Le post-traitement
Dans notre cas précis, la note devant s’afficher immédiatement, le temps que la vraie image de la zone apparaisse, nous utilisons donc une image temporaire, que nous remplacerons simplement via JavaScript.
Déploiement
Lancer le traitement des tâches en fond avec la ligne de commande c’est faisable mais un peu fastidieux à chaque déploiement de l’application. Copiez/collez simplement les lignes suivantes à la fin de votre fichier de déploiement (/config/deploy.rb) :
-
namespace :delayed_job do
-
desc “Start delayed_job process”
-
task :start, :roles => :app do
-
run “cd #{current_path};#{rails_env} ./script/delayed_job start”
-
end
-
-
desc “Stop delayed_job process”
-
task :stop, :roles => :app do
-
run “cd #{current_path};#{rails_env} ./script/delayed_job stop”
-
end
-
-
desc “Restart delayed_job process”
-
task :restart, :roles => :app do
-
run “cd #{current_path};#{rails_env} ./script/delayed_job restart”
-
end
-
end
Le projet DelayedJob :
http://github.com/collectiveidea/delayed_job
Un très bon railsCast sur DelayedJob :
http://railscasts.com/episodes/171-delayed-job
Pour aller plus loin :
rails, les taches de fond distribuées, S3, EC2, …
http://www.slideshare.net/jondahl/asynchronous-processing-with-ruby-on-rails-railsconf-2008-443159
Chargement ...
