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

8660 次点击
所在节点    Python
76 条回复
ClericPy
2022-04-27 16:43:31 +08:00
uvicorn 那日志, 好几次想清都没清干净, 想改改了不对... 不过倒是学到了 propagate 这个参数, 以前居然从来没用过这东西. 好在 Python 的库就算再难搞, 基本都可以魔改一下解决问题, 遇到 Cython 的才真麻烦

至于 celery... 前同事挺喜欢配合 Django 用的, 然后遇到了几次 worker block 主线程的一个常见问题, 后来交接给我看了几天直接全丢掉了, 从那之后能解耦到消息队列的, 基本不用任务队列, 耦合的东西越多越复杂, 一出问题找不到就头大. 现在更是能不用框架就不用框架了

all-in 了两年多协程, 痛苦并快乐着. 最舒服的地方是 CPU 利用率高了不少, 内存也很友好, 最烦躁的地方是写了几百个 to_thread 代码长的太难看了..... 有次 DOM 解析纯计算那种本来觉得速度很快可以不放 run_in_executor, 结果遇到两三万 tag 的 HTML 直接把主线程堵了, 调试好久才找到. 唉, 真羡慕 go 那种方式
chenxf84
2022-04-27 16:54:27 +08:00
自己用 redis 实现吧,celery 已经被我们抛弃了
asuraa
2022-04-27 16:57:44 +08:00
@abersheeran 没错 这货我们上次项目差点用了 哦写了个 demo 着实被恶心到了,一个上下文到处传递 连个切面注入都没有 真正的营销库
CatCode
2022-04-27 18:45:05 +08:00
@est 详细聊聊?
so1n
2022-04-27 19:19:24 +08:00
@ClericPy 通过这个库,可以写一些类似于 go 的调度的...https://github.com/omnilib/aiomultiprocess
so1n
2022-04-27 19:23:30 +08:00
@LeeReamond
task_async_result: AsyncResult = AsyncResult(task_id)
result = task_async_result.get(timeout=3)
so1n
2022-04-27 19:24:18 +08:00
so1n
2022-04-27 19:25:01 +08:00
@LeeReamond 自动回复了两条忽略下,最简单就是这样实现
```Python
from celery.result import AsyncResult
task_async_result: AsyncResult = AsyncResult(task_id)
result = task_async_result.get(timeout=3)
```
ClericPy
2022-04-27 19:43:26 +08:00
@so1n 这库几个月前看到过, 能解决无法利用多核问题, 也能节省一些套路代码, 不过还是用不习惯, 以前自己代码里已经做过类似的操作了.

主要还是羡慕 go 那种管它协程线程进程的异步直接丢给 CSP 的原生体验, 吐槽一下, 感觉 py 有不少设计尾大不掉地, 勉强期待一下 3.11 但对协程易用性暂时不抱太大期望
so1n
2022-04-27 19:50:18 +08:00
@ClericPy 这个确实,新生语言没有那么多包袱
volvo007
2022-04-27 20:26:49 +08:00
消息队列的话, 不是有一个国人写的分布式消息队列库吗
不是很熟悉但是看起来好像还可以?
xmap
2022-04-27 20:51:36 +08:00
看了这么多讨论,似乎都是一些边角 case ,不是“核心设计”级别的不合格啊?
这样就轻易的说: 是玩具?
hkz670
2022-04-27 21:34:51 +08:00
@volvo007 #51 之前见过 celery 相关博客下发现有人留言“写的太好了。但是本质还是 celery 设计的太复杂,我推荐一个国产的高性能、支持人物语言、任何框架的分布式消息队列库”,

就是那个作者自己写的脚本刷的评论。。。博客园 celery 相关的博文,都快被他刷完了
tcpdump
2022-04-28 00:08:26 +08:00
回复标记一下,celery 居然有这么不堪? 目前没遇到什么问题,有代替品或者有自己实现的大概思路可以分享一下?
neoblackcap
2022-04-28 02:57:23 +08:00
@tcpdump dropbox 分享过一个任务系统的设计思路。celery 玩具不至于,不过架构复杂,导致加什么功能都比较难吧。而且 celery 自身已经有任务编排聚合功能,一般的任务队列还真是不能跟它比较。
至于 asyncio ,理论上自己写一个 worker 类是完全可以支持的。只不过很多人也不看源码,也不看文档。所以就觉得不行。反正 IO 复用,我是用过 gevent 的 worker 类,效果还不错。
LeeReamond
2022-04-28 03:06:53 +08:00
@so1n #48 问题在于你这是同步代码,转到异步里难道开线程池?

@neoblackcap 读了几遍回复感觉你说的挺乱的,你说的可以是 asyncio 可以还是 gevent 可以,社区维护者 issue 里标记说 aio 无支持,我觉得你有解决方案可以放出来,解决全网网友痛点。
gjquoiai
2022-04-28 10:56:59 +08:00
python 的 asyncio 生态基本没有靠谱的库(特指 web 开发),建议远离。。回到任务队列,喜欢 redis 的试试 huey ,喜欢 rabbitmq 的试试 dramatiq ,当然这俩相比于 celery 都有一些功能缺失,不过都可以克服。。
est
2022-04-28 11:10:04 +08:00
这么多人觉得 celery 还不错,可能这几年的确改进不少了吧。这里 8 一下有个老外用 celery 的故事

早年间有个网站叫 backtype ,就是把你在全世界 blog/twitter 等各个地方留的评论聚合在一个地方,这样你可以整理你的互联网踪迹,它家当年就是用了大量 celery 任务来跑数据(不奇怪),celery 问题在哪里呢,数据处理本身可能代码不复杂,复杂的是数据怎么可靠的进去,可靠的出来,数据之间依赖怎么处理,Celery **当时** 简直烂透了。就简单的说失败重试都是不可靠的。可能新手连它异常出在哪一步都不清楚。需要写大量的业务之外的保障性代码。这作者就自己轮了一个基于 jvm 的框架,把异步任务当成一个分布式 stream 来整体管控,他的这个设计得到了极大的成功,被推崇为「 realtime hadoop 」,后来他把这个框架开源了,交给了 apache 孵化,成了 apache storm

http://nathanmarz.com/blog/history-of-apache-storm-and-lessons-learned.html

吐槽 py 的东西烂不是为了秀优越感而贬低它,是真的希望有改进,或者更好的替代出现。
est
2022-04-28 11:11:24 +08:00
@gjquoiai dramatiq 我老早就推荐过了。。t/418343 不知道现在许可证授权问题有解不。
est
2022-04-28 11:17:27 +08:00
另外 8 一嘴,backtype 被 twitter 收购了,收购过程中, 因为作者的这个 celery 轮子吹得太好,twitter 展示了极大的兴趣,极大提高 backtype 被收购估值

> During acquisition talks I announced Storm to the world by writing a post on BackType's tech blog. The purpose of the post was actually just to raise our valuation in the negotiations with Twitter. And it worked: Twitter became extremely interested in the technology, and when they did their tech due-diligence on us, the entire due-diligence turned into a big demo of Storm.
> The post had some surprising other effects. In the post I casually referred to Storm as "the Hadoop of realtime", and this phrase really caught on. To this day people still use it, and it even gets butchered into "realtime Hadoop" by many people. This accidental branding was really powerful and helped with adoption later on.



所以喷 celery 烂,光拿出替代品是不够的,还要得会包装,会营销。

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

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

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

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

© 2021 V2EX