Netty的HttpObjectAggregator似乎错过了HTTP块

时间:2015-06-29 23:50:47

标签: java json http netty chunked-encoding

我正在使用Java中的Netty框架处理异步HTTP客户端,并且遇到了与分块编码相关的一些麻烦。客户端连接到REST服务,该服务发出JSON响应,可通过长轮询进行访问。服务器使用分块编码进行响应,因此我在处理每个响应之前使用Netty的HttpObjectAggregator来重新组装块。我遇到的问题是,对于大约1/2的长轮询请求,我的HTTP处理程序只获得部分JSON响应。通常一次或两次发出相同的请求会导致提供完整的请求。

我采取了解决问题的步骤:

  1. 使用HttpContentDecompressor
  2. Netty版本5.0.0.Alpha2,4.1.0.Beta5,4.0.29.Final
  3. 通过给予充足的空间来保留回复,删除了HttpContentDecompressor不够“大”的可能性
  4. 我不知道的事情

    1. 如果Netty真的是问题:这可能只是一个糟糕的网络服务,但它是用SSL加密的,我不知道如何在汇编之前记录Netty的原始响应

    2. 为什么只有部分请求会发生这种情况。通常,对同一请求重试一次或两次可以解决问题

    3. 我的目标:将这些块可靠地组装成一个整体。

      我真的很感激有任何关于调试这个的建议!

      编辑:正如Bayou.io指出的那样,我有分块编码重组的顺序和gzip通胀混淆了。但是,我没有使用gzip编码也尝试了这个并且发生了同样的错误。

      部分代码:

      这是我配置HTTP客户端的地方

      /**
       * Establishes a connection, or throws an exception if one cannot be made
       * @throws Exception If there is a problem connecting to {@link #mUri}
       */
      private void connect() throws Exception {
          mGroup = new NioEventLoopGroup();
          Bootstrap b = new Bootstrap();
          b.group(mGroup)
                  .channel(NioSocketChannel.class)
                  .handler(new ChannelInitializer<SocketChannel>() {
      
                      @Override
                      public void initChannel(SocketChannel ch) {
                          /* all channel IO goes through the pipeline */
                          ChannelPipeline p = ch.pipeline();
      
                          /* handles read timeout */
                          p.addLast(new ReadTimeoutHandler(mTimeout));
      
                          /* handles SSL handshake if needed */
                          if (mUri.getScheme().equalsIgnoreCase("https"))
                              p.addLast(sslContext.newHandler(ch.alloc(), mUri.getHost(), mUri.getPort()));
      
                          /* converts to HTTP response */
                          p.addLast(new HttpClientCodec());
      
                          /* decompress GZIP if needed */
                          p.addLast(new HttpContentDecompressor());
      
                          /* aggregates chunked responses */
                          p.addLast(new HttpObjectAggregator(Integer.MAX_VALUE));
      
                          /* handles response for child class */
                          configureCustomPipelines(p, mCallback);
                      }
      
                  });
      
          mChannel = b.connect(mUri.getHost(), mUri.getPort()).sync().channel();
      }
      

      configureCustomPipelines中配置的处理程序是以下类(省略了不必要的详细信息):

      public abstract class BaseHttpHandler extends SimpleChannelInboundHandler<HttpObject> {
          ...    
          /**
           * Processes the response and ensures that the correct callback is invoked,
           * and then that the HttpClient is shutdown
           */
          @Override
          public synchronized void messageReceived(ChannelHandlerContext ctx, HttpObject msg) {
              if (!mHandled) {
                  if (msg instanceof FullHttpResponse) {
                      HttpResponse response = (FullHttpResponse) msg;
                      int status = response.status().code();
      
                      if (status < 200 || status > 299) {
                          handleBadResponse(response, status);
                          mHandled = true;
                      } else {
                          HttpContent content = (HttpContent) msg;
                          String body = content.content().toString(
                                  0,
                                  content.content().writerIndex(),
                                  CharsetUtil.UTF_8);
      
                          if (body.length() > 0) {
                              handleMessageBody(body, status);
                              mHandled = true;
                          }
                      }
                  }
              }
      
              if (mHandled)
                  shutdown(ctx);
          }
      
          @Override
          public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
              shutdown(ctx);
          }
      
          private void shutdown(ChannelHandlerContext ctx) {
              ctx.channel().close();
              ctx.channel().eventLoop().shutdownGracefully();
          }
      }
      

      我知道响应被缩短了,因为下面的处理函数无法解析JSON主体。经过进一步检查,似乎JSON字符串突然结束:

      if (body.length() > 0) {
              handleMessageBody(body, status);
              mHandled = true;
      }
      

1 个答案:

答案 0 :(得分:1)

我能够确定Netty不是问题所在。事实证明,HTTP服务器定期无法发送一些响应。我能够使用管道中的自定义流转储处理程序来确定这一点,该处理程序将Netty接收的(post-ssl解密)内容写入文件。手动检查后,问题很明显。