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的优势而已。
三、netty中的channel组件

四、netty中的反应器-Reactor
反应器模式中,一个反应器(或者SubReactor子反应器)会负责一个事件处理线程,不断地轮询,通过Selector选择器不断查询注册过的IO事件(选择键)。如果查询到IO事件,则分发给Handler业务处理器。

netty的反应器为NIOEventLoop。通过关系图,可以看出:一个NioEventLoop拥有一个Thread线程,负责一个Java NIO Selector选择器的IO事件轮询。
五、netty中的处理器-Handler
- 可供选择器监控的通道IO时间类型4中:可读-READ,可写-WRITE,连接-CONNECT,接收-ACCEPT。
- 选择器执行以上事件的查询,然后进行对应的事件分发,分发目标就是netty的Handler处理器。
六、netty的流水线
- netty设计了一个特殊的组件-ChannelPipeline-通道流水线。实际设计为一个双向链表。
- 入站的IO操作只会且只能从INbound入站处理器类型的Handler流过。出站的IO操作只会且只能从Outbound出站处理器类型的Handler流过。

七、启动器
bootstrap类是netty提供的一个便利的工厂类。用来快速组装通道、反应器、处理器。

父子通道:
- 连接监听类型:放在服务端,负责接收客户端的套接字连接。ACCEPT。
- 传输数据类型:放在客户端和服务端。
- 在netty中,将有接收关系的NIOServerSocketChannel(父)和NIOSocketChannel(子)叫做父子通道。
EventLoopGroup线程组:
- 在netty中,一个EventLoop相当于一个子反应器。一个EventLoop相当于一个线程。
- netty的程序开发不用直接使用单个EventLoop线程,而是使用EventLoopGroup。默认的EventloopGroup内部线程数为CPU*2。
- 为了及时接收到新连接。在服务器端,一般有两个独立的反应器,一个反应器负责连接的监听和接收,另一个反应器负责IO事件处理。对应到netty的服务器程序中,则是设置到两个EventLoopGroup线程组,一个EventLoopGroup负责新连接监听和接收---Boss线程组,一个EventLoopGroup负责IO事件处理,并执行Handler处理器中的业务处理---Worker线程组。
启动流程
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();
本文介绍了Netty中的出站和入站概念,详细阐述了Netty的基本流程,包括通道注册、查询选择、事件分发和IO操作。接着讨论了Netty中的channel组件、Reactor、Handler及其工作原理。还提到了netty的ChannelPipeline流水线设计,以及服务器端的启动流程,包括EventLoopGroup线程组的作用和配置建议。
2361

被折叠的 条评论
为什么被折叠?



