2012-11-14 13 views
6

Lets sagen, ich habe eine Blog-Anwendung mit Posts. Nach dem Erstellen eines Posts wird ein Worker für einige Hintergrundoperationen erstellt. Mein Fall ist, dass ich nach dem Senden eines Postformulars eine Art von Lade-Nachricht (GIF Loader oder so) anzeigen möchte und wenn der Worker fertig ist, möchte ich die Lade-Nachricht ausblenden und einige Daten anzeigen, die vom Worker bereitgestellt werden. Meine Frage ist, was der beste Weg ist zu kommunizieren, dass ein Arbeiter seinen Job beendet hat und ihn im Frontend für Benutzer anzeigt. Worker Rückruf so etwas wie dies Ich denkeHintergrund Arbeiter sehen Kommunikation in Schienen App

def worker_finish 
    #message the user 
end 

Antwort

11

sieht kann man den Punkt fehlt den Hintergrund Arbeiter mit, im Grunde, was Sie versuchen zu tun, selbst zu besiegen. - Wenn der Benutzer das Formular abschickt und Sie den Job in Ihrem Controller in die Warteschlange stellen, um den Benutzer darauf zu warten, dass der Worker gestartet und beendet wird, haben Sie nicht nur genau das erreicht, was der Controller selbst hätte tun können, aber Sie haben machte den Prozess viel komplizierter (und diese Art von Funktionalität ist nicht in entweder resque oder sidekiq eingebaut).

Wenn Offloading einen Auftrag von einer Warteschlange verarbeitet werden Sie sofort eine Antwort an den Client zurückgeben möchten, dh

class PostsController < ApplicationController 
    def create 
    @post = Post.create(params[:post]) 
    BackgroundBlogOperation.enque(@post.id) 
    respond_with(@post) 
    end 
end 

Die BackgroundBlogOperation Klasse dann in eine Warteschlange gestellt werden würde, für einen Arbeiter zu durchlaufen und arbeite ab. Wenn Sie möchten, dass der BackgroundBlogOperation-Worker nach dessen Beendigung noch etwas anderes tut, können Sie dies tun, aber dies sollte innerhalb des Jobs selbst geschehen, damit der Worker dafür verantwortlich sein kann.

Wenn Sie nur versuchen, den Spinner nach dem Erstellen eines Beitrags anzuzeigen und auszublenden, ohne die Seite neu zu laden, zeigen Sie einfach den JavaScript-Spinner vor dem Klicken auf die Schaltfläche Senden an und stellen Sie sicher, dass der Anforderungstyp js ist (add: remote = > treu in Form). Dann erstellen Sie eine Javascript-Ansicht Antwort, die wie folgt aussieht:

class PostsController < ApplicationController 
    respond_to :js, :only => [:create] 
    def create 
    @post = Post.create(params[:post]) 
    BackgroundBlogOperation.enque(@post.id) 
    respond_with(@post) 
    end 
end 

Und die create.js.erb, könnte auch eine Nachricht ihnen zu sagen, dass alles, was anhängen Sie im Hintergrund tun Warteschlange ist, wenn sein komplexer als die Schaffung ein Post oder was auch immer.

$("#spinner").hide(); 

jetzt - Obwohl das, was Sie ursprünglich fragten jeden Zweck dienen doesent (weil zu den Spinner nach Beendigung des Auftrags ein- und ausblenden würde warten auf den Controller benötigen die Aktion abzuschließen) - Es gibt Situationen, in denen es ist nützlich, dem Client anzuzeigen, dass der Job fertig bearbeitet wurde.

Lassen Sie uns zunächst ein Szenario definieren, in dem die Hintergrundverarbeitung tatsächlich nützlich ist. Nehmen wir an, Sie haben eine Schaltfläche, die beim Klicken Daten von einer externen API abruft, und nachdem Sie Daten von der externen Site abgerufen haben, führen Sie eine Datenbankoperation basierend auf der Antwort aus. Dies wäre ein gutes Beispiel für den Hintergrundprozess. Im Grunde genommen in der Steuerung werden Sie so etwas wie:

class ApiController < ApplicationController 
    respond_to :js, :only => [:create] 

    def sync_tweets 
    TwitterApiJob.enque(current_user.twitter_username) 
    respond_with(message: 'Syncing Tweets') 
    end 

end 

Nun, um den Benutzer zu sagen, wann die Tweets fertig Syncing ein bisschen komplizierter ist, und Sie haben drei grundlegende Optionen:

1) Benutzer benachrichtigen per E-Mail (in der Regel schlechteste Option imo) 2) Verwenden Sie eine Art von HTML5 oder WebSocket-fähigen Rails-Server, Schienen laufen, und senden Sie einen Push durch die Websocket an den Client, die zwar sehr cool, ist in den meisten Fällen übertrieben und außerhalb des Umfangs von dieser Antwort. Google rails websocket drängen, wenn Sie Ihre Optionen sehen möchten. 3) IMO beste Option für die meisten Fälle, erstellen notifcations Modell alle Arten von Benutzerbenachrichtigungen zu handhaben, und in Ihrem Job, nachdem die Arbeit abgeschlossen ist, so etwas wie

Notification.create(:user_id => user.id, :message => 'Sync has finished', type => 'sync_complete') 

Dann werden die Benutzeranforderungen nächste Seite , in einer Header-Symbolleiste oder wo auch immer, ich hätte eine Notiz, dass der Benutzer ungelesene Benachrichtigungen hat, um den Benutzer darauf hinzuweisen, darauf zu klicken.

--- Auch ich denke, in Ihrem Beitrag, den Sie erwähnen Sie resque verwenden. Ich habe versucht, resque und es war anständig, aber ich fand es zu kompliziert in der Produktion zu debuggen, und es verwendete verrückten Speicher - ich würde Ihnen raten sidekiq zu überprüfen, wenn Sie nicht bereits verwenden, verwendet redis auch, aber viel schneller als es verwendet Themen:

https://github.com/mperham/sidekiq

Es ist viel schneller, sauberer und einfache Einrichtung zu bekommen, imho.

+0

Während der vorgestellte Anwendungsfall möglicherweise nicht ideal ist, könnte die Funktionalität sehr nützlich sein, um beispielsweise einen Bericht auszuführen. – aaandre