|
|
为什么不选择JAVA原生NIO
NIO的类库和API繁杂,使用麻烦:需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等。- 编写出高质量的
NIO程序需要具备其他的额外技能做铺垫:例如熟悉Java多线程编程,因为NIO编程涉及到Reactor模式,必须对多线程和网路编程非常熟悉。 NIO编程的特点是功能开发相对容易,但是可靠性能力补齐工作量和难度都非常大:例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理等等。JDK NIO的Epoll Bug,它会导致Selector空轮询,最终导致CPU 100%
为什么选择Netty
API使用简单,开发门槛低;- 功能强大,预置了多种编解码功能,支持多种主流协议;
- 定制能力强,可以通过ChannelHandler对通信框架进行灵活地扩展;
- 性能高,通过与其他业界主流的
NIO框架对比,Netty的综合性能最优; - 成熟、稳定,
Netty修复了已经发现的所有JDK NIO BUG;
Netty架构设计
Netty采用了比较典型的三层网络架构进行设计,逻辑架构图如下所示:

- 传输服务:支持
BIO和NIO - 容器集成:支持
OSGI、JBossMC、Spring、Guice容器 - 协议支持:
HTTP、Protobuf、二进制、WebSocket等一系列常见协议都支持。还支持通过实行编码解码逻辑来实现自定义协议 Core核心:可扩展事件模型、通用通信API、支持零拷贝的ByteBuf缓冲对象
高性能的三大要素
- 传输:用什么样的通道将数据发送给对方,BIO、NIO 或者 AIO,IO 模型在很大程度上决定了框架的性能。
- 协议:采用什么样的通信协议,
HTTP或者内部私有协议。协议的选择不同,性能模型也不同。相比于公有协议,内部私有协议的性能通常可以被设计的更优。 - 线程模型:数据报如何读取?读取之后的编解码在哪个线程进行,编解码后的消息如何派发,
Reactor线程模型的不同,对性能的影响也非常大。

Netty高性能原因
IO模型
Netty的IO线程NioEventLoop由于聚合了多路复用器Selector,可以同时并发处理成百上千个客户端Channel,由于读写操作都是非阻塞的,这就可以充分提升IO线程的运行效率,避免由于频繁IO阻塞导致的线程挂起
零拷贝
在后面的文章专门详解
内存池
在后面的文章专门详解
Netty 线程模型
Netty 主要基于主从 Reactor 多线程模型(如下图)做了一定的修改,其中主从 Reactor 多线程模型有多个 Reactor:

MainReactor负责客户端的连接请求,并将请求转交给SubReactorSubReactor负责相应通道的IO读写请求- 非IO请求(具体逻辑处理)等待
worker threads进行处理
注意:Netty 的线程模型基于主从 Reactor多线程,借用了 MainReactor 和 SubReactor 的结构。但是Netty实际实现上 SubReactor 和 Worker 线程在同一个线程池中
|
|
Netty里对应mainReactor的角色叫做Boss,而对应subReactor的角色叫做Worker
bossGroup 和 workerGroup 这两个 group 均是线程池:
bossGroup线程池则只是在Bind某个端口后,获得其中一个线程作为MainReactor,专门处理端口的Accept事件workerGroup线程池会被各个SubReactor和Worker线程充分利用
Netty线程模型
下图来自组内大牛分享,盗来使用~~:

注意:服务器端的 ServerSocketChannel 只绑定到了 bossGroup 中的一个线程, 因此在调用Selector.select 处理客户端的连接请求时, 实际上是在一个线程中的, 所以对只有一个服务(监听一个端口)的应用来说, bossGroup 设置多个线程是没有什么作用的, 反而还会造成资源浪费。