2011-05-04 85 views
16

這裏是我的設置:如何判斷任務是否已在django-celery中排隊?

  • 的Django 1.3
  • 芹菜2.2.6
  • Django的芹菜2.2.4
  • djkombu 0.9.2

在我的settings.py文件我有

BROKER_BACKEND = "djkombu.transport.DatabaseTransport" 

即我只是usi使數據庫排隊任務。

現在到我的問題:我有一個用戶啓動的任務,可能需要幾分鐘才能完成。我希望任務只能爲每個用戶運行一次,並且我會將任務的結果緩存到一個臨時文件中,這樣如果用戶再次啓動任務,我只需返回緩存的文件。我在我的視圖函數中看起來像這樣的代碼:

task_id = "long-task-%d" % user_id 
result = tasks.some_long_task.AsyncResult(task_id) 

if result.state == celery.states.PENDING: 
    # The next line makes a duplicate task if the user rapidly refreshes the page 
    tasks.some_long_task.apply_async(task_id=task_id) 
    return HttpResponse("Task started...") 
elif result.state == celery.states.STARTED: 
    return HttpResponse("Task is still running, please wait...") 
elif result.state == celery.states.SUCCESS: 
    if cached_file_still_exists(): 
     return get_cached_file() 
    else: 
     result.forget() 
     tasks.some_long_task.apply_async(task_id=task_id) 
     return HttpResponse("Task started...") 

此代碼幾乎可用。但是當用戶快速重新加載頁面時,我遇到了一個問題。任務排隊和任務最終從隊列中取出併發給工作人員之間的延遲爲1-3秒。在此期間,任務的狀態保持爲PENDING,這會導致視圖邏輯啓動重複的任務。

我需要的是某種方式來判斷任務是否已經提交給隊列,所以我最終沒有提交兩次。芹菜中有這樣做的標準方法嗎?

+0

可以'kick_off_the_long_task_again()'檢查以確保任務移出待定?如果是這樣,這可能足以延遲用戶和芹菜之間的競爭條件。 – 2011-05-04 19:19:54

+0

kick_off_the_long_task_again()不會導致重複的任務。我更新了我的示例以顯示代碼將執行重複任務的位置。 – cwick 2011-05-04 19:34:00

+0

這不是我的問題。可以'kick_off_the_long_task_again()'檢查並等待確定任務在完成之前移出待定嗎? – 2011-05-04 19:42:09

回答

1

您可以通過手動將結果存儲在數據庫中來作弊。讓我解釋這將如何幫助。

例如,如果使用RDBMS(表列 - TASK_ID,狀態,結果):

查看部分:

  1. 使用事務管理。
  2. 使用SELECT FOR UPDATE獲取行,其中task_id ==「long-task-%d」%user_id。 SELECT FOR UPDATE將阻止其他請求,直到這一個COMMIT或ROLLBACK。
  3. 如果它不存在 - 將狀態設置爲PENDING並啓動'some_long_task',則結束請求。
  4. 如果狀態爲PENDING - 通知用戶。
  5. 如果狀態爲SUCCESS - 將狀態設置爲PENDING,則啓動該任務,返回'result'列指向的文件。我基於這個假設,你想重新運行獲得結果的任務。 COMMIT
  6. 如果狀態爲ERROR - 將狀態設置爲PENDING,請啓動任務,通知用戶。 COMMIT

任務部分:

  1. 準備文件,在嘗試,catch塊包裹。
  2. 成功 - 更新狀態爲= SUCCESS的正確行,結果。
  3. 失敗時 - 使用state = ERROR更新正確的行。
4

我用Redis解決了這個問題。只需在每個任務的redis中設置一個密鑰,然後在任務的after_return方法中從redis中刪除密鑰。 Redis重量輕,速度快。

4

我不認爲(正如Tomek和其他人所建議的那樣)使用數據庫的方式是執行此鎖定。 Django具有內置的緩存框架,它應該足以完成這種鎖定,並且必須更快。請參閱:

http://docs.celeryproject.org/en/latest/tutorials/task-cookbook.html#cookbook-task-serial

Django的可以配置爲使用memcached作爲緩存後端,這樣就可以在多臺機器......這似乎是更好的我被分配。思考?

+0

一個美麗的解決方案,正是我在找的東西。感謝您的鏈接! – 2014-09-18 15:32:05