Netty初探

1
2
3
Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.
Netty is a NIO client server framework which enables quick and easy development of network applications such as protocol servers and clients. It greatly simplifies and streamlines network programming such as TCP and UDP socket server.

为什么不选择JAVA原生NIO

  • NIO 的类库和 API 繁杂,使用麻烦:需要熟练掌握 SelectorServerSocketChannelSocketChannelByteBuffer等。
  • 编写出高质量的 NIO 程序需要具备其他的额外技能做铺垫:例如熟悉 Java 多线程编程,因为 NIO 编程涉及到 Reactor 模式,必须对多线程和网路编程非常熟悉。
  • NIO 编程的特点是功能开发相对容易,但是可靠性能力补齐工作量和难度都非常大:例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理等等。
  • JDK NIOEpoll Bug,它会导致Selector空轮询,最终导致CPU 100%

为什么选择Netty

  • API使用简单,开发门槛低;
  • 功能强大,预置了多种编解码功能,支持多种主流协议;
  • 定制能力强,可以通过ChannelHandler对通信框架进行灵活地扩展;
  • 性能高,通过与其他业界主流的NIO框架对比,Netty的综合性能最优;
  • 成熟、稳定,Netty修复了已经发现的所有JDK NIO BUG

Netty架构设计

Netty采用了比较典型的三层网络架构进行设计,逻辑架构图如下所示:

  • 传输服务:支持 BIONIO
  • 容器集成:支持 OSGIJBossMCSpringGuice容器
  • 协议支持:HTTPProtobuf、二进制、WebSocket等一系列常见协议都支持。还支持通过实行编码解码逻辑来实现自定义协议
  • Core 核心:可扩展事件模型、通用通信 API、支持零拷贝的 ByteBuf 缓冲对象

高性能的三大要素

  1. 传输:用什么样的通道将数据发送给对方,BIO、NIO 或者 AIO,IO 模型在很大程度上决定了框架的性能
  2. 协议:采用什么样的通信协议,HTTP 或者内部私有协议。协议的选择不同,性能模型也不同。相比于公有协议,内部私有协议的性能通常可以被设计的更优。
  3. 线程模型:数据报如何读取?读取之后的编解码在哪个线程进行,编解码后的消息如何派发,Reactor 线程模型的不同,对性能的影响也非常大。

Netty高性能原因

IO模型

Netty的IO线程NioEventLoop由于聚合了多路复用器Selector,可以同时并发处理成百上千个客户端Channel,由于读写操作都是非阻塞的,这就可以充分提升IO线程的运行效率,避免由于频繁IO阻塞导致的线程挂起

零拷贝

在后面的文章专门详解

内存池

在后面的文章专门详解

Netty 线程模型

Netty 主要基于主从 Reactor 多线程模型(如下图)做了一定的修改,其中主从 Reactor 多线程模型有多个 Reactor

  • MainReactor负责客户端的连接请求,并将请求转交给 SubReactor
  • SubReactor负责相应通道的IO读写请求
  • 非IO请求(具体逻辑处理)等待 worker threads 进行处理

注意:Netty 的线程模型基于主从 Reactor多线程,借用了 MainReactorSubReactor 的结构。但是Netty实际实现上 SubReactorWorker 线程在同一个线程池中

1
2
3
4
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)

Netty里对应mainReactor的角色叫做Boss,而对应subReactor的角色叫做Worker

bossGroupworkerGroup 这两个 group 均是线程池:

  • bossGroup 线程池则只是在 Bind 某个端口后,获得其中一个线程作为 MainReactor,专门处理端口的 Accept 事件
  • workerGroup 线程池会被各个SubReactorWorker 线程充分利用

Netty线程模型

下图来自组内大牛分享,盗来使用~~:

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

参考

Introduction to Netty

Netty的那点事儿

NIO Reactor 模型 & Netty 线程模型

do we need more than a single thread for boss group?

热评文章