不得不吐槽一下 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 那边定义任务完成后再向队列加入一个子任务,你可以在子任务里用自定义的方式给网关一个回调。。。

8639 次点击
所在节点    Python
76 条回复
cz5424
2022-04-27 09:57:11 +08:00
生产者不支持任务完成回调,这个应该有比较多的解决方法:生产者想要回调只能直接等任务完全完成然后同步进行下一个操作;如果想像 js 那样,那可以把函数名传进去消费者,由消费者结束后再调用函数执行
cz5424
2022-04-27 09:58:17 +08:00
因为历史原因,celery 开发的时候没有 asyncio ,改成 asyncio 估计要推到重建,这样就变成了遥遥无期
fengjianxinghun
2022-04-27 09:59:48 +08:00
celery 是个传销软件,作者早就弃坑了。连个任务硬超时都没作好
paopjian
2022-04-27 10:08:02 +08:00
这是 python 的问题还是 celery 的问题呢?
so1n
2022-04-27 10:13:59 +08:00
celey 自己实现了一个异步库 所以改造比较难,至于第二点自己封装一下就可以实现了
youngce
2022-04-27 10:17:07 +08:00
“2202 年了,asyncio 居然不是被原生支持的。“

不被原生支持的 python 库还是大多数的吧。。。asyncio 生态好了一些,但是指望随手找一个库都是原生支持 asyncio ,真的还有太长的路要走。

说白了就是 python 半路加入协程带来的阵痛
LeeReamond
2022-04-27 10:19:41 +08:00
@so1n 我只是很质疑,事件以及回文通知需要自己封装。还有问题又回到 worker 不支持异步调用,所以我需要在异步网关那里 await channel ,然后 worker 这边同步 publish 。过程中间的可靠性又有一大票需要维护的,那为什么我需要一个第三方任务队列呢
julyclyde
2022-04-27 10:22:46 +08:00
看不懂……
我一直以为 celery 既然都是离线任务,其实同步写法也可以啊
LeeReamond
2022-04-27 10:23:31 +08:00
@youngce 绝大多数计算库不做支持完全可以理解,基于 asgi 的 web 框架不做异步支持也可以理解,毕竟没有必要重新发明轮子,当然也有一些偏就发明了的,比如 flask 。甚至 sqlalchemy 之类的 ORM 不支持也可以理解,但是现在好像也不需要我理解了。现在 py 唯一指定任务队列,文档里第一句话写我是为了处理大量分布式任务存在的,它解决分布式任务的方式是让大家都用同步代码这。。
LeeReamond
2022-04-27 10:24:13 +08:00
@LeeReamond *wsgi
LeeReamond
2022-04-27 10:32:13 +08:00
@fengjianxinghun 草,看了一下 contribute 列表还真是,原作者提交显示 2016 年就弃坑了,之后一直是社区在维护。我看他最近版本是 22 天前发布的还以为更新非常频繁
est
2022-04-27 11:01:43 +08:00
celery 从来就没好感。。。。python 里有 3 大骗子营销软件

1 是 flask
2 是 celery
3 是 gunicorn

其实好多实现都是玩具级别。当然别人是开源项目,没法指责作者什么,但是哪些写教程的把吹上天就不对了。
catsoul
2022-04-27 11:08:49 +08:00
@est @fengjianxinghun 我觉得也不能说是传销吧,毕竟简单的东西就是容易流行,也容易被各种自媒体拿来洗稿吸粉,这也是没办法的事情。

另外初学者通过这些简单的东西入门其实也没问题,至于后面怎么办,那只能说愿意钻研的人总有办法找到更好的替代品或者更可以贡献代码让这些东西不再只是一个玩具
ospider
2022-04-27 11:12:54 +08:00
celery 就是个玩具级别的项目,谁用谁被坑
Richard14
2022-04-27 11:17:23 +08:00
@est gunicorn 有什么问题吗,因为现在 fastapi 炒的火,fastapi 里的推荐部署方式都是 gunicorn
Te11UA
2022-04-27 11:17:40 +08:00
所以现在建议用的任务队列是什么呢?
jenlors
2022-04-27 11:25:32 +08:00
est
2022-04-27 11:38:58 +08:00
@Richard14 fastapi 我也在用,比如它 http 日志如何规范搜集?

https://github.com/encode/uvicorn/issues/527

gunicorn 这货只要指定了 work ,就甩手啥都不管了。日志日志不管,被 SIGTERM 杀了也不管,感情 gunicorn 就管了个 fd ,管了个寂寞啊。
julyclyde
2022-04-27 13:24:00 +08:00
@est flask 也是骗子啊?咋啦?
LeeReamond
2022-04-27 13:27:55 +08:00
@est uvicorn 我还真不知道,gunicorn 以前倒是做过,记不太清了,印象里是 logging.getlogger("gunicorn.err")这种感觉的,然后重写 handler

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

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

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

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

© 2021 V2EX