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 等。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章