从Nginx到Pingora?从 Cloudflare 的架构演进讲起

1. Cloudflare 的代理架构演进史

1.1 第一代:Nginx 时代(2009-2019)

Cloudflare 成立于 2009 年,最初的边缘代理架构基于 Nginx。这是一个合理的选择:

  • 成熟稳定:Nginx 经过千万级网站验证

  • 事件驱动:epoll/kqueue 支撑高并发

  • 模块化:C 模块扩展功能

    ┌─────────────────────────────────────────┐
    │ Cloudflare Edge (2009) │
    │ ┌─────────┐ ┌─────────┐ ┌─────┐ │
    │ │ DNS │───→│ Nginx │───→Cache│ │
    │ └─────────┘ └────┬────┘ └─────┘ │
    │ │ │
    │ ┌────┴────┐ │
    │ │ Upstream │ │
    │ │ (Origin) │ │
    │ └─────────┘ │
    └─────────────────────────────────────────┘

1.2 Nginx 的瓶颈显现

随着 Cloudflare 规模指数级增长,Nginx 架构的固有局限开始暴露:

维度 Nginx 的限制 Cloudflare 的需求
连接管理 每个连接一个文件描述符,工作进程隔离 千万级长连接共享
内存模型 多进程 + 共享内存(有限) 统一内存池,精细控制
热更新 reload 信号,连接可能中断 零停机,连接保持
可观测性 有限的状态导出 实时精细化指标
定制能力 C 模块开发成本高 Rust 安全扩展
c 复制代码
// Nginx 的 Worker 进程模型 ------ 内存隔离是双刃剑
// 每个 worker 独立处理连接,无法跨进程复用连接池

nginx.conf:
worker_processes auto;  # 通常 CPU 核数

events {
    worker_connections 4096;  # 每个 worker 的连接上限
}

1.3 第二代:内部框架(2019-2022)

Cloudflare 开始构建内部代理框架,基于 Rust + Tokio:

复制代码
┌─────────────────────────────────────────────────────┐
│         Cloudflare Internal Proxy (2019)            │
│  ┌─────────────┐    ┌───────────────────────────┐  │
│  │   Tokio     │    │      Unified Runtime       │  │
│  │  Runtime    │───→│  (Single-process, async)   │  │
│  └─────────────┘    └───────────────────────────┘  │
│                                                     │
│  关键改进:                                          │
│  - 单进程异步模型,消除 worker 隔离                    │
│  - 连接池全局共享                                    │
│  - 零拷贝转发优化                                    │
└─────────────────────────────────────────────────────┘

这个内部框架在 Cloudflare 生产环境验证了 3 年,处理了数万亿请求。

1.4 第三代:Pingora 开源(2022-至今)

2022 年,Cloudflare 将内部框架开源为 Pingora:

rust 复制代码
// Pingora 的架构哲学:Library > Framework
// 不是给你配置文件的代理,而是给你构建代理的积木

use pingora_core::server::Server;
use pingora_core::upstreams::peer::HttpPeer;
use pingora_proxy::{ProxyHttp, Session};

#[async_trait]
impl ProxyHttp for MyProxy {
    async fn upstream_peer(&self, session: &mut Session) -> Result<Box<HttpPeer>> {
        // 完全自定义 upstream 选择逻辑
        let peer = self.load_balancer.select(session).await?;
        Ok(Box::new(HttpPeer::new(peer)))
    }
}

2. Pingora 相比 Nginx 的核心优势

2.1 架构层面的本质差异

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                    架构对比:进程模型                              │
├───────────────────────────┬─────────────────────────────────────┤
│      Nginx                │           Pingora                   │
│  ┌─────┐ ┌─────┐ ┌─────┐ │  ┌─────────────────────────────────┐│
│  │ W1  │ │ W2  │ │ W3  │ │  │         Single Process          ││
│  │[EP] │ │[EP] │ │[EP] │ │  │  ┌─────────┐  ┌─────────┐       ││
│  └──┬──┘ └──┬──┘ └──┬──┘ │  │  │ Thread1 │  │ Thread2 │  ...  ││
│     │       │       │    │  │  │[Tokio]  │  │[Tokio]  │       ││
│  ┌──┴───────┴───────┴──┐ │  │  └────┬────┘  └────┬────┘       ││
│  │    Shared Memory    │ │  │       └─────────────┘            ││
│  │   (Limited usage)   │ │  │           │                      ││
│  └─────────────────────┘ │  │      ┌────┴────┐                 ││
│                          │  │      │  Shared │                 ││
│  EP = Event Poll (epoll) │  │      │  State  │                 ││
│                          │  │      └─────────┘                 ││
└──────────────────────────┴─────────────────────────────────────┘

关键差异

  1. Nginx:多进程模型,worker 间连接池无法共享
  2. Pingora:单进程多线程,Tokio runtime 统一调度,连接池全局可见

2.2 连接复用的革命性提升

Nginx 的多进程模型带来了一个根本性限制:每个 worker 进程只能看到自己建立的上游连接 。如果 worker A 与 backend-1:8080 有 10 个空闲连接,worker B 的新请求也路由到同一 backend,它无法复用 worker A 的连接,只能重新建立。

Pingora 的单进程多线程模型消除了这个边界:所有 async 任务共享同一个 ConnectionPool(基于 DashMap 实现无锁并发访问)。任意线程都能取到任意线程归还的连接,全局复用率自然远高于 Nginx。

