移除ChannelPipeline中的HttpObjectAggregator后Netty Server无响应原因
我正在使用Netty 4.1.16.Final搭建一个简单的HTTP服务器,服务器代码如下:
EventLoopGroup masterGroup = new NioEventLoopGroup(); EventLoopGroup slaveGroup = new NioEventLoopGroup(); final ServerBootstrap bootstrap = new ServerBootstrap() .group(masterGroup, slaveGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) { ch.pipeline().addLast("codec", new HttpServerCodec()); ch.pipeline().addLast("aggregator", new HttpObjectAggregator(512 * 1024)); ch.pipeline().addLast("request", new HTTPSimpleChannelInboundHandler()); } }).option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); channel = bootstrap.bind(8080).sync();
HTTP处理器类HTTPSimpleChannelInboundHandler的代码如下:
public class HTTPSimpleChannelInboundHandler extends SimpleChannelInboundHandler<FullHttpRequest> { protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) { HttpResponseStatus responseStatus = OK; FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, responseStatus, Unpooled.copiedBuffer("My Netty".getBytes())); response.headers().add(request.headers()); response.headers().set(CONTENT_LENGTH, response.content().readableBytes()); if (isKeepAlive(request)) { response.headers().set(CONNECTION, HttpHeaderValues.KEEP_ALIVE); } if (is100ContinueExpected(request)) { ctx.write(new DefaultFullHttpResponse(HTTP_1_1, CONTINUE)); } ctx.writeAndFlush(response); } public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); } }
上述代码运行正常,但当我注释掉服务器代码中的ch.pipeline().addLast("aggregator", new HttpObjectAggregator(512 * 1024));这一行后,服务器不再返回响应,服务器日志如下:
01:37:14.806 [nioEventLoopGroup-3-2] DEBUG io.netty.channel.DefaultChannelPipeline - Discarded inbound message DefaultHttpRequest(decodeResult: success, version: HTTP/1.1) GET /test HTTP/1.1 Host: localhost:5055 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:60.0) Gecko/20100101 Firefox/60.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Cookie: _ga=GA1.1.177481759.1523295602; Idea-46064427=2276f52b-2928-4410-8f4c-c7561bd33457 Connection: keep-alive Upgrade-Insecure-Requests: 1 that reached at the tail of the pipeline. Please check your pipeline configuration. 01:37:14.806 [nioEventLoopGroup-3-2] DEBUG io.netty.channel.DefaultChannelPipeline - Discarded inbound message EmptyLastHttpContent that reached at the tail of the pipeline. Please check your pipeline configuration.
请问为何HttpObjectAggregator在ChannelPipeline中是必需的?
这个问题的核心在于Netty处理HTTP请求的拆分与聚合逻辑,我来给你一步步拆解清楚:
1. HttpServerCodec会拆分HTTP请求
当你使用HttpServerCodec时,它不会直接输出完整的HTTP请求对象,而是将客户端发送的HTTP请求拆分成多个独立的消息片段:
- DefaultHttpRequest:包含请求行和所有请求头部信息
- HttpContent(可能有多个):如果请求带有body,会被拆分成多个内容块
- EmptyLastHttpContent:一个标记对象,用来表示整个HTTP请求已经发送完毕
这些片段会按顺序进入ChannelPipeline,传递给下一个处理器。
2. 你的处理器只接收FullHttpRequest
你的HTTPSimpleChannelInboundHandler继承自SimpleChannelInboundHandler<FullHttpRequest>,这意味着它只会处理FullHttpRequest类型的消息。而FullHttpRequest是一个封装了完整HTTP请求的对象(包含请求行、头部和完整body)——这个对象并不是由HttpServerCodec直接生成的。
3. HttpObjectAggregator的核心作用
HttpObjectAggregator的唯一职责就是把HttpServerCodec输出的零散消息片段(HttpRequest、HttpContent、LastHttpContent)聚合拼接成一个完整的FullHttpRequest对象,然后再传递给后面的处理器。只有经过这个聚合步骤,你的处理器才能接收到可以处理的消息,进而执行响应逻辑。
4. 去掉聚合器后的连锁反应
当你注释掉HttpObjectAggregator后:
HttpServerCodec输出的HttpRequest、EmptyLastHttpContent等消息进入Pipeline,但你的处理器不处理这些类型的消息- 这些未被消费的消息会一直走到Pipeline的尾部,没有任何处理器处理它们
- Netty会自动丢弃这些消息,并输出你看到的日志(提示消息到达Pipeline尾部未被处理)
- 你的处理器从未收到任何
FullHttpRequest,所以channelRead0方法根本不会执行,自然也就不会给客户端返回任何响应了
可选方案:不使用聚合器的情况
如果你确实不想使用HttpObjectAggregator,你需要修改处理器,让它能处理HttpRequest和HttpContent类型的消息,自己手动维护请求的拼接状态(比如先保存HttpRequest,再逐步累加HttpContent,直到收到LastHttpContent再处理完整请求)。但这种方式会增加代码复杂度,所以对于绝大多数场景,使用HttpObjectAggregator是更简洁、高效的选择。
内容的提问来源于stack exchange,提问作者user51




