api7 / api7/ngx_http_ffi_client
Roadmap: OpenResty/APISIX FFI HTTP client
Nobody has claimed this yet.
- Dominant language
- C
- Stars
- 2
- Forks
- 0
- PR merge metrics
- No merged PRs in 30d
Description
ngx_http_ffi_client 总规划
背景
ngx_http_ffi_client 的目标是在 OpenResty/APISIX worker 进程内提供一个 LuaJIT FFI HTTP client。Lua 层通过 resty.ngx_http_ffi_client 发起请求,C 层负责 HTTP 请求拼包、发送、接收和解析,并接入 Nginx/OpenResty 的事件循环,避免阻塞 worker。
这个 issue 是总规划和需求池,不直接对应单个 PR。每个可控阶段会拆成独立子 issue 和独立 PR。
当前目标范围
当前先推进到性能验证阶段,也就是完成以下 4 个阶段:
- #2 Bootstrap: repository skeleton
- #3 MVP: plain HTTP/1.1 short-lived request path
- #4 Keepalive: plain HTTP/1.1 connection pool
- #5 Benchmark: C FFI client vs Lua cosocket
完成 #5 后,应能判断这个 C FFI HTTP client 方案相对纯 Lua cosocket 方案是否有明确性能收益,以及是否值得继续投入后续 HTTP 完整性、HTTPS、APISIX 集成等工作。
更新(结论已得出):#2–#5 均已完成关闭。C FFI 方案相对纯 Lua cosocket 有明确性能收益 —— 一次性小响应下,cosocket 的 outbound CPU 约为 C FFI 的 2 倍(1.9–2.1×),且该差距是结构性的、干净优化只能抹掉 3–11%(隔离对比:两侧同用 llhttp 解析、仅 I/O 模型不同,见
benchmark/cosocket-vs-c.md)。结论:值得继续投入后续 HTTP 完整性、HTTPS、APISIX 集成等工作。
阶段原则
- 每个子 issue 工作量必须可控。
- 每个子 issue 应能独立验收。
- 一个 PR 只解决一个阶段的主要目标。
- 不在 MVP 中引入 HTTPS、TLS、SNI、HTTP/2、streaming 等额外变量。
- 正式性能比较必须基于双方都启用 keepalive 的场景。
后续暂不创建的 backlog
完成 #5 后已经做了一轮 parser backend 选型验证(不在原 4 阶段范围内,但产出是后续决策依据):
- llhttp(sans-io C parser),#10(已合并,默认关闭,
NGX_HTTP_FFI_CLIENT_USE_LLHTTP=1开启):吞吐量对比手写 parser 基本持平(-1.25%)。 - hyper(Rust async runtime),#9(已关闭未合并):多一层运行时桥接,吞吐量下降 29%~74%,HTTP/1 场景不合适。
结论:parser 层不再维护手写实现,改用 llhttp;TLS 层用 rustls(sans-io)——in-process 内嵌 nginx、不做 sidecar 的硬约束基本排除 Go/Node,只剩 Rust。详见 README「Project status」一节。下面这份 backlog 是这轮决策之前写的,按新结论更新如下:
chunked response parser→ 把 llhttp 转正为默认 backend、删除手写 parser。chunked/trailer/连接关闭语义随之解决,不要在手写 parser 上另外打补丁。- header edge cases,聚焦 llhttp 之上 FFI/Lua 侧的 header 契约(重复 header、大小写、多值等),不是重新写解析。
- request body and method coverage(例如 HEAD 响应体抑制,#10 已知未覆盖)。
- 增量 / 流式响应消费(大 body 分片、大模型 SSE):已出具体设计,见 #12 ——
request上buffer/stream/sse三种 mode + Lua 驱动的零拷贝 reader(read()/close()),走全 C(不走 cosocket,理由见 #12 与benchmark/cosocket-vs-c.md)。当前 llhttp 后端仍是「缓冲整个 body 再返回」,SSE / 超大 body 必须走这套增量契约。 - timeout and cancellation semantics。
- error model and diagnostics。
- 组件解耦(新增):引入 TLS 层之前,先把 FFI contract / 事件循环与传输 / TLS 层 / HTTP parser 这几层接口定清楚,让每层可以独立替换。接下来要连续经历 parser 换血(llhttp)→ 引入 TLS(rustls)→ 未来可能的 HTTP/2(hyper)三次替换,不先做这层容易被各 feature 焊死耦合。
- HTTPS TLS handshake and encrypted IO,限定用 rustls(sans-io),不用 OpenSSL/BoringSSL C 绑定。
- SNI and certificate verification,属于 rustls 层的一部分,不单独选型。
- APISIX integration smoke test。
- packaging and build matrix,范围已从纯 C 模块构建变成 C + Rust 混合工具链(llhttp 已引入独立 vendored 静态库构建,rustls/hyper 会再引入 cargo/交叉编译),按新范围估工作量。
parser backend 选型这次决策本身没有对应 sub-issue(#9/#10 均未关联 issue),先在这里记一笔,避免以后看到代码里两套 backend 却不知道原因。
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
This issue is a roadmap rather than a single implementation task. Start with the README's Project status section, #12, and benchmark/cosocket-vs-c.md, then choose a concrete backlog item or sub-issue such as the streaming contract, TLS integration, or APISIX smoke test; done should be defined by that item's independent acceptance criteria.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c, lua, nginx, rust
- Domain
- api, backend, performance
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 20/100