Netty支持的Reactor网络模型

本篇我们主要说一下Netty支持的Reactor网络模型,以及Netty本身是如何实现的。

Netty切换3种IO模式

3种经典的IO模式

我们常见的3中IO模式,可以类比生活中的例子:如吃饭的场景。阻塞IO:去食堂窗口打饭,首先要排好队,直到打完饭才能走;非阻塞IO:点单-等待被叫模式,也就是顾客先来到饭店,用手机/纸质菜单点完餐之后,会得到一个取餐号/取餐牌,然后等饭菜做好后,会有喇叭通知你去取餐;异步IO:这种可以类比包厢模式,当你点完菜之后,如果饭菜做好了,会有专门的服务员把才端上来,你直接实用即可。

当我们提到IO模式的时候,就会提到阻塞/非阻塞 和 同步/异步 这两种概念。对于阻塞/非阻塞而言:比如TCP连接没有数据传过来的时候,会阻塞等待直到有数据到来;当缓冲区写满的时候,会阻塞等待直到缓冲区可写。但是对于非阻塞而言,这些动作都是直接返回的,不会卡住调用线程的主流程操作。所以从本质上看,阻塞/非阻塞的差异就在于,如果事情没Ready等待或者不等待。

对于同步/异步而言,当数据Ready了之后,需要应用程序自己去读取,则就是同步。反之,当数据Ready了之后,首先由操作系统帮你读取到了内存,然后直接交付给应用程序使用,这就是异步。所以同步和异步的差异就在于,数据Ready了之后,读取到内存这个动作是不是由操作系统完成的,如果是则是异步,如果不是则是同步。

Netty对3种IO模式的支持

为何Netty仅支持NIO了?

从上面的表格可以看出,对于阻塞IO(OIO/BIO)Netty已经标记为过期,也就是不建议使用了。原因是因为,在高连接数的情况下,阻塞IO会导致消耗资源较高,效率低下。但是对于AIO也被标记为过期了,并且在后续的版本里可能会被移除,其原因有以下几点:

Windows平台实现成熟,但是很少用来做服务器

Linux常用来做服务器,但是AIO实现不够成熟

Linux系统AIO相比较NIO的性能提升不明显

为何Netty有多种NIO实现?

对于Netty而言,BIO和AIO已经不被推荐使用了。通用的NIO实现(Common)在Linux平台上使用的也是epoll,那么为什么还要单独为Linux单独做一套实现呢?具体原因如下:

Netty暴露了更多的可控参数,如:JDK的NIO默认实现是水平触发的,而Netty是边缘触发(默认)和水平触发可切换;Netty实现的垃圾回收更少,性能更高。

NIO一定优于BIO么?

BIO的实现代码相对简单一些,但是在连接少,并发低的场景下,BIO的性能不一定低于NIO。所以在不追求并发的场景下,可以考虑使用BIO。

Netty如何切换IO模式?

从代码实现的角度来看,Netty从NIO切换至OIO只需要将对应实现类的前缀换掉即可,如:NioEventLoopGroup -> OioEventLoopGroup、NioServerSocketChannel -> OioServerSocketChannel 即可。

至于为何可以直接切换实现类就达到切换IO模式,Netty底层使用的是 泛型 + 反射 + 工厂 的模式实现的。

Netty支持的3种Reactor

对于Reactor网络模型的介绍,可以参见《Kafka网络模型基础》。下面我们主要说一下Netty相关的实现。

Reactor网络模型的核心流程就是:注册感兴趣的事件 -> 扫描是否有感兴趣的事件发生 -> 事件发生后做出相应的处理。服务端和客户端的 Channel 可以处理的事件类型如下:


对于在Netty中,不同的网络模式的代码实现如下所示:

Reactor单线程模式

EventLoopGroup eventGroup = new NioEventLoopGroup(1);
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(eventGroup);

这里需要注意的的是 NioEventLoopGroup 指定了1个线程。

非主从Reactor多线程模式

EventLoopGroup eventGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(eventGroup);

主从Reactor多线程模式

EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();

ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup);


参考:《Netty实战》、《极客时间:Netty源码剖析与实战》