Netty极大地简化了TCP、UDP套接字、HTTP web服务程序的开发。
简单来说,出站就是指输出,入站就是指输入。
Channel --> selector --> Reactor ---> handler
第1步:通道注册。IO源于通道(Channel)。IO是和通道(对应于底层连接而言)强相关的。一个IO事件,一定属于某个通道。但是,如果要查询通道的事件,首先要将通道注册到选择器。只需通道提前注册到Selector选择器即可,IO事件会被选择器查询到。第2步:查询选择。在反应器模式中,一个反应器(或者SubReactor子反应器)会负责一个线程;不断地轮询,查询选择器中的IO事件(选择键)。第3步:事件分发。如果查询到IO事件,则分发给与IO事件有绑定关系的Handler业务处理器。第4步:完成真正的IO操作和业务处理,这一步由Handler业务处理器负责。以上4步,就是整个反应器模式的IO处理器流程。其中,第1步和第2步,其实是Java NIO的功能,反应器模式仅仅是利用了Java NIO的优势而已。
反应器模式中,一个反应器(或者SubReactor子反应器)会负责一个事件处理线程,不断地轮询,通过Selector选择器不断查询注册过的IO事件(选择键)。如果查询到IO事件,则分发给Handler业务处理器。
netty的反应器为NIOEventLoop。通过关系图,可以看出:一个NioEventLoop拥有一个Thread线程,负责一个Java NIO Selector选择器的IO事件轮询。
bootstrap类是netty提供的一个便利的工厂类。用来快速组装通道、反应器、处理器。
1.创建反应器线程组,并赋值给启动器实例。
注:不一定非得配置两个线程组,可以仅配置一个EventLoopGroup反应器线程组。具体的配置方法是调用b.group( workerGroup)。在这种模式下,连接监听IO事件和数据传输IO事件可能被挤在了同一个线程中处理。这样会带来一个风险:新连接的接受被更加耗时的数据传输或者业务处理所阻塞。 注:在服务器端,建议设置成两个线程组的工作模式。
//创建反应器线程组 //boss线程组 EventLoopGroup bossLoopGroup = new NioEventLoopGroup(1); //worker线程组 EventLoopGroup workerLoopGroup = new NioEventLoopGroup(); //... //1 设置反应器线程组 b.group(bossLoopGroup, workerLoopGroup);2.设置通道的IO类型
//2 设置nio类型的通道 b.channel(NioServerSocketChannel.class);3.设置监听端口
//3 设置监听端口 b.localAddress(new InetSocketAddress(port));4.设置传输通道的配置选项
注:option()设置父通道,childOption()设置子通道。
//4 设置通道的参数 b.option(ChannelOption.SO_KEEPALIVE, true);//是否开启心跳机制 b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); 通道参数: SO_RCVBUF,SO_SNDBUF 发送缓冲区和接收缓冲区。 TCP_NODELAY 立即发送数据。 SO_KEEOALIVE 心跳机制 SO_REUSEADDR 地址复用 SO_LINGER 关闭socket的延迟时间 SO_BACKLOG 服务器端接收连接的队列长度 SO_BROADCAST 设置广播模式5.装配子通道的PiPeline流水线
注:每一条通道的子通道,都用一条ChannelPipeline流水线。它的内部有一个双向的链表。 注:父通道不需要装配流水线。他的内部业务固定:接收新连接,创建子通道,初始化子通道。 注:ChannelInitializer有一泛型参数SocketChannel,代表初始化通道类型。这个类和启动器中设置的通道类型会一一对应 //5 装配子通道流水线 b.childHandler(new ChannelInitializer<SocketChannel>() { //有连接到达时会创建一个通道的子通道,并初始化 protected void initChannel(SocketChannel ch) throws Exception { // 流水线管理子通道中的Handler业务处理器 // 向子通道流水线添加一个Handler业务处理器 ch.pipeline().addLast(new NettyDiscardHandler()); } });6.开始绑定服务器新连接的监听端口
注:在netty中,所有的IO操作都是异步执行的,IO操作会立刻返回。执行完有两种方式,一自我阻塞到异步任务Future完成。二为Future增加时间监听器。
// 6 开始绑定端口,通过调用sync同步方法阻塞直到绑定成功 ChannelFuturechannelFuture = b.bind().sync(); Logger.info(" 服务器启动成功,监听端口: " + channelFuture.channel().localAddress());7.自我阻塞,知道通道关闭。
注:当通道被关闭时,closeFuture实例的sync()方法返回。
// 7 等待通道关闭 // 自我阻塞,直到通道关闭的异步任务结束 ChannelFuturecloseFuture = channelFuture.channel().closeFuture(); closeFuture.sync();8.关闭EventLoopGroup
注:关闭Reactor反应器线程组,同时会关闭内部的subReactor子反应器线程,也会关闭内部的Selector选择器、内部的轮询线程以及负责查询的所有的子通道。在子通道关闭后,会释放掉底层的资源,如TCP Socket文件描述符等。
// 8关闭EventLoopGroup, // 释放掉所有资源,包括创建的反应器线程 workerLoopGroup.shutdownGracefully(); bossLoopGroup.shutdownGracefully();
