一个多路复用器Selector可以同时轮询多个Channel,由于JDK1.5_update10版本(+)使用了epoll()代替传统的select实现,所以它并没有最大连接句柄1024/2048的限制。这也就意味着只需要一个线程负责Selector的轮询,就可以接入成千上万的客户端,这确实是个非常巨大的技术进步。
使用非阻塞I/O模型之后,Netty解决了传统同步阻塞I/O带来的性能、吞吐量和可靠性问题。
线程调度模型
常用的Reactor线程模型有三种,分别如下:
1.Reactor单线程模型:Reactor单线程模型,指的是所有的I/O操作都在同一个NIO线程上面完成。对于一些小容量应用场景,可以使用单线程模型。
2.Reactor多线程模型:Rector多线程模型与单线程模型最大的区别就是有一组NIO线程处理I/O操作。主要用于高并发、大业务量场景。
3.主从Reactor多线程模型:主从Reactor线程模型的特点是服务端用于接收客户端连接的不再是个1个单独的NIO线程,而是一个独立的NIO线程池。利用主从NIO线程模型,可以解决1个服务端监听线程无法有效处理所有客户端连接的性能不足问题。
事实上,Netty的线程模型并非固定不变,通过在启动辅助类中创建不同的EventLoopGroup实例并通过适当的参数配置,就可以支持上述三种Reactor线程模型。
在大多数场景下,并行多线程处理可以提升系统的并发性能。但是,如果对于共享资源的并发访问处理不当,会带来严重的锁竞争,这最终会导致性能的下降。为了尽可能的避免锁竞争带来的性能损耗,可以通过串行化设计,即消息的处理尽可能在同一个线程内完成,期间不进行线程切换,这样就避免了多线程竞争和同步锁。
为了尽可能提升性能,Netty采用了串行无锁化设计,在I/O线程内部进行串行操作,避免多线程竞争导致的性能下降。表面上看,串行化设计似乎CPU利用率不高,并发程度不够。但是,通过调整NIO线程池的线程参数,可以同时启动多个串行化的线程并行运行,这种局部无锁化的串行线程设计相比一个队列-多个工作线程模型性能更优。
序列化方式
影响序列化性能的关键因素总结如下:
1.序列化后的码流大小(网络带宽占用)
2.序列化&反序列化的性能(CPU资源占用)
3.并发调用的性能表现:稳定性、线性增长、偶现的时延毛刺等
对Java序列化和二进制编码分别进行性能测试,编码100万次,测试结果表明:Java序列化的性能只有二进制编码的6.17%左右。
Netty默认提供了对Google Protobuf的支持,通过扩展Netty的编解码接口,用户可以实现其它的高性能序列化框架,例如Thrift的压缩二进制编解码框架。
不同的应用场景对序列化框架的需求也不同,对于高性能应用场景Netty默认提供了Google的Protobuf二进制序列化框架,如果用户对其它二进制序列化框架有需求,也可以基于Netty提供的编解码框架扩展实现。
Netty架构剖析之可靠性
Netty面临的可靠性挑战:
1.作为RPC框架的基础网络通信框架,一旦故障将导致无法进行远程服务(接口)调用。
2.作为应用层协议的基础通信框架,一旦故障将导致应用协议栈无法正常工作。
3.网络环境复杂(例如手游或者推送服务的GSM/3G/WIFI网络),故障不可避免,业务却不能中断。
从应用场景看,Netty是基础的通信框架,一旦出现Bug,轻则需要重启应用,重则可能导致整个业务中断。它的可靠性会影响整个业务集群的数据通信和交换,在当今以分布式为主的软件架构体系中,通信中断就意味着整个业务中断,分布式架构下对通信的可靠性要求非常高。
从运行环境看,Netty会面临恶劣的网络环境,这就要求它自身的可靠性要足够好,平台能够解决的可靠性问题需要由Netty自身来解决,否则会导致上层用户关注过多的底层故障,这将降低Netty的易用性,同时增加用户的开发和运维成本。
Netty的可靠性是如此重要,它的任何故障都可能会导致业务中断,蒙受巨大的经济损失。因此,Netty在版本的迭代中不断加入新的可靠性特性来满足用户日益增长的高可靠和健壮性需求。