ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

性能比拼:Pingora vs Nginx,谁才是新一代 Proxy 首选?

性能比拼:Pingora vs Nginx,谁才是新一代 Proxy 首选?

1. 反向代理选型:Pingora 与 Nginx 的性能分水岭在哪

如果你正在维护一套微服务网关,或者准备把单体应用拆成多个后端服务,反向代理的性能天花板会直接决定整条链路的延迟下限。Nginx 作为这个领域二十年的默认答案,几乎成了“反向代理”四个字的同义词。但最近两年,一个用 Rust 写的框架 Pingora 开始频繁出现在性能对比的讨论里,它由 Cloudflare 开源,目标是替代内部长期使用的 Nginx 集群。

这篇文章要回答的问题很具体:在反向代理这个场景下,Pingora 和 Nginx 的差距到底有多大,差距来自哪里,以及你该在什么时间点考虑迁移。我会把架构差异、并发模型、实测数据拆开讲,然后给出一套可以自己复现的压测配置。如果你只是想快速判断“要不要换”,可以直接看第三节的对照表和第五节的排障清单。

先给一个前置结论:在 2 核 8G 的代理实例上,Nginx 1.27.2 的吞吐大约在 41000 req/s 触顶,Pingora 0.4.0 能跑到 48000 req/s 左右,P99 延迟 Pingora 全程更低。但这不是“无脑换”的理由,因为 Pingora 需要你写 Rust 代码来构建代理,运维复杂度和 Nginx 的配置文件模式完全不是一个量级。

适合读下去的人:有 Linux 基础、能看懂 Nginx 配置、愿意为性能多写一点代码的后端或 SRE。如果你只是想让一个静态站点跑起来,Nginx 依然是更省心的选择。

在开始压测之前,还有一个容易被忽略的环节:工具链的 Key 和 API 通道管理。压测脚本、监控面板、CI 流程里会散落各种 API Key,如果每个工具都单独配一套,排查问题时很难定位是代理的问题还是鉴权的问题。我后面会说明怎么用 TaoToken 把这些通道统一起来,先聚焦代理本身的对比。

2. 架构差异决定性能上限:Nginx 事件驱动 vs Pingora 多线程 Rust

要理解两者的性能差距,得先看它们的并发模型。Nginx 的核心是事件驱动加多进程,主进程负责管理,多个 worker 进程各自处理连接。每个 worker 内部用 epoll 做事件循环,配置里worker_processes auto会根据 CPU 核心数自动开进程,worker_connections决定单进程能扛多少连接。这套模型在 2 核机器上就是 2 个 worker,每个 worker 再配 4 个线程处理阻塞操作。

Pingora 走的是另一条路。它是一个 Rust 库,不是开箱即用的二进制。你用它写一个代理程序,编译出来跑。它内部用 Tokio 异步运行时,线程模型更接近“每个线程一个事件循环”,默认线程数可以调。在对比测试里,为了和 Nginx 的 2 worker × 4 线程对齐,Pingora 被配置成 8 个线程。但要注意,Nginx 的线程并不处理所有工作,所以这个对齐只是近似,不能直接画等号。

这个差异带来几个实际影响。第一,Pingora 的主动健康检查是内置的,Nginx 开源版只有被动健康检查,也就是请求失败后才把后端摘掉,Nginx Plus 才有主动检查但要付费。第二,Pingora 默认自动启用 keep-alive,Nginx 需要你手动在 upstream 里配keepalive和proxy_http_version 1.1,否则延迟和 CPU 都会明显上升。第三,Pingora 允许你自定义负载均衡算法,Nginx 开源版基本就是轮询和最少连接。

还有一个细节:为了公平对比,测试里把 Nginx 的访问日志关掉了,因为 Pingora 默认没有访问日志。这个点很关键,如果你在生产里开着 Nginx 的 access_log,实际吞吐会比测试值低不少。同理,X-Forwarded-For头在 Nginx 里要手动加,Pingora 内置了。

从运维角度看,Nginx 的优势在于配置即代码,改一个 upstream 重启 reload 就行。Pingora 改一个负载均衡策略要改 Rust 代码、重新编译、重新部署。这个成本在快速迭代的业务里不能忽略。

