在Netty之上构建Future API

时间:2018-03-30 11:45:15

标签: java-8 netty api-design completable-future

我想构建一个基于Futures(来自java.util.concurrent)的API,该API由Netty(版本4)上的自定义协议提供支持。基本思想是编写一个简单的库,它将抽象底层的Netty实现并使其更容易发出请求。

使用这个库,应该能够编写如下内容:

Request req = new Request(...);
Future<Response> responseFuture = new ServerIFace(host, port).call(req);
// For example, let's block until this future is resolved
Reponse res = responseFuture.get().getResult();

在此代码下方,连接了Netty客户端     

public class ServerIFace {
    private Bootstrap bootstrap;
    private EventLoopGroup workerGroup;
    private String host;
    private int port;

    public ServerIFace(String host, int port) {
        this.host = host;
        this.port = port;
        this.workerGroup = new NioEventLoopGroup();

        bootstrap();
    }

    private void bootstrap() {
        bootstrap = new Bootstrap();
        bootstrap.group(workerGroup);
        bootstrap.channel(NioSocketChannel.class);
        bootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                ch.pipeline().addLast(new ObjectEncoder());
                ch.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(Response.class.getClassLoader())));
                ch.pipeline().addLast("response", new ResponseReceiverChannelHandler());
            }
        });
    }

    public Future<Response> call(final Request request) throws InterruptedException {
        CompletableFuture<Response> responseFuture = new CompletableFuture<>();
        Channel ch = bootstrap.connect(host, port).sync().channel();

        ch.writeAndFlush(request).addListener((f) -> {
            if (f.isSuccess()) {
                System.out.println("Wrote successfully");
            } else {
                f.cause().printStackTrace();
            }
        });

        ChannelFuture closeFuture = ch.closeFuture();

        // Have to 'convert' ChannelFuture to java.util.concurrent.Future
        closeFuture.addListener((f) -> {
            if (f.isSuccess()) {
                // How to get this response?
                Response response = ((ResponseReceiverChannelHandler) ch.pipeline().get("response")).getResponse();
                responseFuture.complete(response);
            } else {
                f.cause().printStackTrace();
                responseFuture.cancel(true);
            }
            ch.close();
        }).sync();
        return responseFuture;
    }
}

现在,正如您所看到的,为了抽象Netty的内部ChannelFuture,我必须转换&#39;它是Java的未来(我知道ChannelFuture来自Future,但是这些信息在这一点上看起来并不有用)。

现在,我在客户端管道的入站部分的最后一个处理程序ResponseReceiverChannelHandler中捕获此Response对象。

public class ResponseReceiverChannelHandler extends ChannelInboundHandlerAdapter {
    private Response response = null;

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        this.response = (Response)msg;
        ctx.close();
    }

    public Response getResponse() {
        return response;
    }
}

由于我是Netty的新手以及一般情况下的这些事情,我正在寻找一种更清晰,线程安全的方式将此对象提供给API用户。

如果我错了,请纠正我,但没有一个Netty示例显示如何实现这一点,并且大多数客户端示例只打印出从Server获得的任何内容。

请注意,我的主要目标是了解有关Netty的更多信息,并且此代码没有生产用途。

供参考(虽然我不认为它是相关的)这里是服务器代码。

public class Server {
    public static class RequestProcessorHandler extends ChannelInboundHandlerAdapter {
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            ChannelFuture future;
            if (msg instanceof Request) {
                Request req = (Request)msg;
                Response res = some function of req
                future = ctx.writeAndFlush(res);
            } else {
                future = ctx.writeAndFlush("Error, not a request!");
            }
            future.addListener((f) -> {
                if (f.isSuccess()) {
                    System.out.println("Response sent!");
                } else {
                    System.out.println("Response not sent!");
                    f.cause().printStackTrace();
                }
            });
        }
    }

    public int port;

    public Server(int port) {
        this.port = port;
    }

    public void run() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(Request.class.getClassLoader())));
                            ch.pipeline().addLast(new ObjectEncoder());
                            // Not really shutting down this threadpool but it's ok for now
                            ch.pipeline().addLast(new DefaultEventExecutorGroup(2), new RequestProcessorHandler());
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            ChannelFuture f = b.bind(port).sync();

            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        int port;
        if (args.length > 0) {
            port = Integer.parseInt(args[0]);
        } else {
            port = 8080;
        }
        new Server(port).run();
    }
}

0 个答案:

没有答案