不得不吐槽一下 Python 的任务队列,异步支持太差了

2022-04-27 09:47:49 +08:00
 LeeReamond

今天带着学习的目的花了半天时间看了一下 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 那边定义任务完成后再向队列加入一个子任务,你可以在子任务里用自定义的方式给网关一个回调。。。

8645 次点击
所在节点    Python
76 条回复
shuimugan
2022-04-27 13:31:20 +08:00
是的,全链路异步生态太差,需要各种妥协,我受不了就切到 nodejs 了,你这个需求可以看看 bullmq
chaoshui
2022-04-27 13:32:14 +08:00
@Richard14 我怎么看 fastapi 文档里面使用 uvicorn 呢
arischow
2022-04-27 13:34:39 +08:00
又是回复改变认知的一帖
westoy
2022-04-27 13:42:55 +08:00
本质上其实还是 python 的 asyncio 生态问题

比如 celery 搭配最多的 django, asyncio 支持也是混搭, 尤其你要搭配 channels , 要硬编码一堆 a****,sync_to_async 各种混用, 割裂得一批

所以你猜 celery 的命令行里为啥给了--eventlet 和--gevent 就是没给--asyncio, 就是压根没建议你用 asyncio 啊.........
est
2022-04-27 13:45:41 +08:00
@LeeReamond 对 gunicorn 还有个 handler 可以写。uvicorn 直接无视你定义的 handler 。。。他丫的日志就是一个写死的格式。连时间都木有。

https://github.com/encode/uvicorn/blob/master/uvicorn/_logging.py#L97
est
2022-04-27 13:46:07 +08:00
@westoy 早年间还直接 async 关键字冲突,报错一大堆。。。。
so1n
2022-04-27 13:46:08 +08:00
@LeeReamond 还好吧我改造成本就 20 行代码左右
LeeReamond
2022-04-27 13:57:42 +08:00
@so1n 老哥可以一发,我们学习一下
lolizeppelin
2022-04-27 14:04:44 +08:00
celery 应该用的 kumbo 把? mq 库不支持 asyncio 你指望上代码支持?

python 的海量库是优势也是累赘...

所以说,老老实实学 openstack,就用 eventlet,折腾啥 asyncio/gevent 的。
另外,任务对了 rpc 之类的抄 openstack 它不香么?折腾个啥子 celery 、flask 、gunicorn
v2exe2v
2022-04-27 14:27:49 +08:00
flask 有啥问题啊?
NCZkevin
2022-04-27 15:00:31 +08:00
python 异步生态极其别扭,有些框架为了适配异步,用各种魔法来搞。异步还是转 go 吧,go 写异步是真的省事
junmoxiao
2022-04-27 15:03:09 +08:00
@est 关键现实中很多项目只用玩具做就够了
est
2022-04-27 15:20:23 +08:00
@junmoxiao 没毛病。但是我比较反感把玩具吹上天。

希望实事求是,有更多的人贡献把玩具调教得更好用,更好玩。
elboble
2022-04-27 15:20:23 +08:00
拔凉拔凉,刚学会 flask+celery+vue ,用 gunicorn 部署到 nginx 上,给 pdf 加页码的小工具,原来都是玩具,玩具。。。。
abersheeran
2022-04-27 15:26:58 +08:00
@est 不是吧,我觉得 fastapi 比 flask 骗子营销多了……至少 flask 没吹过自己压根不存在的“高性能”。
julyclyde
2022-04-27 15:38:52 +08:00
@est 我前几天把 python3.6 上好好的软件挪到 3.8 去运行也遇到 async 关键字的问题了哈哈哈哈
bairdshi
2022-04-27 15:56:11 +08:00
回复直接改变我认知了,
用了五六年 python ,gunicorn celery flask 都是玩具...
反正 web 后台性能差 那就都归类玩具...
est
2022-04-27 16:03:23 +08:00
@abersheeran flask 满世界吹自己 lightweight 啊。。其实一个 werkzeug 设计就比 django 复杂了。。(不对这句话负责。逃。。。。。。。。。。。。。
est
2022-04-27 16:04:31 +08:00
@bairdshi 别乱说。是很多地方的设计。。。我没有一竿子全打死啊。。(逃。。。。。
LeeReamond
2022-04-27 16:14:36 +08:00
@chaoshui 我印象里生产部署是 gunicorn 套 uvicorn.workers.UvicornWorker

@bairdshi 没什么改变认知的,玩不玩具另说,起码那位老哥吐槽的几个问题确实存在,作为生产级引擎日志格式不能改确实令人惊讶,同时没有子进程守护也同样是痛点

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/849494

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX