为何Celery处理IO密集型任务比同步方式快得多?
我正在学习Python并发编程,用Celery执行脚本发起50次请求仅耗时约0.13秒,这让我非常惊讶!而用常规同步编程完成同样操作则需要约1分钟。
我知道Celery会生成进程池执行任务,但搞不懂为什么Celery执行50次任务的速度会比同步执行单次任务还快这么多。
同步代码示例
# sync.py import json import requests from timer import Timer URL = 'https://httpbin.org/uuid' def fetch(url): response = requests.get(url) data = json.loads(response.text) print(data['uuid']) def main(): with Timer(): for _ in range(1): fetch(URL) main() # take ~50 seconds
Celery异步代码示例
# celery_demo.py from celery import Celery from timer import Timer import json import requests URL = 'https://httpbin.org/uuid' BROKER_URL = 'redis://localhost:6379/0' RESULT_BACKEND = 'redis://localhost:6379/0' app = Celery(__name__, broker=BROKER_URL, backend=RESULT_BACKEND) @app.task(name='fetch') def fetch(): res = requests.get(URL) return res.json()['uuid'] def main(): with Timer(): for i in range(50): res = fetch.delay() print(res) if __name__ == '__main__': main() # Celery configuration # celery -A celery_demo.app worker --concurrency=4 # takes ~0.1 to 0.2 seconds
原因解析
计时范围的核心差异:同步代码里的Timer包裹了单次请求的完整流程——从发起请求、等待服务器响应,到接收数据、解析输出,统计的是请求真实的全程耗时;而Celery代码里的Timer只统计了把任务提交到消息队列的过程,
fetch.delay()只是向worker发送任务指令后立刻返回任务ID,真正的请求执行、等待响应等操作都是在后台worker中异步进行的,这部分时间完全没被Timer算进去。并发执行的真实逻辑:你配置的Celery worker开启了4个并发进程,能同时处理多个请求。但当前的计时并没有体现并发执行的实际耗时,如果你修改代码,等待所有Celery任务执行完成后再结束Timer(比如用
res.get()逐个等待结果,或者用group批量提交后统一等待),统计出的时间会比单次同步请求长,但肯定远低于50次同步请求的总耗时——因为并发可以让多个请求同时处于网络等待状态,CPU不用空等,能高效利用资源。网络请求的IO特性:网络请求的大部分时间都花在等待服务器响应(IO等待),同步代码只能串行等待,而Celery的并发模式可以把这些等待时间重叠起来,自然整体效率会大幅提升,但你当前的测试没有捕捉到这个真实效率,只是测了任务提交的速度。
内容的提问来源于stack exchange,提问作者Quoc-Hung Hoang

