Hotdry.

Article

zeroserve 与 Caddy 集成:反向代理层 3x 吞吐量优化的技术决策

解析 zeroserve 在小响应代理场景下实现 Caddy 3.4 倍吞吐量的技术原理,提供可落地的分层架构配置与参数调优方案。

2026-06-14systems

在 HTTPS 反向代理的性能优化领域,一个新兴项目 zeroserve 正在挑战 Caddy 和 Nginx 的统治地位。根据 su3.io 发布的基准测试数据,在小响应体(174B)的代理场景下,zeroserve 单核吞吐量达到 26,486 req/s,相较 Caddy 的 7,683 req/s 实现了约 3.4 倍的性能提升。本文将深入分析这一性能差距背后的技术决策,并探讨如何在现有 Caddy 架构中集成 zeroserve 以获取显著的吞吐量收益。

性能基准:数据背后的真相

测试环境采用 8 核 Ryzen 7 3700X,所有服务器均绑定单核运行(taskset),使用 wrk -t4 -c100 生成负载。测试场景为 TLS 1.3 终止后代理至后端服务,后端为独立 2 核服务器,自身可支撑 100k req/s,确保测量结果仅反映代理层开销。

小响应代理(174B)结果:

代理服务器 吞吐量 (req/s) P50 延迟 P99 延迟
zeroserve 26,486 3.3 ms 8 ms
nginx 21,761 4.2 ms 10.5 ms
Caddy 7,683 10.3 ms 33 ms

zeroserve 在此场景下不仅领先 Caddy 约 3.4 倍,甚至超越了以性能著称的 Nginx(领先约 22%)。然而,当响应体增大至 100KB 时,排名发生逆转:Nginx 以 5,882 req/s 领先,Caddy 为 4,285 req/s,zeroserve 降至 3,631 req/s。这一差异揭示了不同代理服务器在 I/O 模型和缓冲策略上的本质区别。

技术架构:io_uring 与 eBPF 的双轮驱动

zeroserve 的性能优势源于三个核心技术决策:

1. io_uring 全链路异步 I/O

zeroserve 基于 monoio 运行时构建,所有网络和磁盘操作均通过 Linux io_uring 提交。相比传统的 epoll 模型,io_uring 通过内核与用户空间共享的环形缓冲区实现零拷贝提交,减少了系统调用次数和上下文切换开销。在 TLS 加密场景下,由于数据必须在用户空间完成加密(无法使用内核 TLS),io_uring 的批量提交能力显著降低了 per-request 的系统调用成本。

2. 用户态 eBPF 脚本引擎

zeroserve 摒弃了传统的配置文件,采用 eBPF 程序作为配置逻辑。脚本在打包时通过 clangllc 编译为 BPF 字节码,运行时通过 async-ebpf 运行时 JIT 编译为原生 x86-64 机器码。关键点在于:

  • 指针笼(Pointer Cage):替代内核 BPF 验证器,通过内存访问掩码将脚本限制在独立内存区域
  • 可抢占执行:2-10 ms 的抢占定时器允许中断长时间运行的脚本,防止单请求阻塞事件循环
  • 零成本抽象:JIT 后的脚本以原生速度执行,TLS 加密成为 per-request 的主要开销

调优参数 --preempt-timer-interval-ms 对性能影响显著:默认值 2 ms 时,eBPF 动态响应吞吐约 32k req/s;提升至 10 ms 后,吞吐量恢复约 40%,达到 46,945 req/s,超越 nginx LuaJIT 的 41,231 req/s。

3. 上游连接池优化

zeroserve 通过 zs_reverse_proxy("http://backend:port") 实现代理功能,内部维护每个后端最多 128 个连接、30 秒空闲超时的连接池。与 Nginx 默认关闭 keep-alive 不同,zeroserve 默认启用连接复用,消除了 per-request 的 TCP 握手和 TLS 协商开销。

分层架构:zeroserve 前置 + Caddy 后端

鉴于 zeroserve 在小响应场景下的性能优势和 Caddy 的丰富生态,推荐采用分层架构:

Internet → zeroserve (TLS 终止 + 静态文件 + 小响应代理)
              ↓
         Caddy (复杂路由 + 大文件代理 + 插件生态)
              ↓
         应用服务器

zeroserve 层配置要点

部署模式:单实例单线程,通过多进程实现多核扩展。每个实例约 15 MB PSS,代码页在多进程间共享。

