在 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 程序作为配置逻辑。脚本在打包时通过 clang 和 llc 编译为 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
内容声明:本文无广告投放、无付费植入。
如有事实性问题,欢迎发送勘误至 i@hotdrydog.com。