Netty 從 Channel 引導(dǎo)客戶(hù)端

2018-08-08 10:45 更新

我們會(huì)碰到引導(dǎo)客戶(hù)端 Channel 從另一個(gè) Channel的情況。如果您正在編寫(xiě)一個(gè)代理或者要從其他系統(tǒng)需要檢索數(shù)據(jù)的時(shí)候,這可能發(fā)生。后一種情況是常見(jiàn)的,因?yàn)樵S多 Netty 的應(yīng)用程序集成現(xiàn)有系統(tǒng),例如 web 服務(wù)或數(shù)據(jù)庫(kù)。

你當(dāng)然可以創(chuàng)建一個(gè)新的 Bootstrap 并使用它如9.2.1節(jié)所述,這個(gè)解決方案不一定有效。至少,你需要?jiǎng)?chuàng)建另一個(gè) EventLoop 給新客戶(hù)端 Channel 的,并且 Channel 將會(huì)需要在不同的 Thread 間進(jìn)行上下文切換。

幸運(yùn)的是,由于 EventLoop 繼承自 EventLoopGroup ,您可以通過(guò)傳遞 接收到的 Channel 的 EventLoop 到 Bootstrap 的 group() 方法。這允許客戶(hù)端 Channel 來(lái)操作 相同的 EventLoop,這樣就能消除了額外的線程創(chuàng)建和所有相關(guān)的上下文切換的開(kāi)銷(xiāo)。

為什么共享 EventLoop 呢?

當(dāng)你分享一個(gè) EventLoop ,你保證所有 Channel 分配給 EventLoop 將使用相同的線程,消除上下文切換和相關(guān)的開(kāi)銷(xiāo)。(請(qǐng)記住,一個(gè)EventLoop分配給一個(gè)線程執(zhí)行操作。)

共享一個(gè) EventLoop 描述如下:

Figure%209

  1. 當(dāng) bind() 調(diào)用時(shí),ServerBootstrap 創(chuàng)建一個(gè)新的ServerChannel 。 當(dāng)綁定成功后,這個(gè)管道就能接收子管道了
  2. ServerChannel 接收新連接并且創(chuàng)建子管道來(lái)服務(wù)它們
  3. Channel 用于接收到的連接
  4. 管道自己創(chuàng)建了 Bootstrap,用于當(dāng) connect() 調(diào)用時(shí)創(chuàng)建一個(gè)新的管道
  5. 新管道連接到遠(yuǎn)端
  6. 在 EventLoop 接收通過(guò) connect() 創(chuàng)建后就在管道間共享

Figure 9.4 EventLoop shared between channels with ServerBootstrap and Bootstrap

實(shí)現(xiàn) EventLoop 共享,包括設(shè)置 EventLoop 引導(dǎo)通過(guò)Bootstrap.eventLoop() 方法。這是清單9.5所示。

ServerBootstrap bootstrap = new ServerBootstrap(); //1
bootstrap.group(new NioEventLoopGroup(), //2
    new NioEventLoopGroup()).channel(NioServerSocketChannel.class) //3
        .childHandler(        //4
            new SimpleChannelInboundHandler<ByteBuf>() {
            ChannelFuture connectFuture;

            @Override
            public void channelActive(ChannelHandlerContext ctx) throws Exception {
                Bootstrap bootstrap = new Bootstrap();//5
                bootstrap.channel(NioSocketChannel.class) //6
                        .handler(new SimpleChannelInboundHandler<ByteBuf>() {  //7
                            @Override
                            protected void channelRead0(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
                                System.out.println("Reveived data");
                            }
                        });
                bootstrap.group(ctx.channel().eventLoop()); //8
                connectFuture = bootstrap.connect(new InetSocketAddress("www.manning.com", 80));  //9
            }

            @Override
            protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
                if (connectFuture.isDone()) {
                    // do something with the data  //10
                }
            }
        });
ChannelFuture future = bootstrap.bind(new InetSocketAddress(8080));  //11
future.addListener(new ChannelFutureListener() {
    @Override
    public void operationComplete(ChannelFuture channelFuture) throws Exception {
        if (channelFuture.isSuccess()) {
            System.out.println("Server bound");
        } else {
            System.err.println("Bound attempt failed");
            channelFuture.cause().printStackTrace();
        }
    }
});
  1. 創(chuàng)建一個(gè)新的 ServerBootstrap 來(lái)創(chuàng)建新的 SocketChannel 管道并且綁定他們
  2. 指定 EventLoopGroups 從 ServerChannel 和接收到的管道來(lái)注冊(cè)并獲取 EventLoops
  3. 指定 Channel 類(lèi)來(lái)使用
  4. 設(shè)置處理器用于處理接收到的管道的 I/O 和數(shù)據(jù)
  5. 創(chuàng)建一個(gè)新的 Bootstrap 來(lái)連接到遠(yuǎn)程主機(jī)
  6. 設(shè)置管道類(lèi)
  7. 設(shè)置處理器來(lái)處理 I/O
  8. 使用相同的 EventLoop 作為分配到接收的管道
  9. 連接到遠(yuǎn)端
  10. 連接完成處理業(yè)務(wù)邏輯 (比如, proxy)
  11. 通過(guò)配置了的 Bootstrap 來(lái)綁定到管道

注意,新的 EventLoop 會(huì)創(chuàng)建一個(gè)新的 Thread。出于該原因,EventLoop 實(shí)例應(yīng)該盡量重用。或者限制實(shí)例的數(shù)量來(lái)避免耗盡系統(tǒng)資源。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)