Jekit 的一次访问上报,Body 长度为 10 字节。
这种紧凑布局降低了每次上报的请求体积,也减少了后端处理数据时需要执行的操作。固定布局让枚举沿着底层的紧凑表示方式直接进入服务器。
这 10 字节的组成、页面哈希和访客识别规则,上一篇文章已经讲过。本篇接着向后看:这些字节进入服务器之后,会经过一条怎样的处理链路。
一、为什么选择定长二进制协议?
JSON 当然好用。字段名清楚、抓包能读、调试也方便。数据结构经常变化时,这些优点非常重要。
Jekit 的访问上报则有另一组特点:数据很小,类型固定,调用频繁。
如果使用常见的 JSON 表达方式,上报的浏览器、操作系统或来源等维度通常以文本字符串传递(如 "chrome"、"windows")。后端需要识别字段名、解析字符串,再将这些字符串匹配或转换为内部枚举。
这些工作换来了灵活性,也增加了高频上报时的数据处理步骤。
固定二进制协议既让有效数据占据主要空间,也让各种维度在端侧就完成离散化,作为紧凑的枚举直接存在。服务器收到以后,无需再对这些维度进行字符串匹配,就可以更快地交给业务。
对这条结构稳定的链路来说,Jekit 选择了更紧凑、处理步骤也更直接的表达方式。
二、路径匹配:直接复用 TCP 接收缓冲区
Jekit 基于 TCP 自行处理请求,不需要先将收到的数据转换为完整的 HTTP 请求对象。
数据进入接收缓冲区后,后端只识别当前业务关心的内容。对于 /greet,只需要定位请求路径,并与预设的路由进行匹配,无需解析与业务无关的字段。
解析器找到路径的起点和长度后,可以直接拿这段字节视图与固定路由比较。无需为路径额外构造字符串,也不必复制路径内容,就能完成 /greet 的路径确认。
虽然这件事听起来很小,但随着 /greet 在每次页面访问时被调用,高频路径上的小动作,其耗时也会被请求量一遍遍放大。
直接复用 TCP 接收缓冲区,让路由匹配始终停留在已有字节上的轻量操作中。
三、结构体映射:从字节流到内存视图
路由匹配以后,后端定位请求体,并检查其长度。
请求体通过精确的 10 字节长度校验后,随即进入结构转换。
JSON 解析器需要提取字段值并转换为对应的业务类型;Jekit 的固定协议则已经约定好数据布局。
Jekit 按照预先约定的内存布局,直接将 TCP 接收缓冲区中的请求体映射为协议结构体视图,无需重新分配内存或逐字段构造新的结构体。
字段经过协议有效性校验后,业务代码即可直接读取底层表示,并将对应的枚举交给业务处理。
这里实现了从字节流到结构化内存视图的原地转换。紧凑布局确定了协议形式,配合明确的字段偏移、数据类型和对齐要求,让每次结构访问都有确定的内存布局依据。
传输结构的大小还会在编译时检查。编译期尺寸约束把协议长度变成构建条件,减少结构调整时意外改变传输格式的风险。
结合字段布局与有效值校验,前后端可以按照同一份协议约定完成数据编码和读取。
四、零拷贝解码:减少内存复制与分配
把这条路径连起来看,Jekit 需要执行的操作很少:在 TCP 接收缓冲区中定位请求路径,匹配业务关心的内容,定位请求体,校验长度,然后就地读取协议结构。
整个过程依托已有的接收缓冲区与只读内存切片完成。
从请求内容识别到协议解码,Jekit 直接复用 TCP 接收缓冲区中的原始字节,无需复制载荷或为解析结果额外分配堆内存,实现了这一处理阶段的零拷贝(Zero-Copy)与零额外堆分配(Zero Additional Heap Allocation)。
响应也沿用同一个思路。后端准备好紧凑、连续的结果,再将其追加到 TCP 发送缓冲区中,让状态码与枚举保持原本的紧凑表示方式,减少额外的文本编码与数据转换。
五、CPU 执行效率:固定布局的性能优势
固定协议给 CPU 一份确定的工作清单:长度固定,布局固定,读取位置也固定。
解析 JSON 时,字段名长度、动态字符串长度和空格位置都可能改变解析过程。解析器需要根据实际输入识别字段边界,再提取对应的值。
固定协议则将这些工作提前落实到编码约定中。服务器只需要判断长度,并按照已知偏移读取对应字段,再完成必要的有效性检查。
固定协议不必重复扫描字段名,也不需要将文本字符串转换为内部枚举。对于 Jekit 这种字段固定的小型载荷,解码过程可以缩短为少量长度判断、内存读取与字段校验操作。
相比需要完整解析文本并转换业务类型的 JSON 实现,这条路径减少了不必要的处理步骤。
数据本身也足够紧凑。固定字段集中在连续的 10 字节区域内,读取一个值时,接下来要用的值通常就在相邻位置。
这种布局具有良好的 CPU 缓存局部性,也让热路径上的内存访问和执行步骤更加稳定。
六、底层高效,上层简单:Schema 与类型安全
Jekit 既让后端直接处理固定布局的数据,也让前端继续操作熟悉的类型和对象。
这些细节被收进了 jekit-core。
协议 Schema 同时约束运行时布局和 TypeScript 类型,将字段定义、枚举取值和固定长度纳入开发阶段的检查。编码时处理的是普通对象,得到的则是最终要发送的二进制数据。
对 React、Vue 和 CDN 用户来说,公开接口仍然是熟悉的调用方式。二进制协议留在 core 内部,前端使用体验保持简单。
前端 Schema 与后端协议定义共同约束数据类型和内存布局,让编码与解码遵循一致的通信约定。
这让 Jekit 在前后端之间建立了明确的协议约束:底层足够贴近机器,上层仍然保持现代 TypeScript 库的使用体验。
七、10 字节背后:更短的传输与处理链路
JSON 擅长表达灵活、多变的数据,固定二进制协议则擅长处理结构稳定的高频数据。两者各有合适的位置。
Jekit 的访问上报结构稳定、数据很小,而且发生得足够频繁,恰好适合后者。
这 10 字节既缩短了请求体的传输长度,也缩短了数据进入业务的距离:请求路径直接从 TCP 接收缓冲区中匹配,请求体按照固定布局就地读取,各项指标以紧凑的枚举表示,请求识别与协议解码阶段也无需额外复制载荷或构造中间对象。
从 TCP 接收缓冲区到业务计数,Jekit 只处理当前业务真正需要的内容。路径匹配、定长校验与原地结构体读取沿着同一块缓冲区依次完成,让数据在进入业务之前尽可能少地经历额外处理。
从请求字节到计数器,中间越短越好。
这就是 Jekit 选择二进制协议的原因。
