concurrency设为cpu核心数不总是最优,因i/o密集型任务中过多进程会加剧上下文切换和内存开销;应据任务类型调优,并优先观测真实瓶颈。

为什么 concurrency 设为 CPU 核心数不总是最优?
Celery 默认用 prefork 模型(即多进程),concurrency 参数控制工作进程数。但吞吐量瓶颈未必在 CPU:I/O 密集型任务(如 HTTP 请求、数据库查询)下,增加进程数反而加剧上下文切换和内存开销,导致吞吐下降。
实操建议:
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
- 先用
celery -A proj inspect active_queues确认实际消费队列是否均衡; - 对纯 I/O 任务,尝试将
concurrency设为2 × CPU 核心数(例如 4 核机器设为 8),再逐步压测; - 若任务含大量同步阻塞调用(如未加
await的requests.get),优先改用异步客户端(aiohttp+celery[redis]配合eventlet或gevent); - 避免盲目设
concurrency=100—— 进程太多会触发 Linuxulimit -n限制,出现Too many open files错误。
如何用 eventlet 替代 prefork 提升 I/O 并发?
eventlet 是协程模型,单进程内可支撑数千并发,适合高 I/O、低计算的任务。但它不兼容某些 C 扩展(如 numpy、部分数据库驱动的同步接口)。
实操建议:
- 安装时明确指定:
pip install celery[eventlet]; - 启动命令必须加
-P eventlet -c 1000(-c此处是协程数,非进程数); - 确保所有 I/O 调用都“greenified”:比如用
eventlet.patcher.monkey_patch()(放在celery.py初始化最前); - 注意
eventlet不支持 Windows,且time.sleep()必须换成eventlet.sleep(),否则协程挂起失效。
worker_prefetch_multiplier 设太高反而拖慢响应?
该参数决定每个 worker 进程预取多少条消息到本地内存(默认 4)。值过大时,长任务会“锁住”大量消息,新进短任务排队等待,整体延迟升高。
实操建议:
- 对任务耗时差异大的场景(如 100ms 和 30s 混合),设为
1最稳妥; - 若所有任务执行时间稳定(±20%),可适度提高到
2~4,减少 broker 通信开销; - 配合
task_acks_late=True使用:确保任务真正执行完才确认,避免预取后 worker 崩溃导致消息丢失; - 检查
celery -A proj inspect stats输出中的prefetch_count,确认是否生效。
Redis 作为 broker 时,连接池配置常被忽略
Celery 默认为每个 worker 进程创建独立 Redis 连接,concurrency=16 就可能打开 16+ 连接。Redis 默认 maxclients=10000,看似够用,但连接复用率低会放大网络延迟和内存占用。
实操建议:
- 显式配置连接池:
BROKER_POOL_LIMIT = 10(限制每个进程最多复用 10 个连接); - 使用
redis://...?max_connections=20URL 参数,与BROKER_POOL_LIMIT协同; - 若用
eventlet,必须设BROKER_POOL_LIMIT = None(禁用池化),否则协程间共享连接会引发竞态; - 监控 Redis 的
connected_clients指标,突增或持续高位说明连接未回收。
celery -A proj inspect stats 和 redis-cli info clients 看真实连接与负载分布,再动参数。吞吐瓶颈经常藏在数据库连接池、HTTP 客户端超时、甚至 DNS 解析上,而不是 Celery 自身。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










