2017-02-23 60 views
0

我有一個我在GAE上開發的服務。應用程序需要每3秒「勾選一次以執行一堆計算。這是一款模擬型遊戲。在Google App Engine的任務隊列中避免重複的任務(或處理它們)

我有一個使用像這樣的deferred API和任務隊列(一些錯誤處理等,爲了清楚而移除)手動縮放實例,我開始:

@app.route('/_ah/start') 
def start(): 
    log.info('Ticker instance started') 
    return tick() 

@app.route('/tick') 
def tick(): 
    _do_tick() 
    deferred.defer(tick, _countdown=3) 
return 'Tick!', 200 

的問題是,有時我結束了由於某種原因(這可能是暫時的錯誤/超時導致任務被重新安排),並且我在任務隊列中結束了多個任務,並且該遊戲每3秒週期多次打勾。

任何想法如何最好地處理這個?

據我所見,你不能問一個隊列'有X的任務已經存在嗎?'或「此刻隊列中有多少物品?」。

筆者瞭解到,這款採用的推隊列,一個想法可能要改用拉入隊列,並有從隊列中,通過標籤分組,這將讓所有的人,包括重複自動收報機租賃項目。這會更好嗎?

實際上,我真正想要的只是一個類似cron的調度程序,每3秒調度一次,但我知道GAE上的調度程序可能不會運行到該分辨率。

我可不可以乾脆一切都變成了啓動處理程序,如:

@app.route('/_ah/start') 
def start(): 
    log.info('Ticker instance started') 
while True: 
     _do_tick() 
     sleep(3) 

return 200 

但是從我所看到的,日誌將不會更新爲我做到這一點,因爲它被認爲是一個單一的請求從未完成。這使得在日誌中看到發生了什麼變得更難。目前我將每個單獨的滴答作爲一個單獨的請求日誌條目。

此外,如果上述死亡,我需要得到它重新安排自己無論如何。這可能不會太麻煩,因爲我知道當實例即將關閉時可以捕獲異常,然後我可以啓動延遲任務以重新啓動該實例。

或者還有更好的方法來處理GAE?

+0

每3秒鐘做一件事似乎非常具有挑戰性。您是否可以在需要時根據需求進行所有計算?例如,當您收到用戶的請求時,根據當前時間進行所有需要的計算並將結果呈現給用戶? –

+0

唉不,它是一個連續運行的模擬。這正在更新數據庫中的一堆數據,然後同時被400多個遊戲客戶訪問。 –

回答

0

https://stackoverflow.com/a/36621588/4495081中所述,您可以設置爲_retry_options可選參數中的task_retry_limit值。

麻煩的是,如果存在失敗的有效原因,那麼滴答作業將永遠停止。您可能還想跟蹤上次執行作業的時間,並且有一個基於cron的完整性檢查作業來定期檢查滴答作業是否仍在運行,如果不是,請重新啓動。

+0

我確實考慮過存儲上一次工作的時間,以某種方式試圖跳過在3s過期之前進入的工作,但是記賬工作可能會過於昂貴。我已經試圖保持數據庫的電話。 –

+0

如果/當您發現需要該選項時 - 考慮使用memcache(免費)而不是數據存儲區,只是在memcache數據消失時做好恢復準備 –

+0

我已經在使用memcache。 'tick'進程執行一堆數據庫查詢和計算,並將結果緩存在memcache中。然後訪問該服務的客戶端從memcache(hit)獲取數據或從db(未命中)重新計算 –

1

我看不到一種方法來檢測/消除重複,但現在使用不同的機制解決它。而不是依賴於任務隊列的調度,我跑在手動縮放實例我自己的調度循環:

TICKINTERVAL = 3 

@app.route('/_ah/start') 
def scheduler(): 
    log.info('Ticker instance started') 
    while True: 
     if game.is_running(): 
      task = taskqueue.add(
       url='/v1/game/tick', 
       queue_name='tickqueue', 
       method='PUT', 
       target='tickworker', 
       ) 
     else: 
      log.info('Tick skipped as game stopped') 
     db_session.rollback() 
     sleep(TICKINTERVAL) 

我在queue.yaml

queue: 
- name: tickqueue 
    rate: 5/s 
    max_concurrent_requests: 1 
    retry_parameters: 
    task_retry_limit: 0 
    task_age_limit: 1m 

定義我自己的隊列,tickqueue隊列沒有按不會重試任務,並且任何超過一分鐘的任務都會被取消。我將最大併發數設置爲1,這樣只會嘗試一次處理一個項目。

如果偶爾出現「滴答」時間超過3秒,則它將備份到隊列中,但如果隊列再次加速,則應清除該隊列。如果蜱最終平均花費時間超過3秒,那麼排隊超過一分鐘的任務將被丟棄。

這給我的好處是我得到每個記號的日誌條目(它被稱爲/v1/game/tick很容易發現,而不是/_ah/deferred)。缺點是我需要爲調度程序使用一個實例,爲工作者使用一個實例,因爲您不能讓調度程序實例處理請求,因爲它不會執行,直到/_ah/start完成,而在此處它永遠不會執行。

相關問題