Netty组件(四)—— 引导Bootstrap、内置通信传输模式及ChannelOption
Netty组件(四)—— 引导Bootstrap、内置通信传输模式及ChannelOption
引导Bootstrap
我们在从NIO编程到Netty的使用一文中,简单使用Netty完成了一个小应用,其中在服务端和客户端中,我们分别是使用了ServerBootstrap和Bootstrap,如下:
这两种类型的引导:一种用于客户端(简单地称为Bootstrap),而另一种(ServerBootstrap)用于服务器。无论你的应用程序使用哪种协议或者处理哪种类型的数据,唯一决定它使用哪种引导类的是它是作为一个客户端还是作为一个服务器。
ServerBootstrap | Bootstrap | |
---|---|---|
网络编程中的作用 | 连接到远程主机和端口 | 绑定到一个本地端口 |
EventLoopGroup的数目 | 1 | 2 |
ServerBootstrap 将绑定到一个端口,因为服务器必须要监听连接,而 Bootstrap 则是由想要连接到远程节点的客户端应用程序所使用的。
第二个区别可能更加明显。引导一个客户端只需要一个EventLoopGroup,但是一个ServerBootstrap 则需要两个(也可以是同一个实例)。
因为服务器需要两组不同的Channel。第一组将只包含一个ServerChannel,代表服务器自身的已绑定到某个本地端口的正在监听的套接字。而第二组将包含所有已创建的用来处理传入客户端连接(对于每个服务器已经接受的连接都有一个)的Channel。
与ServerChannel 相关联的 EventLoopGroup 将分配一个负责为传入连接请求创建 Channel 的 EventLoop。一旦连接被接受,第二个 EventLoopGroup 就会给它的Channel分配一个 EventLoop。
可参见网络编程——NIO的Reactor模式理解
如下,我们修改之前的代码,在服务端使用两个EventLoopGroup实例:
在Bootstrap的handler方法中,其参数只能传递一个ChannelHandler ,但是有时我们需要传递多个ChannelHandler参数,怎么办呢?
其实我们之前的例子已经可以在引导过程中添加多个ChannelHandler,因为Netty提供了一个特殊的ChannelInboundHandlerAdapter子类:ChannelInitializer,它定义了下面的方法:initChannel(SocketChannel socketChannel)
这个方法提供了一种将多个 ChannelHandler 添加到一个ChannelPipeline 中的简便方法。
你只需要简单地向Bootstrap 或 ServerBootstrap 的实例提供你的 ChannelInitializer 实现即可,并且一旦Channel 被注册到了它的 EventLoop 之后,就会调用你的 initChannel() 版本。在该方法返回之后,ChannelInitializer 的实例将会从ChannelPipeline 中移除它自己。
内置通信传输模式
我们之前在介绍使用Netty来完成一个小应用的时候,采用的通信是NIO,如下
其实在Netty除了NIO,还有其他的几种网络通信模式,如下:
名称 | 描述 |
---|---|
NIO | io.netty.channel.socket.nio 使用java.nio.channels 包作为基础——基于选择器的方式 |
Epoll | io.netty.channel.epoll 由 JNI 驱动的 epoll() 和非阻塞 IO。这个传输支持只有在Linux 上可用的多种特性,如SO_REUSEPORT,比NIO传输更快,而且是完全非阻塞的。将NioEventLoopGroup替换为EpollEventLoopGroup , 并且将NioServerSocketChannel.class 替换为EpollServerSocketChannel.class 即可。 |
OIO | io.netty.channel.socket.oio 使用java.net包作为基础——使用阻塞流 |
Local | io.netty.channel.local 可以在VM内部通过管道进行通信的本地传输 |
Embedded | io.netty.channel.embedded Embedded 传输,允许使用ChannelHandler 而又不需要一个真正的基于网络的传输。在测试ChannelHandler 实现时非常有用 |
使用也是非常的简单,我们只需将上图中的两处进行修改即可,如我们将NIO修改为Epoll
ChannelOption
ChannelOption可以用来给Channel来设置参数的,该ChannelOption参数其实为TCP/IP相关的参数, 其常用的参数类型如下:
ChannelOption.SO_BACKLOG
ChannelOption.SO_BACKLOG 对应的是 tcp/ip 协议listen函数中的backlog参数,函数listen(int socketfd,int backlog)用来初始化服务端可连接队列
服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接,多个客户端来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理,backlog参数指定了队列的大小
ChannelOption.SO_REUSEADDR
ChanneOption.SO_REUSEADDR 对应于套接字选项中的SO_REUSEADDR,这个参数表示允许重复使用本地地址和端口
比如,某个服务器进程占用了TCP的80端口进行监听,此时再次监听该端口就会返回错误,使用该参数就可以解决问题,该参数允许共用该端口,这个在服务器程序中比较常使用,比如某个进程非正常退出,该程序占用的端口可能要被占用一段时间才能允许其他进程使用,而且程序死掉以后,内核需要一定的时间才能够释放此端口,不设置SO_REUSEADDR就无法正常使用该端口。
ChannelOption.SO_KEEPALIVE
Channeloption.SO_KEEPALIVE 参数对应于套接字选项中的SO_KEEPALIVE,该参数用于设置TCP连接,当设置该选项以后,连接会测试链接的状态,这个选项用于可能长时间没有数据交流的连接。当设置该选项以后,如果在两小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文。
ChannelOption.SO_SNDBUF
和 ChannelOption.SO_RCVBUF
ChannelOption.SO_SNDBUF 参数对应于套接字选项中的SO_SNDBUF,ChannelOption.SO_RCVBUF参数对应于套接字选项中的SO_RCVBUF这两个参数用于操作接收缓冲区和发送缓冲区的大小
接收缓冲区用于保存网络协议站内收到的数据,直到应用程序读取成功,发送缓冲区用于保存发送数据,直到发送成功。
ChannelOption.SO_LINGER
ChannelOption.SO_LINGER 参数对应于套接字选项中的SO_LINGER,Linux内核默认的处理方式是当用户调用close() 方法的时候,函数返回,在可能的情况下,尽量发送数据,不一定保证会发生剩余的数据,造成了数据的不确定性,使用SO_LINGER可以阻塞close()的调用时间,直到数据完全发送。
ChannelOption.TCP_NODELAY
ChannelOption.TCP_NODELAY 参数对应于套接字选项中的TCP_NODELAY,该参数的使用与Nagle算法有关,Nagle算法是将小的数据包组装为更大的帧然后进行发送,而不是输入一次发送一次,因此在数据包不足的时候会等待其他数据的到了,组装成大的数据包进行发送,虽然该方式有效提高网络的有效负载,但是却造成了延时,而该参数的作用就是禁止使用Nagle算法,使用于小数据即时传输,于TCP_NODELAY相对应的是TCP_CORK,该选项是需要等到发送的数据量最大的时候,一次性发送数据,适用于文件传输。
如上图,我们就是用了ChannelOption参数,用于禁止使用Nagle算法,使得小数据得以即时传输。