Netty、Redis、ZooKeeper高并发实战(笔记六)

大兔子大兔子 提交于 2020-08-11 16:16:33

netty 使用:
Bootstrap启动流程中8个步骤。
//创建反应器线程组
     //boss线程组
 EventLoopGroupbossLoopGroup = new NioEventLoopGroup(1);
        //worker线程组
EventLoopGroupworkerLoopGroup = new NioEventLoopGroup();
        //...
        //1 设置反应器线程组
        b.group(bossLoopGroup, workerLoopGroup);
在设置反应器线程组之前,创建了两个NioEventLoopGroup线程组,一个负责处理连接监听IO事件,名为bossLoopGroup;另一个负责数据IO事件和Handler业务处理,名为workerLoopGroup。
在线程组创建完成后,就可以配置给启动器实例,调用的方法是b.group(bossGroup,workerGroup),它一次性地给启动器配置了两大线程组。
不一定非得配置两个线程组,可以仅配置一个EventLoopGroup反应器线程组。具体的配置方法是调用b.group(workerGroup)。在这种模式下,连接监听IO事件和数据传输IO事件可能被挤在了同一个线程中处理。这样会带来一个风险:新连接的接受被更加耗时的数据传输或者业务处理所阻塞。
Netty不止支持Java NIO,也支持阻塞式的OIO(也叫BIO,Block-IO,即阻塞式IO)。下面配置的是Java NIO类型的通道类型,方法如下:
//2 设置nio类型的通道
 b.channel(NioServerSocketChannel.class);
 如果确实需要指定Bootstrap的IO模型为BIO,那么这里配置上Netty的OioServerSocketChannel.class类即可。由于NIO的优势巨大,通常不会在Netty中使用BIO。
 /3 设置监听端口
 b.localAddress(new InetSocketAddress(port));
 //4 设置通道的参数
 b.option(ChannelOption.SO_KEEPALIVE, true);
 b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
 这里用到了Bootstrap的option()选项设置方法。对于服务器的Bootstrap而言,这个方法的作用是:给父通道(Parent Channel)接收连接通道设置一些选项。
如果要给子通道(Child Channel)设置一些通道选项,则需要用另外一个childOption()设置方法。
可以设置哪些通道选项(ChannelOption)呢?在上面的代码中,设置了一个底层TCP相关的选项ChannelOption.SO_KEEPALIVE。该选项表示:是否开启TCP底层心跳机制,true为开启,false为关闭。

第5步:装配子通道的Pipeline流水线
装配子通道的Handler流水线调用childHandler()方法,传递一个ChannelInitializer通道初始化类的实例。在父通道成功接收一个连接,并创建成功一个子通道后,就会初始化子通道,这里配置的ChannelInitializer实例就会被调用。
在ChannelInitializer通道初始化类的实例中,有一个initChannel初始化方法,在子通道创建后会被执行到,向子通道流水线增加业务处理器。
//5 装配子通道流水线
b.childHandler(new ChannelInitializer<SocketChannel>() {
    //有连接到达时会创建一个通道的子通道,并初始化
    protected void initChannel(SocketChannelch) throws Exception {
        // 流水线管理子通道中的Handler业务处理器
            // 向子通道流水线添加一个Handler业务处理器
            ch.pipeline().addLast(new NettyDiscardHandler());
        }
});
为什么仅装配子通道的流水线,而不需要装配父通道的流水线呢?
原因是:父通道也就是NioServerSocketChannel连接接受通道,它的内部业务处理是固定的:接受新连接后,创建子通道,然后初始化子通道,所以不需要特别的配置。如果需要完成特殊的业务处理,可以使用ServerBootstrap的handler(ChannelHandler handler)方法,为父通道设置ChannelInitializer初始化器。

 / 6 开始绑定端口,通过调用sync同步方法阻塞直到绑定成功
ChannelFuturechannelFuture = b.bind().sync();
Logger.info(" 服务器启动成功,监听端口: " +
channelFuture.channel().localAddress());
b.bind()方法的功能:返回一个端口绑定Netty的异步任务channelFuture。在这里,并没有给channelFuture异步任务增加回调监听器,而是阻塞channelFuture异步任务,直到端口绑定任务执行完成。

