如何基于TCP首包数据实现Nginx TCP连接路由转发
Nginx 基于TCP首包内容实现路由转发的实现方案
原生Nginx的stream模块默认不支持基于首包payload内容的路由,你可以通过以下两种可行路径实现需求:
方案一:使用ngx_stream_lua_module模块实现(推荐)
这是落地成本最低的定制方案,具体逻辑如下:
- 编译Nginx时加入
ngx_stream_lua_module模块即可,也可以直接使用内置了该模块的OpenResty发行版,省掉单独编译的步骤 - 在stream配置块的
preread_by_lua_block阶段读取首包内容,匹配规则后转发到对应上游即可 - 示例配置片段如下:
stream { upstream server1 { server 192.168.1.10:1234; # 替换为server1的实际地址 } upstream server2 { server 192.168.1.20:1234; # 替换为server2的实际地址 } server { listen 8888; # 替换为实际监听端口 preread_by_lua_block { -- 读取首包前2字节,可根据你的实际标识长度调整 local sock, err = ngx.req.socket() if not sock then ngx.exit(ngx.ERROR) end local data, err = sock:receive(2) if data == "A1" then ngx.var.upstream = "server1" elseif data == "A2" then ngx.var.upstream = "server2" else -- 不匹配规则可走默认上游或者直接关闭连接 ngx.exit(ngx.ERROR) end } proxy_pass $upstream; proxy_request_buffering on; # 开启preread缓冲,确保首包数据会同步转发到上游 } }
- 如果首包标识是变长格式,也可以调整
receive的参数,比如按分隔符读取receive("\n")来获取完整的标识位再做匹配。
方案二:自研Nginx stream扩展模块
如果不想引入Lua依赖,可以自行开发Nginx stream模块,在preread阶段读取TCP payload,匹配规则后设置对应上游地址即可,开发成本比Lua方案高,适合对性能要求极高、不愿意引入Lua虚拟机的场景。
踩坑提示
- TCP是流式协议,需要确认你的业务首包标识一定会在第一个TCP段里发送,不会出现粘包拆包导致首包标识被拆分的情况;如果存在拆包可能,需要在逻辑里加缓冲拼接完整的标识位再做匹配。
- 不要在content阶段读取数据后再转发,会导致首包数据被消费没有传递给上游,preread阶段是最合适的处理位置,该阶段缓冲的数据会自动转发给上游。
内容的提问来源于stack exchange,提问作者perekinsun
相关产品推荐
相关产品推荐

