如何使用Netty

时间:2016-10-15 15:17:48

标签: java netty

我正在尝试创建一个简单的异步Netty HTTP服务器。我需要支持的关键特性是能够将事件中包含的HttpRequest发送到事件驱动的组件(在同一个JVM中),该组件将处理事件并生成响应。此事件驱动的组件在与Netty组件的单独线程中运行。

我已经按照一些例子来构建适合我的东西,以及当我在与处理程序相同的线程中生成响应时我的工作原理

我编写/改编的自定义处理程序是:

    private class WebServerHandler extends SimpleChannelInboundHandler<Object> {
        String uri;
        @Override
        public void messageReceived(final ChannelHandlerContext ctx, final Object msg) {
            if (!(msg instanceof FullHttpRequest)) return;

            final FullHttpRequest request = (FullHttpRequest) msg;

            uri = request.uri();
            System.out.println("Handling: " + uri);
            if (HttpUtil.is100ContinueExpected(request)) send100Continue(ctx);

            final Handler handler = WebServer.this.getHandler(request.uri());
            if (handler == null) {
                writeNotFound(ctx, request);
            } else {
                try {
                    handler.handle(ctx, request);
                } catch (final Throwable ex) {
                    ex.printStackTrace();
                    writeInternalServerError(ctx, request);
                }
            }
        }

        @Override
        public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) {
            ctx.close();
        }

        @Override
        public void channelReadComplete(final ChannelHandlerContext ctx) {
            System.out.println("Completed: " + uri);
//          ctx.flush();
        }
    }

当我将事件发送到事件驱动的组件(它再次在单独的线程中运行)时,会出现问题。在这种情况下,在事件驱动的组件处理事件之前调用上述channelReadComplete(...)方法。在我用来测试解决方案的Web浏览器中,连接永远不会终止。

我正在使用的管道是:

    final ChannelPipeline p = ch.pipeline();
    p.addLast("decoder", new HttpRequestDecoder(4096, 8192, 8192, false));
    p.addLast("aggregator", new HttpObjectAggregator(100 * 1024 * 1024));
    p.addLast("encoder", new HttpResponseEncoder());
    p.addLast("handler", new WebServerHandler());

我没有包含事件驱动组件的任何信息,因为它是一个庞大而复杂的系统(它实际上是一种编程语言),但我知道相关代码正在被成功调用。我试图执行以生成响应的实际代码是:

File file = new File(file_uri);
System.out.println("file: " + file.getAbsolutePath());
RandomAccessFile raf = new RandomAccessFile(file, "r");
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
HttpUtil.setContentLength(response, raf.length());

ctx.write(response); 
ctx.write(new DefaultFileRegion(raf.getChannel(), 0, file.length())); 
ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);

raf.close();

所以,我的问题是 - 在我发送响应之前,如何阻止Netty调用channelReadComplete(...)方法?

1 个答案:

答案 0 :(得分:1)

我认为你的raf.close调用应该在一个附加到你文件写入的监听器中。正如所写,我希望你在写电话有机会阅读之前关闭文件。您的响应正确设置了内容长度,但如果您不写那么多字节,因为您提前关闭文件,那么您的客户端将等待其余的。

您应该在管道中的聚合器之前移动编码器。聚合器可以发送编码器需要编码的响应。请查看使用HttpServerCodec,因为它可以用一个处理程序替换您的编码器/解码器。

您使用Object作为类型扩展 SimpleChannelInboundHandler ,但随后拒绝除 FullHttpRequest 消息之外的任何内容。如果您更改了扩展中的类型,那么the parent class will take care of that check and casting for you(您使用的是Netty 5吗?为什么?)。

示例:

... extends SimpleChannelInboundHandler<FullHttpRequest> {
...
public void messageReceived(ChannelHandlerContext ctx, FullHttpRequest request) {

我不认为channelReadComplete被称为早期事务。由于您已经在读取/响应处理程序中刷新,因此channelReadComplete中的刷新是不必要的。