You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

移除ChannelPipeline中的HttpObjectAggregator后Netty Server无响应原因

问题:Netty HTTP服务器中HttpObjectAggregator为何是必需的?

我正在使用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,你需要修改处理器,让它能处理HttpRequestHttpContent类型的消息,自己手动维护请求的拼接状态(比如先保存HttpRequest,再逐步累加HttpContent,直到收到LastHttpContent再处理完整请求)。但这种方式会增加代码复杂度,所以对于绝大多数场景,使用HttpObjectAggregator是更简洁、高效的选择。


内容的提问来源于stack exchange,提问作者user51

火山引擎 最新活动