Netty:核心功能Bootstrapping

ChannelPipeline 、ChannelHandler和編解碼器(後面再系統講解)提供工具,我們可以處理對數據進行豐富的邏輯處理。

我們創建了組件後,如何將其組裝形成一個應用程序?
答案是“bootstrapping(引導)”。在最簡單的條件下,引導就是配置應用程序的過程。Netty 的引導客戶端和服務器的類從網絡基礎設施使您的應用程序代碼可以在後臺連接和啓動所有的組件。簡而言之,引導使你的 Netty 應用程序完整。

Netty中的引導類型有兩種。“服務器”和“客戶”的引導,他們可以支持應用程序的功能。從這個意義上講,“服務器”應用程序通過一個“父”管道接受連接並創建“子”管道,而“客戶端”很可能只需要一個單一的、非“父”對所有網絡交互的管道(對於無連接的比如 UDP 協議也是一樣)。
兩個引導實現自一個名爲 AbstractBootstrap 的超類
在這裏插入圖片描述

1.Bootstrap

在這裏插入圖片描述
Bootstrap工作原理
在這裏插入圖片描述

EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap(); //1創建一個新的 Bootstrap 來創建和連接到新的客戶端管道
bootstrap.group(group) //2指定 EventLoopGroup
    .channel(NioSocketChannel.class) //3指定 Channel 實現來使用
    .handler(new SimpleChannelInboundHandler<ByteBuf>() { //4設置處理器給 Channel 的事件和數據
        @Override
        protected void channeRead0(
            ChannelHandlerContext channelHandlerContext,
            ByteBuf byteBuf) throws Exception {
                System.out.println("Received data");
                byteBuf.clear();
            }
        });
ChannelFuture future = bootstrap.connect(
    new InetSocketAddress("www.manning.com", 80)); //5連接到遠端主機
future.addListener(new ChannelFutureListener() {
    @Override
    public void operationComplete(ChannelFuture channelFuture)
        throws Exception {
            if (channelFuture.isSuccess()) {
                System.out.println("Connection established");
            } else {
                System.err.println("Connection attempt failed");
                channelFuture.cause().printStackTrace();
            }
        }
    });

注意:Channel 的實現和 EventLoop 的處理過程在 EventLoopGroup 中必須兼容,哪些 Channel 是和 EventLoopGroup 是兼容的可以查看 API 文檔。
例如,NioEventLoop,NioEventLoopGroup 和 NioServerSocketChannel 在一起使用。

2 ServerBootstrap

服務器的引導共用了客戶端引導的一些邏輯。
在這裏插入圖片描述

在這裏插入圖片描述
child* 的方法都是操作在子的 Channel,被 ServerChannel 管理。

NioEventLoopGroup group = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap(); //1創建新的 ServerBootstrap 來創建新的 SocketChannel 管道並綁定他們
bootstrap.group(group) //2指定 EventLoopGroup 用於從註冊的 ServerChannel 中獲取EventLoop 和接收到的管道
    .channel(NioServerSocketChannel.class) //3指定要使用的管道類
    .childHandler(new SimpleChannelInboundHandler<ByteBuf>() { //4設置子處理器用於處理接收的管道的 I/O 和數據
        @Override
        protected void channelRead0(ChannelHandlerContext ctx,
            ByteBuf byteBuf) throws Exception {
                System.out.println("Reveived data");
                byteBuf.clear();
            }
        }
    );
ChannelFuture future = bootstrap.bind(new InetSocketAddress(8080)); //5綁定管道
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();
            }
        }
    }
);

3 在客戶端 Channel中使用新的Bootstrap

方案一:在Channel中創建一個新的 Bootstrap 並使用,這個解決方案不一定有效。至少,你需要創建另一個 EventLoop 給新的客戶端 Channel 的,並且 Channel 將會需要在不同的 Thread 間進行上下文切換。
方案二:由於 EventLoop 繼承自 EventLoopGroup ,您可以通過傳遞Channel 的 EventLoop 到 新的Bootstrap 的 group() 方法。這允許客戶端 Channel 來操作相同的 EventLoop,這樣就能消除了額外的線程創建和所有相關的上下文切換的開銷。
方案一比較簡單,方案二的處理流程如下
在這裏插入圖片描述

實現 EventLoop 共享,包括設置 EventLoop 引導通過Bootstrap.eventLoop() 方法。

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();
        }
    }
});

4 ChannelInitializer 添加多個ChannelHandler

在所有的例子代碼中,我們在引導過程中通過 handler() 或childHandler() 都只添加了一個 ChannelHandler 實例,對於簡單的程序可能足夠,但是對於複雜的程序則無法滿足需求。例如,某個程序必須支持多個協議,如 HTTP、WebSocket。若在一個 ChannelHandle r中處理這些協議將導致一個龐大而複雜的 ChannelHandler。Netty 通過添加多個 ChannelHandler,從而使每個 ChannelHandler 分工明確,結構清晰。

Netty 的一個優勢是可以在 ChannelPipeline 中堆疊很多ChannelHandler 並且可以最大程度的重用代碼。如何添加多個ChannelHandler 呢?Netty 提供 ChannelInitializer 抽象類用來初始化 ChannelPipeline 中的 ChannelHandler。ChannelInitializer是一個特殊的 ChannelHandler,通道被註冊到 EventLoop 後就會調用ChannelInitializer,並允許將 ChannelHandler 添加到CHannelPipeline;完成初始化通道後,這個特殊的 ChannelHandler 初始化器會從 ChannelPipeline 中自動刪除。

代碼示例:

ServerBootstrap bootstrap = new ServerBootstrap();//1
bootstrap.group(new NioEventLoopGroup(), new NioEventLoopGroup())  //2
    .channel(NioServerSocketChannel.class)  //3
    .childHandler(new ChannelInitializerImpl()); //4
ChannelFuture future = bootstrap.bind(new InetSocketAddress(8080));  //5
future.sync();


final class ChannelInitializerImpl extends ChannelInitializer<Channel> {  //6
    @Override
    protected void initChannel(Channel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline(); //7
        pipeline.addLast(new HttpClientCodec());
        pipeline.addLast(new HttpObjectAggregator(Integer.MAX_VALUE));

    }
}

關於Bootstraping,還有ChannelOption和屬性的知識點,以及關閉客戶端或服務器,這些相對比較簡單,就不再贅述了.
參考:https://www.w3cschool.cn/essential_netty_in_action/essential_netty_in_action-2rto28cd.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章