当前位置: 首页 > news >正文

python中的works的工作原理

workers到底在做什么、为什么需要它、它和线程/协程的关系、进程如何分工、请求是怎么被分配的、怎么根据业务算出合适的 worker 数。

  1. 总体架构:ASGI + 事件循环 + 进程模型
    • ASGI 应用(FastAPI、Starlette 等)是一个“异步应用对象”。
    • Uvicorn 是 ASGI 服务器,实现了:网络 IO、HTTP 解析、事件循环(默认 uvloop)、把请求转成 ASGI 事件交给应用处理。
    • Gunicorn 是一个“进程管理器”(prefork 模型,像 Nginx 的 master/worker)。它负责:
    • 启动一个 master(arbiter) 进程;
    • fork 出 N 个 worker 子进程(-w / --workers 就是这个 N);
    • 监听端口、接管信号、健康检查、崩溃拉起、优雅重载等;
    • 每个 worker 内部再运行一个 Uvicorn 事件循环(-k uvicorn.workers.UvicornWorker)。

你也可以直接用 uvicorn --workers N … 启动多进程,但生产更推荐让 Gunicorn 负责进程管理,Uvicorn 只负责协议栈和事件循环。

  1. 为什么是“多进程”而不是“多线程”
    • Python GIL 限制了同一进程内同时执行字节码的线程数量。
    • 多个 worker = 多个独立 Python 进程,每个进程有自己的 GIL,可以真正用满多核。
    • 一个 worker 内部用的是 异步事件循环(asyncio/uvloop):
    • 一个事件循环线程就能处理大量并发连接(网络 IO 不阻塞时);
    • 但如果你做 CPU 重活(加密、压缩、ML 推理),会阻塞这个事件循环,导致单 worker 吞吐下降。

结论:IO 密集用“少量进程 + 每进程大量协程”,CPU 密集则“多进程分摊重活”。

  1. 请求是怎么分发给 workers 的?

有两种常见 socket 接收模式(Gunicorn 控):
1. master 接收:master 把新连接 round-robin 分发给各 worker(传统、可控)。
2. SO_REUSEPORT:每个 worker 都 bind 同一个端口,内核把新连接直接分配给某个 worker(减少 master 抖动,扩展性更好)。

无论哪种,每个 worker 都维护自己的事件循环,在 loop 里把“连接 → HTTP 报文 → ASGI 事件 → app 调用 → 响应”跑一遍。
Keep-Alive 的连接会在同一 worker 内复用,不会跨 worker。

  1. worker、线程、协程的关系与区别
    • worker(进程):–workers N 创建 N 个进程;最粗粒度的并行,绕过 GIL。
    • 线程(gthread/gevent 线程池):某些 worker-class 有 --threads;在 Python 中受 GIL 影响,适合 IO 混合或少量阻塞。
    • 协程(asyncio 任务):Uvicorn 的核心;单线程事件循环上创建成千上万 task,靠 IO 切换实现高并发。前提:应用函数必须是异步且不做阻塞计算/阻塞 IO。

在 uvicorn.workers.UvicornWorker 下,建议不要再叠线程;如果你必须调用阻塞库,用 asyncio.to_thread() 或 loop.run_in_executor() 把它丢给线程/进程池。

  1. 不同 worker-class 的差异(Gunicorn 视角)
    • sync:同步阻塞,简单但并发差。
    • gthread:同步 + 线程池;IO 混合场景可用,但有 GIL。
    • gevent/eventlet:协作式并发(猴补丁),生态偏 WSGI。
    • uvicorn.workers.UvicornWorker:ASGI 原生异步 + uvloop/httptools,FastAPI 首选。
    • UvicornH11Worker:用纯 Python h11 解析 HTTP(兼容性更广,吞吐略低)。

  2. –workers 如何影响性能(吞吐、延迟、抖动)
    • 吞吐:更多 worker = 更多 CPU 并行度;但过多会引发上下文切换、缓存失效、内存压力,反而掉速。
    • 延迟:worker 太少,排队时间(队列等待)变长;太多,进程争抢导致抖动上升。
    • 内存:每个 worker 都要加载你的应用与依赖(甚至模型),内存线性增长;可以用 --preload(见下一节)配合写时复制节省一部分内存,但别指望对“会频繁写”的对象有奇效。

  3. –preload/preload_app 的写时复制(COW)原理
    • 开启 preload 时,Gunicorn 在 master 里先导入 app,再 fork 出 workers。
    • Linux 的 Copy-On-Write:fork 初始时不复制内存页,父子共享;当某个进程修改某页时才复制那一页。
    • 对于只读的大对象(如加载到CPU 内存的只读表、词典、路由树),多个 worker 可以共享大量物理页,节省内存。
    • 但注意:
    • 若对象在 worker 中被写入/修改,马上失去共享优势;
    • GPU 显存不参与 COW,每个 worker 都要各自占用显存(重推/模型场景要谨慎)。

  4. 如何“算”出合适的 worker 数(不仅仅是 2×CPU+1)

Gunicorn 的经验公式:workers = 2 × CPU核数 + 1。但更科学的做法是结合业务特征和性能目标:
• 设每个请求的平均 服务时间(S)(秒),单 worker 的有效并发容量(Cw)(async 任务上限 / 实测并发),目标 RPS。
• 用粗略的 Little’s Law 直觉:并发 ≈ 吞吐 × 响应时间。
若单 worker 在稳定状态下能支撑 Cw ≈ RPS_per_worker × S,
则需要的 worker 近似为:
workers ≈ (目标RPS × S) / Cw(向上取整,再预留 20–30% 余量)。

