Netty学习笔记(8)——了解Netty

1. 为什么选择Netty

    NIO(包括AIO)的缺点导致在实际开发中并不能直接运用其进行开发:

  1. NIO 的类库和 API 繁杂,使用麻烦。需要熟练掌握 Selector、ServerSocketChannel、SocketChannel、ByteBuffer 等。
  2. 需要具备其他的额外技能做铺垫。例如熟悉 Java 多线程编程,因为 NIO 编程涉及到 Reactor 模式,你必须对多线程和网路编程非常熟悉,才能编写出高质量的 NIO 程序。
  3. 需要对可靠性能力进行补齐,导致开发工作量和难度都非常大。例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理等等。 NIO 编程的特点是功能开发相对容易,但是可靠性能力补齐工作量和难度都非常大。
  4. JDK NIO 的 Bug。例如臭名昭著的 Epoll Bug,它会导致 Selector 空轮询,最终导致 CPU 100%。 官方声称在 JDK 1.6 版本的 update 18 修复了该问题,但是直到 JDK 1.7 版本该问题仍旧存在,只不过该 Bug 发生概率降低了一些而已,它并没有被根本解决。

    Netty的优势:Netty是基于JDK中的NIO实现的,其对于NIO的API使用进行了封装

  1. API使用简单
  2. 功能强大,预置了多种编解码功能,支持各种主流协议。
  3. 定制能力强,可以通过ChannelHandler进行扩展。
  4. 高性能,吞吐量更高,延迟更低;减少资源消耗;最小化不必要的内存复制。
  5. 成熟稳定,Bug少。

 

2. Reactor线程模型

    Reactor模型实现思想

Reactor 是反应堆的意思,Reactor 模型是指通过一个或多个输入同时传递给服务处理器的服务请求的事件驱动处理模式。服务端程序处理传入多路请求,并将它们同步分派给请求对应的处理线程,Reactor 模式也叫 Dispatcher 模式,即 I/O 多路复用统一监听事件,收到事件后分发(Dispatch 给某进程),是编写高性能网络服务器的必备技术之一。

Reactor 依赖于一个事件多路分离器(Event Demultiplexer)。分离器对象可将来自事件源的I/O事件分离出来,并分发到对应的read/write事件处理器(Event Handler)。开发人员预先注册需要处理的事件及其事件处理器(或回调函数);事件分离器负责将请求事件传递给事件处理器。

实际上Reactor就相当于NIO中的Selector差不多,将SocketChannel或者ServerSocketChannel注册到Selector中,并且在该通道上绑定感兴趣的IO事件(可读、可写等),然后调用Selector中的select方法获取可用的Channel通道进行读写操作。

可以这样理解,Reactor 就是一个执行 while (true) { selector.select(); …} 循环的线程,会源源不断的产生新的事件。

    Reactor中5个重要的参与者

e34b53b55328e5662c2e3e18191800ad468.jpg

  1. 描述符(Handle):由操作系统提供,用于识别每一个事件,如Socket描述符、文件描述符等。在Linux中,它用一个整数来表示。事件可以来自外部,来自客户端的连接请求、数据读写等。事件也可以来自内部,如定时器事件。
  2. 同步事件多路分解器(Synchronous Event Demultiplexer):由操作系统内核实现的一个函数(如select/epoll),用于阻塞等待发生在Handle集合上的一个或多个事件,是一个函数,用来等待一个或多个事件的发生。调用者会被阻塞,直到分离器分离的描述符集上有事件发生。相当于NIO中的Selector的select方法。
  3. 事件处理器接口(Event handler):是由一个或多个模板函数组成的接口。这些模板函数描述了和应用程序相关的对某个事件的操作。
  4. 具体的事件处理器(Concrete Event HandlerA):实现应用程序所提供的特定事件处理逻辑;是事件处理器接口的实现。它实现了应用程序提供的某个服务。每个具体的事件处理器总和一个描述符相关。它使用描述符来识别事件、识别应用程序提供的服务。(其实就相当于把读写操作的具体实现封装起来)
  5. Reactor管理器(Reactor):定义了一些接口,用于应用程序控制事件调度,以及应用程序注册、删除事件处理器和相关的描述符。它是事件处理器的调度核心。Reactor管理器使用同步事件分离器来等待事件的发生。一旦事件发生,Reactor管理器先是分离每个事件,然后调度事件处理器,最后调用相关的模板函数来处理这个事件。是Reactor管理器而不是应用程序负责等待事件、分离事件和调度事件。实际上,Reactor管理器并没有被具体的事件处理器调用,而是管理器调度具体的事件处理器,由事件处理器对发生的事件做出处理。这就是类似Hollywood原则的“反向控制”。应用程序要做的仅仅是实现一个具体的事件处理器,然后把它注册到Reactor管理器中。接下来的工作由管理器来完成。

    通过Reactor的操作过程

