celery broker 遷移 (Redis 遷移至 RabbitMq)

背景

項目中一直使用 Redis 作爲 celery 的 broker,會出現偶發的任務重複執行的情況,再加上一直沒有隊列的監控工具,任務沒有持久化,存在一定的風險。故開啓了 broker 由 Redis 遷移至 RabbitMq 的路程。

版本

  • Flask==0.10.1
  • celery==3.1.18

如何無縫遷移 ?

由於不能停服遷移,最大的問題就變成了如何保證原有 Redis 中的任務都能正常執行完成,且新的任務在 rabbitmq 中正常運轉。

話說 celery 提供了 celery migrate 消息遷移工具,主要通過 task_id 或 task_name 匹配 queue_name 來遷移,但是由於之前項目未做持久化,這樣比較麻煩風險比較大。

解決方案

  1. 在服務器上臨時目錄下再拉一套原代碼
  2. 新啓一個celery服務,記爲 celery_old_service,並指定broker地址爲原 Redis 的地址,指定PYTHONPATH爲臨時目錄下的原代碼路徑。待這個遷移前celery服務正常運轉,就可以關閉原服務的celery服務了。
  3. 發版,上遷移後的代碼。此時需要觀察兩個事情:
    (1)新的任務已經進入 RabbitMq 中正常執行 ;
    (2) 若原來 Redis 中還有沒執行完的任務,可以發現 celery_old_service 還在正常執行剩餘的任務,待任務全部消耗掉,即可關閉 celery_old_service。

  • 任務大量堆積,佔用大量內存,拖垮同一集羣中的其他服務:

    解決方案:

    (1)調研階段要做好風險預估,瞭解集羣配置,多方面考慮,上線後,持續觀察 mq 生產消費速率,;

    (2)代碼優化。爲了降低業務風險,本次遷移不做業務的單獨優化,所以這種老業務中的坑,可能不會注意到,在線上爆出來才發現,這時候只能緊急回滾,通過代碼優化/重構解決任務堆積的問題之後再重新上線。
  • celery 每個任務的執行結果都會產生一個臨時隊列,雖然這個臨時隊列生存期只有24h,但是大量的無效隊列會污染整個集羣。

    解決方案:不產生結果,通過配置解決
CELERY_RESULT_PERSISTENT = False
CELERY_IGNORE_RESULT = True
  • celeryenv 沒有consumer

    每個服務器都會產生一個 celeryenv 的隊列,由於此類隊列都是通過routing_key和exchange路由的,一旦有的隊列失去了消費者,就會造成消息大量堆積

    解決方案:通過配置解決,一分鐘之後沒有消費者的celeryenv隊列自動刪除
CELERY_EVENT_QUEUE_EXPIRES = 60 
  • 由於之前使用 Redis,項目啓動用 supervisor 管理,同一個隊列會配置啓多個進程(numprocs大於1),會造成 celery 的 node 命名衝突, 告警信息如下:
UserWarning: A node named [email protected] is already using this process mailbox!

解決方案:啓用一個進程(numprocs=1 ),配置多個併發進程(–concurrency)

  • task 任務數據序列化

    由於之前使用 Redis,設置爲[‘pickle’, ‘json’, ‘msgpack’, ‘yaml’], 改爲 rabbitmq 之後,序列化改爲了 [‘json’],此時就要去梳理項目中每個異步任務了,檢查參數是否符合 json 格式,容易出錯的參數類型有 byte, datetime, 以及key是數字的dict 等。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章