例子(IO 密集 API):
• 目标 3,000 RPS,平均响应时间 S = 50ms = 0.05s;
• 单 worker 经压测能稳定跑 Cw ≈ 400 并发(事件循环+DB IO);
• 需要 workers ≈ (3000 × 0.05) / 400 = 0.375 → 取 1–2;
但考虑 p95 延迟与抖动、滚动升级、安全余量,往往配 8–12 个更稳(8 核)。

例子(CPU 密集):
• 单请求 CPU 计算 100ms,单 worker 几乎难并发(会阻塞事件循环),
一个 worker 的“有效并发”≈ 1–2;
8 核机器就接近 8–12 个 worker(不要太多,避免争抢)。

  1. 关键参数对照与调优建议
    • --workers N:进程数(最重要)。
    • -k uvicorn.workers.UvicornWorker:ASGI 模式。
    • --timeout:单请求最大处理时长(默认 30s,后端慢时需要上调)。
    • --graceful-timeout:优雅停止给的收尾时间(处理完在途请求)。
    • --keep-alive:HTTP Keep-Alive 空闲秒数(默认 2);API 网关/浏览器前置时可适度调高。
    • --max-requests / --max-requests-jitter:worker 处理一定请求数后重启,缓解内存碎片/泄漏。
    • --preload:减少启动时的重复加载、配合 COW 节省内存(对 GPU 无效)。
    • 系统层:ulimit -n(文件描述符)、tcp_tw_reuse、backlog、容器 CPU/内存限制等。

  2. 8 核机器的几种“可解释”推荐

A. 典型 FastAPI IO-密集(DB/Redis/外部 API)

gunicorn app.main:app
-w 12
-k uvicorn.workers.UvicornWorker
-b 0.0.0.0:8000
–timeout 120 --graceful-timeout 30
–keep-alive 10
–max-requests 5000 --max-requests-jitter 500
–preload

解释:12 个进程让 CPU 有余量处理突发;keep-alive 提高连接复用;max-requests 周期性重启缓解内存增长;preload 降低 RSS。

B. 计算/推理偏重(CPU-bound)

gunicorn app.main:app
-w 8
-k uvicorn.workers.UvicornWorker
-b 0.0.0.0:8000
–timeout 300 --graceful-timeout 60
–max-requests 1000 --max-requests-jitter 200

解释:基本与核数等同,避免过多进程争用;超时上调;周期性重启避免长时间累积的碎片。

C. 单机多实例 + 负载均衡
• 每实例 -w 6~8,跑 2–3 个实例,由前面的 Nginx/Envoy/ALB 做分流;
• 实际上更稳、更易滚动升级(蓝绿/金丝雀)。

  1. 阻塞点与“协程失效”的典型坑
    • 在异步端点里调用阻塞库(Requests、pymysql 同步模式、耗时的本地函数)会卡住事件循环 → 单 worker 吞吐暴跌;
    对策:改用异步库(httpx[async]、asyncpg、数据库驱动的 async 版本)或 asyncio.to_thread()/进程池。
    • 大 JSON 序列化、压缩、加解密、图像编解码等 CPU 重活——放执行器/子进程。
    • 大文件上传/下载:用 StreamingResponse,别把整块文件搬进内存。

  2. 生命周期与优雅重启
    • Lifespan(ASGI startup/shutdown)在每个 worker 启动/关闭时触发:连接池、缓存、模型、线程池都在这里初始化/释放。
    • 信号:
    • HUP/USR2:平滑重载;
    • TERM:优雅终止(先停止接新连接,等待在途完成,超时再杀)。
    • 配合 --graceful-timeout 与负载均衡的逐个摘流,可以实现零停机发布。

一句话总括
• --workers = 进程并行度,是“绕开 GIL、用满多核”的核心手段;
• 单 worker = 事件循环 + 海量协程,擅长 IO 并发,怕阻塞;
• “合适的 workers” 取决于:CPU/IO 比例、单请求耗时、目标 RPS、内存预算与稳定性约束;
• 2×CPU+1 是保守默认,真正的最佳值要压测出来。

http://www.dtcms.com/a/479274.html

相关文章:

  • 0基础建站网站搭建教程设计公司logo用什么软件
  • 陕西天工建设有限公司官方网站网站做视频的软件
  • 冒泡排序的原理?
  • 上行30m可以做网站吗前端做一个页面多少钱
  • 2025国内GEO优化服务商综合评测:抢占AI搜索流量新入口
  • 龙华网站建设推广免费行情软件网站大全网页版
  • 为什么收不到自己网站沈阳市网站制作公司
  • IT服务(IT Services):从定义、分类到产业实践的全面解析
  • 做vr效果图的网站如何寻找seo网站建设客户
  • 域名暂无法进行网站备案电子工程师社区
  • 做公司网站教程视频快速建企业网站
  • 网上做兼职的网站有哪些免费设计素材库
  • 普通用户执行ps兼容sudo和非sudo场景
  • Java--网络编程(二)
  • 如何将网站转成小程序网站 微信 app
  • 若依框架下的接口测试
  • Unity网络开发--第三方协议工具Protobuf
  • 城阳网站建设公司wordpress首页文章缩略图插件
  • 兼职招聘网站中国最新军事新闻
  • MATLAB做一个简单的元胞自动机:森林火灾模拟
  • 嵌入式开发培训机构排名seo自动推广工具
  • 百度网盘公益解析网站下载限速一边去
  • 站酷网首页蛋糕店微网站
  • 2025年--Lc183--198. 打家劫舍(菲波那契数列类型)--Java版
  • linux系统中命令基础
  • 站酷网站建设合肥网建公司
  • 湖南旅游网站开发做毕业设计的网站
  • 安卓上谷歌35版本
  • 《架构设计精讲》学习笔记
  • Uvicorn在多核CPU上启动的参数workers