一、先讲下NIO编程。NIO(Non-block I/O),亦叫做非阻塞I/O
与Socket类和ServerSocket类相对应,NIO也提供了SocketChannel和ServerSocketChannel两种不同的套接字通道实现。
1 缓冲区Buffer
这里首先介绍缓冲区的概念,NIO和原I/O的一个重要区别就是NIO库中,所有数据都是用缓冲区处理的。
缓冲区实质上是一个数组。通常它是一个字节数组(ByteBuffer),也可以使用其他种类的数组。
最常用的缓冲区是ByteBuffer,提供了一组功能用于操作byte数组。
2 通道Channel
通道和流的不同之处在于通道是双向的,流只是一个方向上移动。可以用于读、写或者两者同时进行。
下面看下Channel的类图继承关系
可以看出实际上Channel可以分为两大类:用于网络读写的SelectableChannel和用于文件操作的FileChannel。
3 多路复用器Selector
简单来讲,Selector会不断地轮询注册在其上的Channel,如果某个Channel上面发生读或者写事件,这个Channel就处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以获取就绪Channel的集合,进行后续的I/O操作。
NIO服务端序列图
NIO客户端序列图
现总结NIO编程的优点,如下:
(1)客户端发起的连接操作是异步的,可以通过在多路复用器注册OP_CONNECT等待后续结果,不需要像之前的客户端那样被同步阻塞。
(2)SocketChannel的读写操作都是异步的,如果没有可读写的数据它不会同步等待,直接返回,这样I/O通信线程就可以处理其他的链路,不需要同步等待这个链路可用。
(3)一个Selector线程可以同时处理成千上万个客户端连接,而且性能不会随着客户端的增加而线性下降。非常适合做高性能、高负载的网络服务器。
虽然,NIO有众多优势,但是不意味着所有的Java网络编程都必须要选择NIO和Netty,具体选择什么样的I/O模型或者NIO框架,完全基于业务的实际应用场景和性能诉求,如果客户端并发连接数不多,周边对接的网元不多,服务器的负载也不重,那就完全没必要选择NIO做服务端;如果是相反情况,那就要考虑选择合适的NIO框架进行开发。
二、选择Netty的理由
作为一个NIO服务端,需要能够处理网络的闪断、客户端的重复接入、客户端的安全认证、消息的编解码、半包读写等情况,如果没有足够的NIO编程经验,一个NIO框架的稳定往往需要半年甚至更长的时间。
更为糟糕的是,一旦在生产环境中发生问题,往往会导致跨节点的服务调用中断,严重的可能会导致整个集群环境都不可用,需要重启服务器,这种非常停机会带来巨大的损失。
从可维护性角度看,由于NIO采用了异步非阻塞编程模型,而且是一个I/O线程处理多条链路,它的调试和跟踪非常麻烦,特别是生产环境中的问题,我们无法进行有效的调试和跟踪,往往只能靠一些日志来辅助分析,定位难度很大。
使用NIO框架Netty来进行NIO编程,它既可以作为客户端也可以作为服务端,同时支持UDP和异步文件传输,功能非常强大。
三、为什么选择Netty
Netty的优点总结如下:
1 api使用简单,开发门槛低;
2 功能强大,预置了多种编解码功能,支持多种主流协议;
3 定制能力强,可以通过ChannelHandler对通信框架进行灵活地扩展;
4 性能高,通过与其他业界主流的NIO框架对比,Netty的综合性能最优;
5 成熟稳定
6 社区活跃,版本迭代周期短,发现的Bug可以及时修复