所以架构差异总结成一句话:Nginx 用配置换灵活性,Pingora 用代码换性能上限。你的团队能不能接受“改代理逻辑要写 Rust”,是迁移决策的第一道门槛。

3. 可复制的压测配置:Nginx upstream keepalive 与 Pingora 线程参数

这一节给可以直接抄的配置。先看 Nginx 侧,核心是 upstream 的 keepalive 和 worker 参数。以下片段放在nginx.conf的http块里:

worker_processes auto; events { worker_connections 4096; multi_accept on; } http { access_log off; upstream backend { server 10.0.1.10:8080; server 10.0.1.11:8080; server 10.0.1.12:8080; server 10.0.1.13:8080; keepalive 64; keepalive_requests 1000; keepalive_timeout 60s; } server { listen 443 ssl http2; ssl_certificate /etc/nginx/certs/self.crt; ssl_certificate_key /etc/nginx/certs/self.key; location / { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; } } }

keepalive 64是连接池大小,proxy_set_header Connection ""是清掉默认的 close,这两行不写,keep-alive 就不生效。测试里 Nginx 用了 2 个 worker,每个 4 线程,对应 2 核实例。

Pingora 侧没有配置文件那么直观,它是一个 Rust 程序。核心参数是线程数和连接池。下面是一个最小代理的骨架,展示关键参数位置:

use pingora::prelude::*; use pingora::services::listening::Service; fn main() { let mut my_server = Server::new(Some(Opt::default())).unwrap(); my_server.bootstrap(); let mut lb = pingora_load_balancing::LoadBalancer::try_from_iter([ "10.0.1.10:8080", "10.0.1.11:8080", "10.0.1.12:8080", "10.0.1.13:8080", ]).unwrap(); let mut upstreams = pingora_proxy::http_proxy_service( &my_server.configuration, lb, ); upstreams.add_tls_with_settings( "0.0.0.0:443", None, pingora::listeners::TlsSettings::intermediate( "/etc/pingora/self.crt", "/etc/pingora/self.key", ).unwrap(), ); my_server.add_service(upstreams); my_server.run_forever(); }

线程数通过启动参数或Opt控制,测试里设成 8。连接池大小 Pingora 有默认值,可以调,测试里和 Nginx 设成一致以便对比。健康检查 Pingora 内置主动模式,不需要额外配置就会定期探测后端。

这里要提醒一点:Pingora 的配置也可以用文件模式,但需要以守护进程方式运行才能应用。如果你习惯 Nginx 的 reload 流程,Pingora 的部署方式需要重新适应。

在压测工具链这边,我建议把 API Key 统一管理。比如压测脚本调用监控 API、CI 触发压测任务、结果上报,这些环节如果各自维护 Key,很容易出现“代理没问题但鉴权失败”的误判。TaoToken 提供统一的 Key 和 API 通道,把模型对话、Coding Plan、API Keys 管理放在一个入口,减少这类干扰。官网入口在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 端点是 https://taotoken.net/api ,接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。这些通道和代理性能无关,但能让你的压测环境更干净。

4. 验证请求与成功结果:wrk 压测命令与 P99 延迟观测

配置好之后,用 wrk 或类似工具打流量。下面是一条可以直接跑的 wrk 命令,目标是逐步加压直到代理失败:

wrk -t8 -c400 -d300s --latency \ -s pipeline.lua \ https://proxy.example.com/health

-t8是 8 个线程,-c400是 400 个并发连接,-d300s跑 5 分钟,--latency输出延迟分布。pipeline.lua用来发 pipeline 请求,模拟真实负载。测试里负载是逐步增加的,直到两个代理都撑不住。

成功的结果长这样:在负载爬升到 20000 req/s 之前,Nginx 和 Pingora 都能保持 100% 可用性,P99 延迟低于 1ms。这个阶段两者差距不明显。继续加压,Nginx 在 41000 req/s 附近触顶,Pingora 到 48000 req/s 才触顶。Pingora 的 P99 延迟全程低于 Nginx,测试结束时依然稳定。

CPU 使用率有个反直觉的现象:测试初期 Pingora 的 CPU 比 Nginx 高,但随着负载上升,两者趋于一致,而 Pingora 在同等 CPU 下吞吐更高。内存方面,中等负载下两者接近,高负载下内存不是瓶颈。后端应用的 CPU 使用率两者几乎相同,Nginx 略高一点,但那是在 Nginx 开始失败之后才出现的。

