redis架构图_Celery+Redis 断开连接 问题解决

最近接手一个Python web项目,项目中使用 Celery 异步执行一些耗时任务,服务每天都有部分接口阶段性的出现 500 响应。

查看日志发现是 Celery 在执行异步任务时与 Redis 断开连接。

主要错误日志如下:

File ""/usr/local/lib/python3. 6/site-packages/redis/connection, py"", line 613, in send_packed_command 
(errno, errmsg))
redis. exceptions. ConnectionError: Error 104 while writing to socket. Connection reset by peer.

由于刚接手项目,本人对 Celery 也不熟,处于会跑示例代码的水平,所以甩锅给 Redis ,联系运维人员查看 Redis 运行日志,发现如下问题:

Client id=1001 addr=127.0.0.1:49148 fd=17 name= age=3 idle=0 flags=N db=1 sub=559686 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=2998 omem=49146289 events=rw cmd=subscribe scheduled to be closed ASAP for overcoming of output buffer limits.

得出的结论为:

生产者生产任务的速度大于消费者消费任务的速度,导致消息队列中有大量消息堆积,Redis 缓冲区溢出,与 Celery 断开连接。

看来是冤枉运维小哥了,还是找一下自身项目的问题吧。

有关 Redis 缓冲区溢出的讲解可以参考这篇文章,调大缓冲区大小治标不治本

https://www.escapelife.site/posts/ca306858.html

Celery 架构图

59dd895a9b523c964f2a813512ab9b18.png

问题排查思路:

手动验证了下 task 的执行时间为毫秒级,而且起了 80 个 worker ,所以正常情况下不应该存在消息堆积问题。

问题就出现在不正常的情况下,Celery 在 Redis 中的默认队列名为 celery,登录 Redis 查看该队列中有6w多条消息,观察一段时间后发现消息数量不变,但是查看 worker 日志确实有任务在处理,那是什么原因导致消息未被消费呢?

查看 Celery 的配置文件中启用了路由任务,同时还有定时任务。

# 示例代码,非业务代码
# 路由任务
CELERY_ROUTES = {'feed.tasks.import_feed': {'queue': 'feeds'}}
# 定时任务
app.conf.beat_schedule = {
    'add-every-30-seconds': {
        'task': 'tasks.add',
        'schedule': 30.0,
        'args': (16, 16)
    },
}

路由任务的作用是:

在使用 delay() 方法或 apply_async() 方法调用 import_feed 任务时会被放入 feeds 队列中

定时任务:

每 30s 执行一次 add 任务

上面提到是默认队列 celery 中有消息堆积,但是配置文件中为每个 task 都指定了其他队列,那这个默认队列中堆积的是啥?

带着疑问进去到默认队列中查看堆积的是什么消息

{'body': 'W1sxLCAxXSwge30sIHsiY2FsbGJhY2tzIjogbnVsbCwgImVycmJhY2tzIjogbnVsbCwgImNoYWluIjogbnVsbCwgImNob3JkIjogbnVsbH1d',
 'content-encoding': 'utf-8',
 'content-type': 'application/json',
 'headers': {'lang': 'py',
  'task': 'tasks.add',
  'id': '50909224-cccd-49eb-8c97-54df439d121c',
  'shadow': None,
  'eta': None,
  'expires': None,
  'group': None,
  'retries': 0,
  'timelimit': [None, None],
  'root_id': '50909224-cccd-49eb-8c97-54df439d121c',
  'parent_id': None,
  'argsrepr': '(1, 1)',
  'kwargsrepr': '{}',
  'origin': 'gen34760@U18-4'},
 'properties': {'correlation_id': '50909224-cccd-49eb-8c97-54df439d121c',
  'reply_to': '60292f07-395d-3706-a51a-e9ec966a282b',
  'delivery_mode': 2,
  'delivery_info': {'exchange': '', 'routing_key': 'celery'},
  'priority': 0,
  'body_encoding': 'base64',
  'delivery_tag': 'c1dce9d0-fcbc-413f-a060-41afb9f05cf7'}}

其中 headers 的 task 记录的为定时任务中的 task 名称。所以结论是定时任务进入到默认队列中并且未被消费。

现在搞清楚是什么任务没有被消费,接下来要查明未被消费的原因。是默认队列优先级不够?还是worker 处理速度不行?

查看 yaml 文件中启动 worker 的命令

celery -A tasks worker --loglevel=info -Q feeds

上面的路由任务中 import_feed 的指定队列为 feeds 所以在启动 worker 时要专门指定 feeds 队列。结果是本次启动 worker 只用来消费 feeds 队列中的消息。并没有 worker 去消费默认队列中的消息,而定时任务就是被放在了默认队列中。

根本问题:

定时任务定时在默认消息队列中新增消息,没有 worker 去消费默认队列中的消息,导致队列中的消息越来越多。

解决方法:

为默认队列启动 worker 来消费消息

# 启动worker同时处理 feeds、celery中的消息
celery -A tasks worker --loglevel=info -Q feeds,celery
# 或者新启动worker
celery -A tasks worker --loglevel=info -Q celery

问题解决。后续会继续 Celery 的学习,有需要的同学欢迎关注~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值