a39482eefb01e4314716316e44c569e0a97.jpg

    总结下来就是三步

  1. 应用启动,将关注的事件handle注册到Reactor中; 
  2. 调用Reactor,进入无限事件循环,等待注册的事件到来; 
  3. 事件到来,select返回,Reactor将事件分发到之前注册的回调函数中处理;

    2.1 单Reactor的单线程模型

é«æ§è½ç½ç»ç¼ç¨(å­)ï¼ä¸æ读æé«æ§è½ç½ç»ç¼ç¨ä¸­ç线ç¨æ¨¡å_3.jpeg

其中,Select 是前面 I/O 复用模型介绍的标准网络编程 API,可以实现应用程序通过一个阻塞对象监听多路连接请求,其他方案示意图类似。
方案说明:

Reactor单线程模型,指的是所有的I/O操作都在同一个NIO线程上面完成,NIO线程的职责如下:

  1. 作为NIO服务端,接收客户端的TCP连接;
  2. 作为NIO客户端,向服务端发起TCP连接;
  3. 读取通信对端的请求或者应答消息;
  4. 向通信对端发送消息请求或者应答消息;

详细图如下所示

635249-20180512223434832-507865796.png
优点:模型简单,没有多线程、进程通信、竞争的问题,全部都在一个线程中完成。
缺点:性能问题,只有一个线程,无法完全发挥多核 CPU 的性能。Handler 在处理某个连接上的业务时,整个进程无法处理其他连接事件,很容易导致性能瓶颈。

可靠性问题,线程意外跑飞,或者进入死循环,会导致整个系统通信模块不可用,不能接收和处理外部消息,造成节点故障。

使用场景:客户端的数量有限,业务处理非常快速,比如 Redis,业务处理的时间复杂度 O(1)。

 

    2.2 单Reactor多线程模型

é«æ§è½ç½ç»ç¼ç¨(å­)ï¼ä¸æ读æé«æ§è½ç½ç»ç¼ç¨ä¸­ç线ç¨æ¨¡å_4.jpeg

635249-20180512223404148-511529933.png

方案说明:

1)Reactor 对象通过 Select 监控客户端请求事件,收到事件后通过 Dispatch 进行分发;

2)如果是建立连接请求事件,则由Acceptor通过 Accept 处理连接请求,然后创建一个 Handler 对象处理连接完成后续的各种事件,比如将建立的连接注册到Reactor中;

3)如果不是建立连接事件,则 Reactor 会分发调用连接对应的 Handler 来响应;

4)Handler 只负责响应事件,不做具体业务处理,通过 Read 读取数据后,会分发给后面的 Worker 线程池进行业务处理;

5)Worker 线程池会分配独立的线程完成真正的业务处理,将响应结果发给 Handler 进行处理;

6)Handler 收到响应结果后通过 Send 将响应结果返回给 Client。

Reactor多线程模型与单线程模型最大区别就是有一组NIO线程处理I/O操作,它的特点如下:

  1. 有一个专门的NIO线程——acceptor用于监听服务端,接收客户端的TCP连接请求;
  2. 网络I/O操作--读、写等由一个NIO线程池负责,线程池可以采用标准的JDK线程池实现,它包含一个任务队列和N个可用的线程,由这些NIO线程负责消息的读取、解码、编码和发送;
  3. 1个NIO线程可以同时处理N条链路,但是1个链路只对应1个NIO线程,防止发生并发操作问题。

优点:可以充分利用多核 CPU 的处理能力。
缺点:多线程数据共享和访问比较复杂;只由一个Reactor承担所有事件的监听和响应,在单线程中运行,高并发场景下容易成为性能瓶颈。

 

    2.3 主从Reactor多线程模型

é«æ§è½ç½ç»ç¼ç¨(å­)ï¼ä¸æ读æé«æ§è½ç½ç»ç¼ç¨ä¸­ç线ç¨æ¨¡å_5.jpeg

635249-20180512223506384-1386558070.png

针对单 Reactor 多线程模型中,Reactor 在单线程中运行,高并发场景下容易成为性能瓶颈,可以让Reactor 在多线程中运行。
方案说明:

1)Reactor 主线程 MainReactor 对象通过 Select 监控建立连接事件,收到事件后通过 Acceptor 接收,处理建立连接事件;

2)Acceptor 处理建立连接事件后,MainReactor 将连接分配 Reactor 子线程给 SubReactor 进行处理;

