尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Fluent Bit 内嵌 nghttp2:nghttp2_session_mem_recv 内存接收 API 深度解析

Fluent Bit 内嵌 nghttp2:nghttp2_session_mem_recv 内存接收 API 深度解析 Fluent Bit 内嵌 nghttp2nghttp2_session_mem_recv 内存接收 API 深度解析【免费下载链接】fluent-bitFast and Lightweight Logs, Metrics and Traces processor for Linux, BSD, OSX and Windows项目地址: https://gitcode.com/GitHub_Trending/fl/fluent-bit本篇技术指南围绕 nghttp2 库中nghttp2_session_mem_recv()这一 HTTP/2 内存接收 API 展开它如何脱离网络回调、直接从调用者提供的内存缓冲区消费 HTTP/2 字节流其返回语义、暂停PAUSE行为与错误码约定是什么。读完本文你将能够理解该 API 在 Fluent Bit HTTP/2 客户端src/flb_http_client_http2.c中的真实调用方式并能对照 nghttp2_session.c 的帧状态机实现读懂一次接收调用的完整处理流程。API 概览与原型nghttp2_session_mem_recv()的官方文档见 nghttp2_session_mem_recv.rst其原型在 nghttp2.h 中声明#include nghttp2/nghttp2.h ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, size_t inlen);要点如下参数in指向远端端点发来的原始字节流inlen是in中待接收的字节数。核心语义该函数的行为类似nghttp2_session_recv()但不使用nghttp2_recv_callback从网络读取数据in缓冲区就是本次调用的唯一数据源。当所有字节被处理完函数即返回。其余回调照常触发与nghttp2_session_recv()相同nghttp2_on_header_callback、nghttp2_on_data_chunk_recv_callback等回调在接收过程中按同样方式被调用。官方弃用提示文档明确标注 Deprecated. Usenghttp2_session_mem_recv2()instead.详见 nghttp2_session_mem_recv2.rst。在 1.65.0 的实现中旧函数只是一层薄封装/* lib/nghttp2-1.65.0/lib/nghttp2_session.c */ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, size_t inlen) { return (ssize_t)nghttp2_session_mem_recv2(session, in, inlen); }见 nghttp2_session.c#L5292-L5295。也就是说两者行为完全一致mem_recv2只是返回类型换为nghttp2_ssize同时注意该原型被#ifndef NGHTTP2_NO_SSIZE_T宏保护nghttp2.h#L3601即在缺少ssize_t的平台上此 API 不可用。返回值与错误码约定文档对返回值给出了明确约定这是集成该 API 时必须处理的边界返回值含义inlen或实际处理的字节数成功返回值是本次已处理的字节数NGHTTP2_ERR_NOMEM内存不足NGHTTP2_ERR_CALLBACK_FAILURE回调函数失败NGHTTP2_ERR_BAD_CLIENT_MAGIC检测到非法的 client magic。仅当session配置为服务端且未用非零值调用nghttp2_option_set_no_recv_client_magic()见 nghttp2_option_set_no_recv_client_magic.rst时才会返回NGHTTP2_ERR_FLOODED会话检测到洪泛flooding必须关闭连接通常由对端行为异常导致暂停语义NGHTTP2_ERR_PAUSE文档特别强调了当前实现的关键行为该函数总是尝试处理完全部inlen字节除非发生错误或nghttp2_on_header_callback/nghttp2_on_data_chunk_recv_callback返回NGHTTP2_ERR_PAUSE。若使用 PAUSE则返回值包含用于产生该回调所对应数据或帧的字节数——即处理在哪个字节处暂停就返回已消耗的字节数。从源码结构看这一点体现在 nghttp2_session.c 中多处return (nghttp2_ssize)(in - first)的写法状态机每消费一部分输入就推进指针in遇到需要等待缓冲或回调暂停的场景时返回in - first已消费长度而非inlen。调用方拿到小于inlen的返回值后应从in 返回值处继续投递剩余字节。NGHTTP2_ERR_PAUSE的语义及配合nghttp2_session_resume_data()的用法在 nghttp2.h 的回调注释中有进一步描述。BAD_CLIENT_MAGIC 的实现位置文档说NGHTTP2_ERR_BAD_CLIENT_MAGIC在 client magic 校验失败时返回对应源码位于接收状态机的初始状态case NGHTTP2_IB_READ_CLIENT_MAGIC: readlen nghttp2_min_size(inlen, iframe-payloadleft); if (memcmp(NGHTTP2_CLIENT_MAGIC[NGHTTP2_CLIENT_MAGIC_LEN - iframe-payloadleft], in, readlen) ! 0) { return NGHTTP2_ERR_BAD_CLIENT_MAGIC; }见 nghttp2_session.c#L5329-L5336。注意它是逐字节增量比对的若本次in只送来 magicPRI * HTTP/2.0\r\n\r\nSM\r\n的前几个字节会继续等下一批数据只有比对不一致才直接返回错误。接收状态机一次调用的内部处理流程nghttp2_session_mem_recv()的核心实现在 nghttp2_session_mem_recv2nghttp2_session.c#L5297本质上是一个围绕session-iframenghttp2_inbound_frame的帧解析状态机。结合源码可以梳理出如下流程入口检查若session当前不需要读!nghttp2_session_want_read(session)直接返回inlen即不消费也不处理——调用方应据此判断无需继续投递。NGHTTP2_IB_READ_CLIENT_MAGIC服务端模式下按上文逐字节校验 client magic全部匹配后进入下一状态。NGHTTP2_IB_READ_FIRST_SETTINGS要求连接上的第一个帧必须是 SETTINGS 且非 ACKnghttp2_session.c#L5357-L5363。违反时通过 error callback 上报 Remote peer returned unexpected data while we expected SETTINGS frame并以NGHTTP2_PROTOCOL_ERROR终止会话。NGHTTP2_IB_READ_HEAD从缓冲区读满 9 字节帧头调用nghttp2_frame_unpack_frame_hd()解出长度、类型、标志与流 ID并检查是否超过对端协商的max_frame_size超过则以NGHTTP2_FRAME_SIZE_ERROR终止见 nghttp2_session.c#L5401-L5413。按帧类型分派NGHTTP2_DATA先经session_on_data_received_fail_fast()做窗口/流状态快检随后按PADDED标志进入NGHTTP2_IB_READ_PAD_DATA或NGHTTP2_IB_READ_DATANGHTTP2_HEADERS处理 pad 与优先级字段后触发on_begin_frame并调用session_process_headers_frame()——这正是文档所述其余回调以与nghttp2_session_recv()相同方式被调用的落点若on_header_callback返回 PAUSE此处即暂停并返回已消费字节数NGHTTP2_PRIORITY、NGHTTP2_RST_STREAM、NGHTTP2_WINDOW_UPDATE等校验负载长度长度不符则进入NGHTTP2_IB_FRAME_SIZE_ERROR以协议错误处理。跨调用续传状态机的中间状态如已读半个帧头保存在iframe中因此一次mem_recv调用不必收到完整帧后续调用可从断点继续。文档中If all bytes are processed, this function returns与返回值包含已处理字节数两条约定正是为这种分块投递设计的。这一状态机也解释了NGHTTP2_ERR_FLOODED的来源nghttp2 内部用 flood 检测机制见 lib/nghttp2_ratelim.c 相关的限速逻辑跟踪回调处理与数据投递的比例若应用处理过慢导致缓冲区长期积压接收路径会判定洪泛并要求调用方关闭会话。Fluent Bit 中的真实用法Fluent Bit 的 HTTP/2 客户端是理解该 API 的典型场景HTTP 数据通过自有的 I/O 层flb_io读入内存后需要把整块缓冲区喂给 nghttp2 做帧解析这正是mem_recv而非session_recv的适用场合。在 src/flb_http_client_http2.c 中可以看到完整的调用模式/* src/flb_http_client_http2.c */ result nghttp2_session_mem_recv(session-inner_session, buffer, length); if (result 0) { /* 已消费 result 字节 */ } result nghttp2_session_send(session-inner_session);见 flb_http_client_http2.c#L553-L559。围绕这次mem_recvFluent Bit 做了三件配套工作回调装配在会话创建时通过nghttp2_session_callbacks_new() 一系列nghttp2_session_callbacks_set_*()注册了send、on_frame_recv、on_stream_close、on_begin_headers、on_data_chunk_recv、on_header等回调flb_http_client_http2.c#L480-L499。由于mem_recv不使用 recv 回调on_data_chunk_recv/on_header就是响应数据到达应用层的唯一通道。收发配对每次mem_recv之后紧跟一次nghttp2_session_send()如 L553-L559 与 L794 附近把接收过程中 nghttp2 排队的出站帧WINDOW_UPDATE、RST_STREAM、ACK 等尽快发出去这是使用 mem_recv 家族 API 时容易遗漏的一步。返回值判断以返回值是否大于 0 区分已消费字节数与错误码符合文档的返回约定。nghttp2 官方示例 examples/libevent-client.c 展示了同样的模式libevent 读事件回调里把read()得到的数据交给nghttp2_session_mem_recv()再驱动nghttp2_session_send()。测试覆盖nghttp2 自身的测试套件对该 API 有直接覆盖tests/nghttp2_session_test.c 中包含多组mem_recv相关的用例覆盖正常帧序列接收、client magic 校验、洪泛检测等场景。需要说明的是这些用例针对的是mem_recv2路径mem_recv作为其封装行为等价。若在自己的项目中集成此 API可以以这些测试为参照重点验证不完整帧的跨调用续传、on_data_chunk_recv返回 PAUSE 后返回值是否恰好等于已消费字节数、以及NGHTTP2_ERR_BAD_CLIENT_MAGIC服务端模式与NGHTTP2_ERR_FLOODED两条错误路径。使用建议与小结综合文档约定与源码实现集成nghttp2_session_mem_recv()时的要点可以归纳为返回值是已消费字节数而非成功标志小于inlen的返回值是合法且常见的暂停、缓冲未就绪应从偏移处继续投递剩余字节只有负值才是文档所列的错误码。会话处于不想读状态时直接返回inlen从 nghttp2_session.c#L5323-L5325 的入口短路可见此时数据未被处理调用方需要自行决定是丢弃还是暂存。新代码建议直接使用nghttp2_session_mem_recv2()官方文档已声明mem_recv弃用二者在本仓库版本中是同一实现的两种返回类型nghttp2_session.c#L5292-L5295。收完必发仿照 src/flb_http_client_http2.c 的mem_recv→session_send配对保证流控与错误帧及时送达对端。服务端模式的 magic 校验默认校验 client magic可用nghttp2_option_set_no_recv_client_magic()关闭见 nghttp2_option_set_no_recv_client_magic.rst检测失败返回NGHTTP2_ERR_BAD_CLIENT_MAGIC。把NGHTTP2_ERR_FLOODED视为致命文档明确要求此时关闭会话它几乎总是对端或本端处理速度异常的信号继续使用该会话没有意义。对维护 HTTP/2 相关功能的开发者而言理解mem_recv这一内存投递入口与session_recv这一网络回调入口的分工是读懂 nghttp2 接收路径的关键前者把字节流的获取完全交给应用如 Fluent Bit 的 I/O 层后者把获取内建在库中两者共用同一套iframe状态机与回调体系因此帧解析、流控与 PAUSE 语义完全一致。【免费下载链接】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),仅供参考
返回列表