连接池的 key 设计也体现了精细化:不仅按 addr:port 区分,还区分 TLS/plaintext、SNI、ALPN 协议、出口绑定 IP------这保证了不同安全上下文的连接不会被错误复用。

:以下性能数据来自 Cloudflare 2022 年发布的博客文章,基于其生产环境的测量,具体数值可能因工作负载不同而有较大差异。

指标 Nginx Pingora 提升
长连接复用率 ~60% ~95% 1.6x
内存占用/连接 ~2.5KB ~0.5KB 5x
CPU 使用率(同负载) 100% ~60% 1.7x

2.3 内存效率的代际差距

Pingora 内存效率优势来自两个层面:

连接级 :Nginx 为每个连接分配固定大小的内存池(通常 2--4 KB),无论连接是否活跃。Pingora 的连接以 PooledConnection 表示,空闲时只保留元数据(几百字节),I/O 缓冲区(BytesMut)在处理请求时按需分配,请求完成后可以释放或缩小。

转发级 :Pingora 在转发 HTTP 请求头时使用 write_vectoredwritev 系统调用),把多个 Bytes 切片组成 iovec 数组一次性发出。这些切片都是对原始解析缓冲区的视图引用,不需要先拼接到新缓冲区------从读取到转发,头部数据没有发生过一次 memcpy

2.4 热升级:fd 继承机制

Nginx 的 reload 信号实际上是优雅重启:启动新 worker 进程,旧 worker 处理完当前请求后退出。这期间:

  • 新 worker 开始接受新连接
  • 旧 worker 上的 keep-alive 连接被强制关闭(客户端会看到 connection reset)
  • 上游连接池完全重建

Pingora 的热升级基于 Unix 文件描述符继承SCM_RIGHTS):

复制代码
旧进程 ──fork + exec──→ 新进程
   │                      │
   │  传递所有 listen fd   │
   └──────SCM_RIGHTS──────→ 新进程开始 accept()
   │
   │  等待现有请求完成(最多 N 秒)
   │
   └──── 退出

listen socket 的文件描述符被传递给新进程,现有 TCP 连接保持不断(fd 引用计数不归零),客户端完全无感知。上游连接池需要重建,但这对客户端没有影响。

3. 什么时候应该选择 Pingora?

3.1 Pingora 适合的场景

场景 原因
超大规模边缘代理 千万级连接,连接复用至关重要
需要深度定制 Rust 代码扩展,而非 C 模块
多协议网关 HTTP/1, HTTP/2, gRPC, WebSocket 统一处理
与 Rust 生态集成 直接复用 Rust crate 生态
极致性能要求 零拷贝、精细内存控制

3.2 Nginx 仍然优秀的场景

场景 原因
简单静态文件服务 Nginx 成熟,配置简单
已有成熟运维体系 配置文件管理、监控生态丰富
团队无 Rust 经验 学习成本需要考虑
需要特定第三方模块 OpenResty/Lua 生态依赖

4. Cloudflare 的迁移经验

4.1 迁移时间线

复制代码
2019: 内部原型开发,验证 Rust + Tokio 可行性
2020: 小规模生产测试,处理 1% 流量
2021: 大规模部署,处理 50% 流量
2022: 完全替代 Nginx,开源 Pingora
2023: Pingora 处理 Cloudflare 100% 边缘流量

4.2 关键教训

  1. 不要直接平移配置:Pingora 是 library,需要重新思考架构
  2. 充分利用类型系统:Rust 的编译期检查在生产环境中防止了大量 bug
  3. 连接管理是核心:投资源优化连接池,回报巨大
  4. 可观测性先行:完善的 metrics 和 tracing 是运维基础

5. 总结:代理架构的三代演进

代际 代表 核心模型 关键特征
第一代 Nginx/Apache 多进程/多线程 稳定、成熟、进程隔离
第二代 Envoy 事件驱动 + xDS 云原生、服务网格
第三代 Pingora 异步 Rust 安全、零拷贝、极致性能

Pingora 不是简单地"更快",而是代表了系统编程语言演进 (C → Rust)和异步运行时成熟(epoll → io_uring/Tokio)在代理场景的结合。

Cloudflare 的选择表明:当规模达到一定程度,基础设施值得用现代系统语言重写

相关推荐
RReality2 小时前
【UGUI】自定义 ListView 架构:设计、原理与可扩展性
unity·架构
willhuo2 小时前
服务器大存储与实时备份解决方案:基于SSH隧道的灾备数据同步系统
运维·服务器·ssh
telllong2 小时前
深入理解React Fiber架构:从栈调和到时间切片
前端·react.js·架构
bukeyiwanshui2 小时前
20260423 一、 负载均衡-LVS 全解析
运维·负载均衡·lvs
赋创小助手2 小时前
OpenClaw部署架构详解:从桌面到数据中心的AI Agent服务器选型指南
服务器·人工智能·架构·agent·openclaw
IT摆渡者2 小时前
Linux 巡检脚本BASH
linux·运维·bash
小宋加油啊2 小时前
服务器双卡5090 配置深度学习环境
运维·服务器·深度学习
minji...2 小时前
Linux 网络套接字编程(二)从 0 到 1 实现 UDP 回声服务器,recvfrom,sendto
linux·运维·网络·单片机·udp
Cyber4K2 小时前
【DevOps专项】GitLab 与 Jenkins 介绍及部署持续集成环境
运维·ci/cd·gitlab·jenkins·devops