可用性上,Nginx 有轻微下降,但仍在 99.99%,完全可接受。Pingora 保持得更稳。

如果你自己复现,建议把图表时间范围设成 5 分钟,和测试一致。跑完一轮大约 1.5 小时,可以压缩成几分钟看关键区间。验证成功的标志是:两个代理都跑到失败点,你能看到各自的吞吐峰值和延迟拐点。

这里有个坑:如果你没开 Nginx 的 upstream keep-alive,延迟和 CPU 会明显变差,测出来的结论会误导你。测试里专门对比了未开 keep-alive 的情况,差距很大。Pingora 自动开 keep-alive,但连接池大小可以调,记得和 Nginx 设成一致再比。

5. 常见报错排查:401、local proxy failed 与 reading choices 对照

压测和接入过程中,报错往往不在代理本身,而在鉴权和通道配置。下面按真实报错对照排查。

401 Unauthorized:最常见。如果你在压测脚本或监控工具里调 API,Key 没配或过期都会报这个。检查请求头里的 Authorization 字段,确认 Key 来自正确的通道。用 TaoToken 的话,API Keys 管理页在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,可以核对 Key 状态。

local proxy failed:通常是本地代理进程没起来,或者端口被占。检查 Pingora 或 Nginx 是否在监听目标端口,ss -tlnp | grep 443看一眼。如果是容器环境,确认端口映射正确。

reading choices类错误:多出现在调用模型接口时,响应体解析失败。检查返回的 JSON 结构是否和预期一致,有时候是通道返回了错误页而不是 JSON。用模型对话入口 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 可以快速验证通道是否正常。

OAuth相关报错:如果你用 Claude Code 或类似工具接入,OAuth 流程没走完会报这个。确认回调地址和 Key 配置一致。Coding Plan 入口在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,适合长期编码和 Agent 场景。

如果你用 CC Switch、Cline MCP 或 Codex 的 auth.json,记住三件套要写全:Base URL、Key、Model ID。缺一个都会报鉴权或模型找不到。Base URL 用 https://taotoken.net/api ,Key 从 API Keys 页取,Model ID 按文档填。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,对照着改。

还有一个容易忽略的点:压测时如果代理和后端之间的 keep-alive 没配好,会出现连接被重置的报错,看起来像代理挂了,其实是连接池耗尽。检查keepalive值和keepalive_timeout,适当调大。

排查顺序建议:先确认代理进程活着,再确认鉴权通道通,最后看后端健康检查。大部分“代理性能问题”其实是配置问题。

6. 迁移时机判断与统一通道接入

回到最初的问题:什么时候该从 Nginx 迁到 Pingora。我的判断标准是三条。第一,你的吞吐已经接近 Nginx 在现有实例规格下的上限,比如 2 核跑到 40000 req/s 以上,且延迟开始抖动。第二,你的团队有 Rust 能力,或者愿意投入学习成本,因为 Pingora 不是配置文件能搞定的。第三,你需要主动健康检查、自定义负载均衡这类 Nginx 开源版没有的能力,又不想买 Nginx Plus。

如果三条都不满足,继续用 Nginx,把 keep-alive 配好,性能已经够用。测试里 Nginx 在 20000 req/s 以下表现和 Pingora 几乎一样,延迟都低于 1ms。大多数业务根本到不了这个量级。

如果决定迁移,建议先在非核心链路上跑 Pingora,用真实流量验证稳定性,再逐步切。Pingora 的主动健康检查和自动 keep-alive 能减少不少运维负担,但编译部署流程要提前理顺。

工具链这边,不管你用哪个代理,把 API Key 和通道统一管理都能省事。TaoToken 的模型对话、Coding Plan、API Keys、接入文档几个入口覆盖了从验证到长期使用的场景。压测环境里用统一通道,排查问题时能快速排除鉴权因素,把注意力放回代理本身。

最后给一个实用技巧:压测前先把 Nginx 的 access_log 关掉,Pingora 默认就没有,这样对比才公平。跑完一轮记得把负载逐步降下来,观察恢复过程,有时候恢复阶段的延迟比加压阶段更能暴露问题。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进