如何在Nginx Ingress Controller中通过Lua读取SignalR消息
可行性说明
WSS请求完全可以参照HTTP payload的读取逻辑实现SignalR双向消息捕获。Nginx Ingress Controller默认内置lua-nginx-module与lua-resty-websocket模块,无需额外编译安装组件,即可在不改动后端.NET SignalR服务的前提下,透明拦截WebSocket双向帧,和现有HTTP请求记录逻辑复用同一套存储/分析链路。
SignalR基于WebSocket传输的消息分为两类:文本帧承载JSON格式协议消息(含普通调用、心跳、协商消息),二进制帧承载MessagePack编码的协议消息,两类帧均可在Lua层读取原始内容,按需解码或直接记录。
前置配置
首先需要给对应Ingress开启WebSocket支持,适配SignalR长连接特性,在Ingress资源中添加以下注解:
annotations: nginx.ingress.kubernetes.io/websocket-services: "你的signalr后端服务名" nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
核心Lua实现代码
通过Nginx Ingress的配置片段注解注入Lua逻辑,实现透明代理+双向帧捕获,逻辑不会中断原有WebSocket连接,仅复制帧内容做记录:
# 以下内容通过 nginx.ingress.kubernetes.io/location-snippet 注解注入到对应SignalR路由的location块中 location = /chatHub { # 替换为实际的SignalR Hub路由路径 # WebSocket代理基础配置 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_pass http://$proxy_upstream_name; # WebSocket帧拦截Lua逻辑 content_by_lua_block { local server = require "resty.websocket.server" local client = require "resty.websocket.client" local cjson = require "cjson.safe" -- 建立和前端客户端的WebSocket连接 local wb_server, err = server:new{ timeout = 60000, -- 帧读取超时,适配SignalR默认心跳间隔 max_payload_len = 1024 * 1024 -- 单帧最大体积,根据业务消息大小调整 } if not wb_server then ngx.log(ngx.ERR, "WebSocket握手失败: ", err) return ngx.exit(444) end -- 建立和后端.NET SignalR服务的WebSocket连接 local wb_client, err = client:new() local backend_uri = "ws://" .. ngx.var.upstream_addr .. ngx.var.request_uri local ok, err = wb_client:connect(backend_uri, { timeout = 60000, max_payload_len = 1024 * 1024 }) if not ok then ngx.log(ngx.ERR, "连接后端SignalR服务失败: ", err) wb_server:send_close(1011, "后端连接失败") return end -- 消息记录公共方法,可直接对接原有HTTP payload的记录逻辑 local function log_msg(direction, data, data_type) local log_entry = { trace_id = ngx.var.request_id, direction = direction, -- client_to_server / server_to_client data_type = data_type, -- text / binary / ping / pong / close payload = data_type == "binary" and ngx.encode_base64(data) or data, client_ip = ngx.var.remote_addr, timestamp = ngx.time() } -- 异步写日志,禁止阻塞转发流程 ngx.log(ngx.INFO, "SignalR消息: ", cjson.encode(log_entry)) end -- 协程1:读取客户端发往服务端的消息,记录后转发给后端 local function client_to_server() while true do local data, typ, err = wb_server:recv_frame() if not data then wb_client:send_close(1000, "客户端断开") return end if typ == "close" then log_msg("client_to_server", data, typ) wb_client:send_close(1000, data) return end if typ == "ping" then wb_client:send_ping(data) log_msg("client_to_server", data, typ) elseif typ == "pong" then wb_client:send_pong(data) log_msg("client_to_server", data, typ) elseif typ == "text" then wb_client:send_text(data) log_msg("client_to_server", data, typ) elseif typ == "binary" then wb_client:send_binary(data) log_msg("client_to_server", data, typ) end end end -- 协程2:读取服务端返回给客户端的消息,记录后转发给前端 local function server_to_client() while true do local data, typ, err = wb_client:recv_frame() if not data then wb_server:send_close(1011, "后端断开") return end if typ == "close" then log_msg("server_to_client", data, typ) wb_server:send_close(1000, data) return end if typ == "ping" then wb_server:send_ping(data) log_msg("server_to_client", data, typ) elseif typ == "pong" then wb_server:send_pong(data) log_msg("server_to_client", data, typ) elseif typ == "text" then wb_server:send_text(data) log_msg("server_to_client", data, typ) elseif typ == "binary" then wb_server:send_binary(data) log_msg("server_to_client", data, typ) end end end -- 启动双向转发协程 local co1 = ngx.thread.spawn(client_to_server) local co2 = ngx.thread.spawn(server_to_client) -- 任意一端连接断开即清理资源 ngx.thread.wait(co1, co2) wb_client:close() wb_server:close() } }
注意事项
- 上述逻辑为透明代理,不会修改原始WebSocket帧内容,不影响SignalR的正常协商、心跳、消息收发流程
- 若使用MessagePack二进制协议,示例中默认将二进制内容转Base64记录,如需可读格式可引入Lua端MessagePack解码库解析后再记录
- 消息记录逻辑建议使用异步写入方式,不要直接加磁盘IO、外部接口同步调用逻辑,避免阻塞WebSocket长连接
- 单帧最大长度配置需要匹配业务实际的SignalR消息大小,避免大消息被截断
内容的提问来源于stack exchange,提问作者Manish Ballav
相关产品推荐
相关产品推荐

