用SimpleChannelInboundHandler<FullHttpRequest>实现HTTP转发,需复制关键字段后构造新请求并重写Host头;路由用预编译Pattern匹配路径前缀与变量;透传X-Forwarded-For/X-Real-IP等头;响应转发须正确处理HttpContent分片与buffer引用。

Netty里怎么写一个能转发HTTP请求的ChannelHandler
直接用 SimpleChannelInboundHandler<FullHttpRequest>,别用 ChannelInboundHandlerAdapter 手动解码——前者自动释放消息、处理完就丢掉请求体,后者容易内存泄漏。你得在 channelRead0 里拿到 FullHttpRequest 后立刻复制关键字段(比如 URI、method、headers),再构造新请求发出去;原对象不能跨线程复用,也不能留着等异步回调结束。
- 记得调用
request.retain()如果要异步转发,否则 Netty 可能在你发出去前就回收了缓冲区 -
DefaultFullHttpRequest构造时,第三个参数(content)要用Unpooled.copiedBuffer(),别传原始ByteBuf,避免引用被上游清空 - 转发前必须重写
Hostheader,否则后端可能 404 或路由错——比如把Host: api.example.com改成目标服务的真实域名或 IP+端口
怎么让路由规则支持路径前缀匹配和变量提取(类似 /api/users/{id})
别自己写正则去 match 每次请求——性能差还难维护。用现成的轻量级路径匹配器,比如 PathPatternParser(Spring 5.3+ 的,不依赖 Spring Web),或者更简单的:预编译 java.util.regex.Pattern,但只对静态前缀用 String.startsWith(),动态段走正则。关键点是「规则要预热」:启动时就把所有路由编译成 Pattern 对象缓存起来,运行时只做 matcher.matches(),别每次 new Pattern。
- 路径中
{id}这种变量,匹配后用matcher.group("id")提取,不是靠 split 或 substring - 注意路径末尾斜杠:/users 和 /users/ 是两个不同 pattern,建议统一处理(比如都转成不带尾部斜杠再匹配)
- 如果规则有优先级(比如 /api/admin/** 优先于 /api/**),就得按顺序遍历,不能扔进 HashMap —— Map 不保序
转发请求时怎么透传原始客户端IP和协议信息
后端服务经常靠 X-Forwarded-For 判断真实用户,靠 X-Forwarded-Proto 知道是不是 HTTPS。但 Netty 默认不加这些 header,你得手动塞。不过别无脑加——如果网关本身就在反向代理后面(比如 Nginx → Netty → Service),那 X-Forwarded-For 可能已存在,你要追加而不是覆盖;X-Real-IP 更可靠,它通常由最外层代理设置,可直接读 ctx.channel().remoteAddress() 取到。
- 获取客户端真实 IP:优先读
request.headers().get("X-Real-IP"),没有再 fallback 到ctx.channel().remoteAddress()的 host 部分 - 加
X-Forwarded-Proto: https的前提是你的网关监听的是 HTTPS 端口,或者 Nginx 传了这个 header;否则一律写http - 别忘了清理敏感 header:比如删掉
Proxy-Connection、Upgrade-Insecure-Requests,避免干扰后端
为什么转发响应后客户端收不到完整的 HTTP body
常见原因是没正确处理 HttpContent 分片。Netty 的 HTTP 解码默认会把大响应拆成多个 HttpContent(包括 LastHttpContent),而你只转发了第一个 FullHttpResponse,后面的 content 全丢了。解决办法只有一个:在转发逻辑里保持对 HttpObjectDecoder 的兼容性,即用 HttpServerCodec + ChunkedWriteHandler,并确保下游 channel 的 pipeline 也配了 HttpClientCodec。
立即学习“Java免费学习笔记(深入)”;
- 转发响应时,如果是
FullHttpResponse,直接 writeAndFlush;如果是HttpContent,检查是否为LastHttpContent,是的话要带上 trailing headers(如有) - 别用
response.copy()转发HttpContent,要用content.retainedDuplicate(),否则可能 double-release - 如果目标服务返回 chunked 编码,而你又用了
HttpObjectAggregator,记得调整聚合上限(new HttpObjectAggregator(10 * 1024 * 1024)),不然大文件会 OOM
事情说清了就结束。真正的难点不在转发动作本身,而在 header 的层层透传、buffer 生命周期的精确控制、还有路由匹配时对边界 case 的容忍度——这些地方一松手,问题就藏在流量高峰里冒出来。











