,我能想到的最簡單的方法是,你在你的數據庫設置一個標誌,當任務完成後,和您的前端(圖)週期性地發送一個Ajax請求,以檢查在數據庫船旗國。如果標誌已設置,則在視圖中採取適當的操作。以下是代碼示例:
由於您建議此長時間運行的任務需要爲每個用戶運行,因此讓我們添加一個布爾值爲users
表 - task_complete
。當您添加作業sidekiq,你可以取消設置標誌:
# Sidekiq worker: app/workers/task.rb
class Task
include Sidekiq::Worker
def perform(user_id)
user = User.find(user_id)
# Long running task code here, which executes per user
user.task_complete = true
user.save!
end
end
# When adding the task to sidekiq queue
user = User.find(params[:id])
# flag would have been set to true by previous execution
# In case it is false, it means sidekiq already has a job entry. We don't need to add it again
if user.task_complete?
Task.perform_async(user.id)
user.task_complete = false
user.save!
end
在視圖中,您可以定期檢查標誌是否被設置使用Ajax請求:
<script type="text/javascript">
var complete = false;
(function worker() {
$.ajax({
url: 'task/status/<%= @user.id %>',
success: function(data) {
// update the view based on ajax request response in case you need to
},
complete: function() {
// Schedule the next request when the current one's complete, and in case the global variable 'complete' is set to true, we don't need to fire this ajax request again - task is complete.
if(!complete) {
setTimeout(worker, 5000); //in miliseconds
}
}
});
})();
</script>
# status action which returns the status of task
# GET /task/status/:id
def status
@user = User.find(params[:id])
end
# status.js.erb - add view logic based on what you want to achieve, given whether the task is complete or not
<% if @user.task_complete? %>
$('#success').show();
complete = true;
<% else %>
$('#processing').show();
<% end %>
您可以根據超時在你的任務的平均執行時間是什麼。假設你的任務平均需要10分鐘,所以他們沒有必要以5秒的頻率檢查它。
此外,在情況下,你的任務執行頻率是複雜的東西(而不是每天1次),你可能需要添加時間戳task_completed_at
,並立足於標誌和時間戳的組合你的邏輯。
至於這部分: 「這個長時間運行的任務將在每個用戶上運行,在一個流量很大的網站上,這是個好主意嗎?」
雖然像在獨立硬件上執行作業(sidekiq workers)這樣的體系結構更改將有所幫助,但我沒有看到此方法的問題。這些都是輕量級的ajax調用,並且一些內置到您的javascript中的智能(如全局完成標誌)可以避免不必要的請求。如果你有很大的流量,並且DB讀/寫是一個問題,那麼你可能想將該標誌直接存儲到redis
而不是(因爲你已經有了它的sidekiq)。我相信這將解決您的讀/寫問題,並且我不認爲它會導致問題。這是我能想到的最簡單最清晰的方法,儘管您可以嘗試通過大多數現代瀏覽器支持的websockets實現相同的功能(儘管可能會導致舊版本出現問題)。
使用HTML 5 socket-io會非常棘手,所以我建議你使用Javascript(例如jQuery)並用定時器('interval'函數)輪詢 – 2014-09-03 21:41:44
我認爲這是一個更具擴展性的解決方案,它也是Heroku兼容的方式是將後臺作業寫入用戶的數據庫行或快速備用(如redis實例),並讓客戶端在定時器上輪詢狀態。 – Gene 2014-09-06 17:47:50
這個問題是關於如何實現它的後端部分(啓動作業,報告狀態回到應用程序)還是關於前端部分(如何通知瀏覽器狀態更改)? – spickermann 2014-09-07 01:37:43