3)SubReactor 将连接加入连接队列进行监听,并创建一个 Handler 用于处理各种连接事件;

4)当有新的事件发生时,SubReactor 会调用连接对应的 Handler 进行响应;

5)Handler 通过 Read 读取数据后,会分发给后面的 Worker 线程池进行业务处理;

6)Worker 线程池会分配独立的线程完成真正的业务处理,如何将响应结果发给 Handler 进行处理;

7)Handler 收到响应结果后通过 Send 将响应结果返回给 Client。

服务端用于接收客户端连接的不再是1个单独的NIO线程,而是一个独立的NIO线程池。Acceptor接收到客户端TCP连接请求处理完成后(可能包含接入认证等),将新创建的SocketChannel注册到I/O线程池(sub reactor线程池)的某个I/O线程上,由它负责SocketChannel的读写和编解码工作。

Acceptor线程池只用于客户端的登录、握手和安全认证,一旦链路建立成功,就将链路注册到后端subReactor线程池的I/O线程上,有I/O线程负责后续的I/O操作。

第三种模型比起第二种模型,是将Reactor分成两部分:

mainReactor负责监听server socket,accept新连接,并将建立的socket分派给subReactor。

subReactor负责多路分离已连接的socket,读写网 络数据,对业务处理功能,其扔给worker线程池完成。通常,subReactor个数上可与CPU个数等同。

优点:父线程与子线程的数据交互简单职责明确,父线程只需要接收新连接,子线程完成后续的业务处理。
父线程与子线程的数据交互简单,Reactor 主线程只需要把新连接传给子线程,子线程无需返回数据。
这种模型在许多项目中广泛使用,包括 Nginx 主从 Reactor 多进程模型,Memcached 主从多线程,Netty 主从多线程模型的支持。

    2.4 总结

    3 种模式可以用个比喻来理解:(餐厅常常雇佣接待员负责迎接顾客,当顾客入坐后,侍应生专门为这张桌子服务)
1)单 Reactor 单线程,接待员和侍应生是同一个人,全程为顾客服务;

2)单 Reactor 多线程,1 个接待员,多个侍应生,接待员只负责接待,由接待员负责为顾客安排对应的侍应生;

3)主从 Reactor 多线程,多个接待员,多个侍应生。

 

3. Proactor线程模型

    模型实现思想

    在 Reactor 模式中,Reactor 等待某个事件或者可应用或者操作的状态发生(比如文件描述符可读写,或者是 Socket 可读写)。然后把这个事件传给事先注册的 Handler(事件处理函数或者回调函数),由后者来做实际的读写操作。其中的读写操作都需要应用程序同步操作,所以Reactor是非阻塞同步网络模型。
    如果把 I/O 操作改为异步,即交给操作系统来完成就能进一步提升性能,这就是异步网络模型 Proactor。

é«æ§è½ç½ç»ç¼ç¨(å­)ï¼ä¸æ读æé«æ§è½ç½ç»ç¼ç¨ä¸­ç线ç¨æ¨¡å_1.jpeg

 

  • 1)Proactor Initiator 创建 Proactor 和 Handler 对象,并将 Proactor 和 Handler 都通过 AsyOptProcessor(Asynchronous Operation Processor)注册到内核;
  • 2)AsyOptProcessor 处理注册请求,并处理 I/O 操作;
  • 3)AsyOptProcessor 完成 I/O 操作后通知 Proactor;
  • 4)Proactor 根据不同的事件类型回调不同的 Handler 进行业务处理;
  • 5)Handler 完成业务处理。

 

4.  Reactor与Proactor的总结

    两者优点

Reactor实现相对简单,对于耗时短的处理场景处理高效;

操作系统可以在多个事件源上等待,并且避免了多线程编程相关的性能开销和编程复杂性;

事件的串行化对应用是透明的,可以顺序的同步执行而不需要加锁;

事务分离:将与应用无关的多路分解和分配机制和与应用相关的回调函数分离开来。

Proactor性能更高,能够处理耗时长的并发场景;

    两者缺点

Reactor处理耗时长的操作会造成事件分发的阻塞,影响到后续事件的处理;

Proactor实现逻辑复杂;依赖操作系统对异步的支持,目前实现了纯异步操作的操作系统少,实现优秀的如windows IOCP,但由于其windows系统用于服务器的局限性,目前应用范围较小;而Unix/Linux系统对纯异步的支持有限,应用事件驱动的主流还是通过select/epoll来实现;

    适用场景

Reactor:同时接收多个服务请求,并且依次同步的处理它们的事件驱动程序;
Proactor:异步接收和同时处理多个服务请求的事件驱动程序;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值