// 7 等待通道关闭
// 自我阻塞,直到通道关闭的异步任务结束
ChannelFuturecloseFuture = channelFuture.channel().closeFuture();
closeFuture.sync();
如果要阻塞当前线程直到通道关闭,可以使用通道的closeFuture()方法,以获取通道关闭的异步任务。当通道被关闭时,closeFuture实例的sync()方法会返回。
// 8关闭EventLoopGroup,
// 释放掉所有资源,包括创建的反应器线程
workerLoopGroup.shutdownGracefully();
bossLoopGroup.shutdownGracefull();

ChannelInboundHandler通道入站处理器
1.channelRegistered 
当通道注册完成后,Netty会调用fireChannelRegistered,触发通道注册事件。通道会启动该入站操作的流水线处理,在通道注册过的入站处理器Handler的channelRegistered方法,会被调用到。
2.channelActive 
当通道激活完成后,Netty会调用fireChannelActive,触发通道激活事件。通道会启动该入站操作的流水线处理,在通道注册过的入站处理器Handler的channelActive方法,会被调用到。
3.channelRead 
当通道缓冲区可读,Netty会调用fireChannelRead,触发通道可读事件。通道会启动该入站操作的流水线处理,在通道注册过的入站处理器Handler的channelRead方法,会被调用到。
4.channelReadComplete 
当通道缓冲区读完,Netty会调用fireChannelReadComplete,触发通道读完事件。通道会启动该入站操作的流水线处理,在通道注册过的入站处理器Handler的channelReadComplete方法,会被调用到。
5.channelInactive 
当连接被断开或者不可用,Netty会调用fireChannelInactive,触发连接不可用事件。通道会启动对应的流水线处理,在通道注册过的入站处理器Handler的channelInactive方法,会被调用到。
6.exceptionCaught 
当通道处理过程发生异常时,Netty会调用fireExceptionCaught,触发异常捕获事件。通道会启动异常捕获的流水线处理,在通道注册过的处理器Handler的exceptionCaught方法,会被调用到。

ChannelOutboundHandler通道出站处理器
1.bind 
监听地址(IP+端口)绑定:完成底层Java IO通道的IP地址绑定。如果使用TCP传输协议,这个方法用于服务器端。
2.connect 
连接服务端:完成底层Java IO通道的服务器端的连接操作。如果使用TCP传输协议,这个方法用于客户端。
3.write 
写数据到底层:完成Netty通道向底层Java IO通道的数据写入操作。此方法仅仅是触发一下操作而已,并不是完成实际的数据写入操作。
4.flush 
腾空缓冲区中的数据,把这些数据写到对端:将底层缓存区的数据腾空,立即写出到对端。
5.read 
从底层读数据:完成Netty通道从Java IO通道的数据读取。
6.disConnect 
断开服务器连接:断开底层Java IO通道的服务器端连接。如果使用TCP传输协议,此方法主要用于客户端。
7.close 
主动关闭通道:关闭底层的通道,例如服务器端的新连接监听通道。
ChannelHandler中的回调方法的执行顺序为:handlerAdded()→channelRegistered()→channelActive()→入站方法回调→channelInactive()→channelUnregistered()→handlerRemoved()。其中,读数据的入站回调为:channelRead()→channelReadComplete();入站方法会多次调用,每一次有ByteBuf数据包入站都会调用到
pipeline.addLast() 入栈处理器的执行顺序是按照流水线添加的顺序执行的,出栈和入栈的执行顺序相反;
通过pipeline.addLast()方法添加OutBoundHandler出站处理器的顺序为A→B→C。从结果可以看出,出站流水处理次序为从后向前:C→B→A

ChannelHandlerContext上下文
Channel、Handler、ChannelHandlerContext三者的关系为:Channel通道拥有一条ChannelPipeline通道流水线,
每一个流水线节点为一个ChannelHandlerContext通道处理器上下文对象,每一个上下文中包裹了一个ChannelHandler通道处理器。
在ChannelHandler通道处理器的入站/出站处理方法中,Netty都会传递一个Context上下文实例作为实际参数。
通过Context实例的实参,在业务处理中,可以获取ChannelPipeline通道流水线的实例或者Channel通道的实例.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!