今天带着学习的目的花了半天时间看了一下 python 广泛使用的任务队列 celery ,看完感觉不少地方挺奇怪。我们部门任务因为削峰一直用的是自己写的任务队列,所以说实话其实一直也没用过 rabbitmq 这些。
网上相关信息很少不提了,stackoverflow 能找到几个问题基本都是好多年前的。
这些都是小问题,我感觉其他几个需要吐槽的地方
1 、2202 年了,asyncio 居然不是被原生支持的。
追踪最新任务 issue ,https://github.com/celery/celery/issues/6552,显然异步支持不在当前官方列表里,甚至不在官方发布计划中,可以说是遥遥无期。社区当然是有一些曲折迂回的方法的,比如我看到 so 上有人说用async_to_sync
做奇怪的封装
from asgiref.sync import sync_to_async
def task_to_async(task):
async def wrapper(*args, **kwargs):
delay = 0.1
async_result = await sync_to_async(task.delay)(*args, **kwargs)
while not async_result.ready():
await asyncio.sleep(delay)
delay = min(delay * 1.5, 2)
return async_result.get()
return wrapper
只能缓缓打出一个问号?且不提这 while not ready: sleep & try ,看了一下依赖的第三方库里实现是基于一个最大线程数为 1 的线程池维护事件循环,这又带来更多衍生问题,比如我如何在 task 间共享连接状态,比如 mysql 连接池?如果我要在异步任务里使用多线程又该如何管理?总之是问题多多。
再者不提消费者不能异步消费,生产者也不能异步生产,添加任务到任务队列的过程是同步的,也许这带来的延迟非常短暂,但是也许设计者认为该延迟应该忽略?看了看 celery 基于的通信模块是基于 socket 自己搞了个 selector ,自行维护的事件循环没有享受到任何现有生态的好处,纯 py 编写无法享受社区 libuv 版的好处,性能和可靠性都让人质疑。。总之这一个设计让用户代码整个又被拖回同步宇宙,也是问题多多。
2 、生产者不支持任务完成回调
对于一些常见的短任务的需求(处理时间小于 1s ),短的任务就不需要加入任务队列了吗?我觉得显然不是的,但是同样的,段任务的处理规范是否应该是返回一个任务状态,再由前端轮询执行结果?我感觉也未必,毕竟任务很短。所以我们部门网关调用时可以选择用异步通知,await 等待执行结果。而 celery 目前看起来网关想获取任务成功状态的话只能轮询,或者是在 worker 那边定义任务完成后再向队列加入一个子任务,你可以在子任务里用自定义的方式给网关一个回调。。。
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.