api7 / api7/ngx_http_ffi_client

Roadmap: OpenResty/APISIX FFI HTTP client

Open
#1 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
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 —— requestbuffer/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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.