压测前必须确认python服务的可观测性接入点真实生效:metrics需验证prometheus端口绑定成功,tracing需确保opentelemetry早期注册并能查到zipkin/jaeger链路,日志须含request_id和耗时字段。

压测前必须确认的 Python 服务可观测性接入点
没埋点、没指标、没日志,压测就是蒙眼开车。Python 服务在压测前最常被跳过的一步,是确认 metrics 和 tracing 是否真实生效,而不是“配了就等于有”。
-
prometheus_client的start_http_server()必须在主进程启动后调用,且端口未被其他进程占用(常见于 Gunicorn 多 worker 场景下重复启动导致绑定失败) - OpenTelemetry 的
TracerProvider需在应用初始化早期注册,否则 Flask/FastAPI 中间件注入会漏掉首请求;验证方式是发一个真实请求后查http://localhost:9411/api/v2/spans(Zipkin)或本地 Jaeger UI - 日志需确保
structlog或logging输出含request_id和耗时字段,否则压测中异常请求根本无法定位到具体 trace
Gunicorn + Uvicorn 混合部署下的并发模型陷阱
用 uvicorn 做 worker、gunicorn 做进程管理时,--workers 和 --worker-connections 的组合极易超卖资源,导致 CPU 打满但 QPS 不升反降。
- 不要设
--workers> CPU 核数;Python 的 GIL 让多进程收益有硬上限,4 核机器上设 8 workers 往往让上下文切换成本超过吞吐增益 -
--worker-connections在uvicornworker 下实际无效(它只对eventlet或gevent生效),但很多人误以为能提升并发,结果只是掩盖了异步瓶颈 - 真实压测前,先用
ab -n 1000 -c 100 http://localhost:8000/health看top中的%CPU和RES内存增长是否线性——不线性说明存在锁、阻塞 IO 或连接池不足
压测脚本里 requests.Session() 的复用误区
用 locust 或原生 threading 写压测脚本时,requests.Session() 如果在每个 task 函数里新建,DNS 解析和 TCP 连接会成为性能瓶颈,尤其在短连接高频请求场景下。
- Session 必须按线程/协程粒度复用:Locust 中放在
on_start()里初始化为self.client;纯 threading 脚本则用threading.local()存储 - 务必设置
pool_connections=10和pool_maxsize=10(与目标服务的 keep-alive 连接数匹配),否则默认的10/10在高并发下会排队等待连接 - 忽略
Connection: close响应头会导致连接无法复用,可在压测脚本中加session.headers.update({'Connection': 'keep-alive'})强制保活(前提是服务端支持)
全链路数据一致性校验不能只靠响应码
压测时看到 HTTP 200 就认为成功,是全链路压测中最危险的错觉。下游 DB 写入延迟、缓存穿透、消息队列堆积,都会让接口返回成功但业务状态不一致。
立即学习“Python免费学习笔记(深入)”;
- 必须设计轻量级一致性探针:例如压测下单接口后,立即用
redis-cli --raw get order_status:<order_id></order_id>查缓存,并用psql -c "SELECT status FROM orders WHERE id = '<order_id>'"</order_id>查 DB,两者必须一致 - 避免在压测流量里混入校验请求——这会污染指标;正确做法是压测结束后,从日志或 trace 中抽样
order_id,再离线比对 - 若使用 Kafka,需确认
acks=all且retries=5已配置,否则压测中消息丢失不会报错,但下游消费永远收不到
全链路压测最难的不是跑出高 QPS,而是让每一条链路上的中间件、存储、网络行为都暴露在可观测之下——少一个 request_id 透传,就可能让一次超时变成三天排查。










