Fluent Bit 内置 nghttp2:流控观测 API nghttp2_session_get_stream_effective_recv_data_length 全解
【免费下载链接】fluent-bitFast and Lightweight Logs, Metrics and Traces processor for Linux, BSD, OSX and Windows项目地址: https://gitcode.com/GitHub_Trending/fl/fluent-bit
导读:Fluent Bit 在lib/下内置了 nghttp2 1.65.0 源码树,其 HTTP/2 客户端与 HTTP/2 服务端均构建在这套实现之上。本文以 nghttp2 官方文档页nghttp2_session_get_stream_effective_recv_data_length为核心,完整继承该 API 的语义定义,并深入 nghttp2_session.c 源码与 单元测试,讲清 HTTP/2 流控窗口记账中“有效已接收数据长度”的精确含义、边界行为及其在 Fluent Bit 中的实际落点。读完你可以准确解释该返回值在何种情况下会小于实际收到的字节数,并能读懂相关测试断言。
一、API 定义:返回值到底在度量什么
按照 nghttp2 文档页 nghttp2_session_get_stream_effective_recv_data_length.rst 的原文定义,该函数签名为:
#include <nghttp2/nghttp2.h> int32_t nghttp2_session_get_stream_effective_recv_data_length( nghttp2_session *session, int32_t stream_id);其官方语义(逐点完整继承原文档):
- 度量对象:返回流
stream_id上已收到但尚未通过 WINDOW_UPDATE 帧归还的 DATA 载荷字节数,单位是字节。这是 HTTP/2 流控的核心记账变量——接收端每收到 DATA,本地窗口就被消耗,直到发出(或自动发出)WINDOW_UPDATE 才重新释放。 - 受本地窗口调整影响:本地(接收侧)窗口大小可以通过
nghttp2_submit_window_update()调整。本函数会把这种调整考虑在内,返回的是“有效(effective)”数据长度,而不是原始的累计接收字节数。 - 负增量下的特殊行为:特别地,如果调用方通过
nghttp2_submit_window_update()提交了负的window_size_increment来缩小本地窗口,那么本函数的返回值会小于实际收到的字节数。这是原文档明确强调的关键细节:它反映的是“窗口记账”视角下的长度,而非“流量”视角下的长度。 - 失败返回:函数失败时返回
-1。
在 公共头文件 中,该声明位于第 3802 行附近,与连接级对偶函数nghttp2_session_get_effective_recv_data_length()(作用于stream_id = 0的连接窗口)同属一组流控观测 API。
二、源码实现:一行核心逻辑背后的窗口记账
该函数的实现极其精炼,位于 nghttp2_session.c:
int32_t nghttp2_session_get_stream_effective_recv_data_length(nghttp2_session *session, int32_t stream_id) { nghttp2_stream *stream; stream = nghttp2_session_get_stream(session, stream_id); if (stream == NULL) { return -1; } return stream->recv_window_size < 0 ? 0 : stream->recv_window_size; }从源码结构看,可以确认三点实现事实:
-1的唯一来源是“流不存在”:nghttp2_session_get_stream()找不到该stream_id时直接返回 -1。也就是说,文档所说的 “fails” 在 nghttp2 中具体指“该流不存在(或已被关闭并从会话中移除)”,而不是内存类错误。- 返回值就是
stream->recv_window_size本身:nghttp2 没有单独维护“有效长度”字段,而是把“尚未通过 WINDOW_UPDATE 归还的已收字节数”直接记账在流的recv_window_size上。这解释了为什么窗口被负增量缩小后,该值会低于真实接收量。 - 负值被钳制为 0:
recv_window_size < 0 ? 0 : ...保证了负窗口记账不会向调用方暴露为负数。
recv_window_size 是如何被写出的
围绕这一字段的写路径在 nghttp2_session.c 中集中实现:
adjust_recv_window_size()(约第 4945 行)负责累加增量,并在超过本地窗口上限或NGHTTP2_MAX_WINDOW_SIZE时拒绝(返回NGHTTP2_ERR_FLOW_CONTROL),防止窗口记账溢出;data_released()(约第 5014 行)负责在数据被消费者释放后回补窗口,必要时自动提交 WINDOW_UPDATE,若回补后窗口仍被耗尽(recv_window_size小于等于 0),则直接以当前已收字节数为增量发出 WINDOW_UPDATE,把recv_window_size复位为 0;- 会话初始化时(第 459 行)连接级
recv_window_size置 0,随后按初始窗口参数建立基线。
初始窗口常量的定义
流控的“起点”定义在 nghttp2.h:
#define NGHTTP2_INITIAL_WINDOW_SIZE ((1 << 16) - 1) /* 65535 */ #define NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE ((1 << 16) - 1) /* 65535 */即流级与连接级初始窗口均为 65535 字节。理解这两个常量后,文档中“窗口缩小导致返回值小于实际接收量”才能落到具体数字上:例如某流初始窗口 65535,收到 100000 字节后若已通过 WINDOW_UPDATE 归还 50000 字节,则recv_window_size记账为 50000,本函数返回 50000 而非 100000。
三、配套 API:nghttp2_submit_window_update 的窗口调整语义
原文档两次引用了nghttp2_submit_window_update(),其完整语义见 nghttp2_submit_window_update.rst:
int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags, int32_t stream_id, int32_t window_size_increment);flags目前被忽略,应传NGHTTP2_FLAG_NONE;stream_id指定目标流;传0表示提交连接级WINDOW_UPDATE;window_size_increment为正:入队一个该增量的 WINDOW_UPDATE;若增量大于对端已发送字节数,本地窗口按差值上调;window_size_increment为负:本地窗口减小|increment|;若启用了自动 WINDOW_UPDATE(通过nghttp2_option_set_no_auto_window_update()可禁用),且库判断应当提交,则按当前已收字节数入队 WINDOW_UPDATE;window_size_increment为 0:什么都不做,直接返回 0;- 成功返回 0,失败返回
NGHTTP2_ERR_FLOW_CONTROL(本地窗口溢出或变负)或NGHTTP2_ERR_NOMEM。
文档页中特别提示:如果目的只是调整本地窗口大小而不真正发帧,应使用nghttp2_session_set_local_window_size()。这与本 API 形成互补——一个负责“改窗口”,一个负责“查账”。
四、单元测试:负增量窗口下的完整断言序列
nghttp2 自带的 nghttp2_session_test.c 中,test_nghttp2_session_get_effective_local_window_size()用例对本 API 的行为做了端到端验证,核心断言序列值得逐行读懂:
/* 流级窗口检查(stream 1) */ stream->recv_window_size = 100; nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 1, 1100); /* 有效窗口 = 65535 + 1000 */ assert_int32(0, ==, nghttp2_session_get_stream_effective_recv_data_length(session, 1)); nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 1, -50); /* 此时 stream->recv_window_size = -50 */ assert_int32(0, ==, nghttp2_session_get_stream_effective_recv_data_length(session, 1)); stream->recv_window_size += 50; /* 记账归零 */ nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 1, 100); assert_int32(50, ==, nghttp2_session_get_stream_effective_recv_data_length(session, 1));这段测试验证了原文档的三条语义:
- 未收到任何 DATA(记账为 0)时,即使反复增删窗口,返回值恒为 0;
- 负增量把窗口记账打穿到负数(
recv_window_size = -50)时,函数返回0而非 -50,印证源码中< 0 ? 0 :的钳制逻辑; - 窗口记账恢复并再次收到数据后,返回值精确等于“尚未归还”的字节数(50)。
同用例前半段(约第 7760–7793 行)对连接级nghttp2_session_get_effective_recv_data_length()做了完全对称的验证,说明流级与连接级共享同一套recv_window_size记账模型,只是分别挂在nghttp2_stream与nghttp2_session两个结构体上。
五、在 Fluent Bit 中的落点:nghttp2 作为内置传输底座
Fluent Bit 将 nghttp2 以源码树形式内置于 lib/nghttp2-1.65.0/,作为其 HTTP/2 能力的唯一实现来源。从源码结构看,仓库中直接集成该库的两处关键位置是:
- src/flb_http_client_http2.c:Fluent Bit HTTP/2 出站客户端。输出插件(如 out_http 系列、各类云厂商输出)在
tls+ HTTP/2 ALPN 协商成功时走此路径,数据发送、头块处理、流管理均由 nghttp2 会话驱动; - src/http_server/flb_http_server_http2.c:Fluent Bit 内置 HTTP 服务端(
$HTTP_SERVER/$HTTP_LISTEN参数,$HTTP2 true启用)的 HTTP/2 接入层,基于 nghttp2 的 server session 处理请求。
需要说明的是:从当前仓库源码检索的结果看,Fluent Bit 自身代码并未直接调用nghttp2_session_get_stream_effective_recv_data_length(),它属于 nghttp2 暴露给集成方的流控观测 API 之一,主要用于需要精细监控“窗口耗尽程度”的场景——例如判断某个接收流是否已逼近窗口上限、是否需要主动提交 WINDOW_UPDATE 以避免对端 DATA 停发。对于 Fluent Bit 这类“接收侧以自动窗口管理为主”的用法,理解该 API 的价值在于读懂内置库的流控行为边界:当窗口被耗尽且自动窗口更新未及时释放时,HTTP/2 对端会停止发送 DATA,表现上即“大消息接收变慢/停滞”,这正是本文档页所描述的记账变量直接影响的链路。
六、使用要点小结
结合文档定义、nghttp2_session.c 实现与测试断言,可将该 API 的使用要点归纳为:
| 要点 | 依据 |
|---|---|
返回 -1 表示stream_id对应的流在当前会话中不存在 | 源码实现,nghttp2_session.c |
| 返回值 = 尚未通过 WINDOW_UPDATE 归还的 DATA 载荷字节数 | 文档页与recv_window_size记账逻辑 |
受nghttp2_submit_window_update()调整影响,可能小于真实累计接收量 | 文档页“effective”语义,测试用例中负增量场景 |
| 负窗口记账被钳制为 0,不会返回负值 | 源码stream->recv_window_size < 0 ? 0 : ... |
流级窗口初始值 65535 字节(NGHTTP2_INITIAL_WINDOW_SIZE) | nghttp2.h |
连接级对偶 API 为nghttp2_session_get_effective_recv_data_length() | nghttp2.h 声明区 |
适用前提与限制:以上分析基于本仓库内置的 nghttp2 1.65.0 版本(lib/nghttp2-1.65.0/),行为以其 公共头文件 与 会话模块 为准;若你的项目链接了其他版本的 libnghttp2,建议对照对应版本的文档页与tests/nghttp2_session_test.c确认语义是否一致。
【免费下载链接】fluent-bitFast and Lightweight Logs, Metrics and Traces processor for Linux, BSD, OSX and Windows项目地址: https://gitcode.com/GitHub_Trending/fl/fluent-bit
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考