# 打包站点
cd /var/www
zeroserve --pack ./public > site.tar

# 单实例启动(绑定单核)
taskset -c 0 zeroserve --addr 0.0.0.0:443 site.tar &
taskset -c 1 zeroserve --addr 0.0.0.0:443 site.tar &
# ... 每核一个实例

eBPF 代理脚本示例.zeroserve/scripts/proxy.c):

#include <zeroserve.h>

ZS_ENTRY
zs_u64 entry(void) {
    char path[256];
    zs_req_path(path, sizeof(path));
    
    // API 路由直接代理到 Caddy
    if (zs_strncmp(path, "/api/", 5) == 0) {
        // 小响应 API 直接代理
        zs_reverse_proxy("http://127.0.0.1:8080");
        return 0;
    }
    
    // 大文件路由标记,由后续脚本处理
    if (zs_strncmp(path, "/download/", 10) == 0) {
        zs_meta_set(ZS_STR("zs.skip_zeroserve"), ZS_STR("true"));
    }
    
    return 0;
}

Caddy 层优化参数

对于必须流经 Caddy 的大响应或复杂路由,启用以下调优:

:8080 {
    reverse_proxy localhost:3000 {
        # 连接池调优
        max_conns_per_host 128
        
        # 健康检查
        health_uri /health
        health_interval 10s
        
        # 缓冲策略:大文件场景启用缓冲
        buffer_requests
        buffer_responses
        
        # 超时参数
        transport http {
            dial_timeout 5s
            response_header_timeout 10s
            expect_continue_timeout 1s
        }
    }
    
    # 压缩策略:小响应压缩,大文件跳过
    encode {
        gzip
        minimum_length 1024
    }
}

可落地的性能调优清单

1. 响应体大小阈值决策

基于基准测试数据,建议设置 50-100 KB 作为路由决策阈值:

  • < 100 KB:路由至 zeroserve 直接代理,获取 3x+ 吞吐量
  • ≥ 100 KB:路由至 Caddy/Nginx,利用其成熟的缓冲机制

2. 抢占定时器调优

根据脚本复杂度调整 zeroserve 的抢占间隔:

# 简单脚本(纯代理逻辑):10 ms 获取最大吞吐
zeroserve --preempt-timer-interval-ms 10 site.tar

# 复杂脚本(含 JSON 解析、加密操作):2 ms 保证公平性
zeroserve --preempt-timer-interval-ms 2 site.tar

3. 连接池参数

zeroserve 内部连接池默认 128 连接 / 后端,在超高并发场景可适当提升:

// 在 eBPF 脚本中动态调整(需重新编译)
// 当前版本暂不支持运行时调整,建议通过多实例分担

4. 热重载策略

zeroserve 支持 SIGHUP 原子热重载,无需中断连接:

# 更新 tarball 后
killall -SIGHUP zeroserve

适用场景与限制

zeroserve 优势场景

  • API 网关:小 JSON 响应、高频调用
  • 微服务代理:服务间通信、短连接复用
  • 边缘节点:TLS 终止 + 静态文件 + 轻量路由

应继续使用 Caddy 的场景

  • 大文件下载(> 100 KB)
  • 需要复杂插件生态(如动态 DNS、自动证书管理)
  • 多租户配置管理(Caddy 的 Caddyfile 更易维护)

已知限制

  • zeroserve 单实例单线程,需多进程实现多核扩展
  • eBPF 脚本开发门槛高于配置语言
  • 大响应体场景性能落后于 Nginx

结论

zeroserve 通过 io_uring 和 eBPF 的技术组合,在小响应代理场景下实现了相较 Caddy 约 3.4 倍的吞吐量提升。这一性能差距并非来自单一优化,而是全链路异步 I/O、JIT 编译脚本和高效连接池的协同结果。对于以 API 调用为主的现代 Web 架构,采用 zeroserve 作为前置代理层、Caddy 处理复杂路由的分层架构,是在保持运维便利性的同时获取显著性能收益的可行路径。


参考来源

  • Heyang Zhou, "zeroserve: a zero-config web server you can script with eBPF", su3.io, 2026
  • Manish R Jain, "HTTPS Reverse Proxy: Caddy outperforms NGINX 4x", manishrjain.com, 2022

systems

内容声明:本文无广告投放、无付费植入。

如有事实性问题,欢迎发送勘误至 i@hotdrydog.com