Netty架构原理

2020-10-14  本文已影响0人  爱健身的兔子

前言

Netty是一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。

Netty与java NIO的区别

java NIO的不足

  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修复了该问题,但是直到JDK1.7版本该问题仍旧存在,只不过该Bug发生概率降低了一些而已,它并没有被根本解决

Netty的特点

  1. 设计优雅,适用于各种传输类型的统一API阻塞和非阻塞Socket;基于灵活且可扩展的事件模型,可以清晰地分析关注点;高度可定制的线程模型-单线程,一个或多个线程池;真正的无连接数据报套接字支持
  2. 使用方便,详细记录的Javadoc,用户指南和示例;没有其他依赖项,JDK5(Netty3.x)或6 (Netty4.x) 就足够了
  3. 高性能,吞吐量更高,延迟更低;减少资源消耗;最小化不必要的内存复制
  4. 安全,完整的SSL/TLS和StartTLS支持
  5. 社区活跃,不断更新,社区活跃,版本迭代周期短,发现的Bug可以被及时修复,同时,更多的新功能会被加入

Netty的核心组件

Nettty 有如下几个核心组件:

Channel

Channel 是 Netty 网络操作抽象类,封装了java nio的channel,它除了包括基本的 I/O 操作,如 bind、connect、read、write 之外,还包括了 Netty 框架相关的一些功能,如获取该 Channel的 EventLoop。Channel 具有如下优势:

EventLoop

Netty 基于事件驱动模型, EventLoop 定义了Netty的核心抽象,用来处理连接的生命周期中所发生的事件,在内部,将会为每个Channel分配一个EventLoop。

EventLoop本身只由一个线程驱动,其处理了一个Channel的所有I/O事件,并且在该EventLoop的整个生命周期内都不会改变。这个简单而强大的设计消除了你可能有的在ChannelHandler实现中需要进行同步的任何顾虑。

这里需要说到,EventLoop的管理是通过EventLoopGroup来实现的。还要一点要注意的是,客户端引导类是 Bootstrap,只需要一个EventLoopGroup。服务端引导类是 ServerBootstrap,通常需要两个 EventLoopGroup,一个用来接收客户端连接,一个用来处理 I/O 事件(也可以只使用一个 EventLoopGroup,此时其将在两个场景下共用同一个 EventLoopGroup)。

当一个连接到达时,Netty 就会注册一个 Channel,然后从 EventLoopGroup 中分配一个 EventLoop 绑定到这个Channel上,在该Channel的整个生命周期中都是有这个绑定的 EventLoop 来服务的。

ChannelFuture

Netty 为异步非阻塞,即所有的 I/O 操作都为异步的,因此,我们不能立刻得知消息是否已经被处理了。Netty 提供了 ChannelFuture 接口,通过该接口的 addListener() 方法注册一个 ChannelFutureListener,当操作执行成功或者失败时,监听就会自动触发返回结果。

ChannelHandler

ChannelHandler 为 Netty 中最核心的组件,它充当了所有处理入站和出站数据的应用程序逻辑的容器。ChannelHandler 主要用来处理各种事件,这里的事件很广泛,比如可以是连接、数据接收、异常、数据转换等。

ChannelHandler 有两个核心子类 ChannelInboundHandler 和 ChannelOutboundHandler,其中 ChannelInboundHandler 用于接收、处理入站数据和事件,而 ChannelOutboundHandler 则相反。

ChannelPipeline

ChannelPipeline 为 ChannelHandler 链提供了一个容器并定义了用于沿着链传播入站和出站事件流的 API。一个数据或者事件可能会被多个 Handler 处理,在这个过程中,数据或者事件经流 ChannelPipeline,由 ChannelHandler 处理。在这个处理过程中,一个 ChannelHandler 接收数据后处理完成后交给下一个 ChannelHandler,或者什么都不做直接交给下一个 ChannelHandler。

当一个数据流进入 ChannlePipeline 时,它会从 ChannelPipeline 头部开始传给第一个 ChannelInboundHandler ,当第一个处理完后再传给下一个,一直传递到管道的尾部。与之相对应的是,当数据被写出时,它会从管道的尾部开始,先经过管道尾部的 “最后” 一个ChannelOutboundHandler,当它处理完成后会传递给前一个 ChannelOutboundHandler

当 ChannelHandler 被添加到 ChannelPipeline 时,它将会被分配一个ChannelHandlerContext,它代表了 ChannelHandler 和 ChannelPipeline 之间的绑定。其中 ChannelHandler 添加到 ChannelPipeline 过程如下:

  1. 一个 ChannelInitializer 的实现被注册到了 ServerBootStrap中
  2. 当 ChannelInitializer.initChannel() 方法被调用时,ChannelInitializer 将在 ChannelPipeline 中安装一组自定义的 ChannelHandler
  3. ChannelInitializer 将它自己从 ChannelPipeline 中移除

Netty的IO模型

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

Reactor模型中有2个关键组成:

  1. Reactor,在一个单独的线程中运行,负责监听和分发事件,分发给适当的处理程序来对IO事件作出反应。
  2. Handlers,处理程序执行IO事件要完成的实际事件,reactor通过调用适当的处理程序来响应IO事件,处理程序执行非阻塞操作

由Reactor的数量和Handler线程数量的不同,Reactor模型有3个变种:

  1. 单reactor 单线程(一个线程既当Reactor,又当Handler)
  2. 单reactor 多线程 (一个线程当Reactor,多个线程当Handler)
  3. 主从reactor 多线程 (多个个线程当Reactor,多个线程当Handler)

Netty线程模型

Netty主要是基于主从Reactors多线程模型(如下图)做了一些修改,其中主从reactor多线程模型有多个reactor:

  1. MainReactor负责客户端的连接请求,并将请求转交给SubReactor
  2. SubReactor负责相应通道的IO读写请求
  3. 非IO请求(具体逻辑处理)的任务则会直接进入写入队列,等到worker threads进行处理

主从Reactor多线程模型的图:


特别说明的是:虽然Netty的线程模型基于主从Reactor多线程,借用了MainReactor和SubReactor的结构。但是实际实现上SubReactor和Worker线程在同一个线程池中

  1. bossGroup线程池则只是在bind某个端口后,获得其中一个线程作为MainReactor,专门处理端口的Accept事件,每个端口对应一个boss线程
  2. workerGroup线程池会被各个SubReactor和Worker线程充分利用

【死磕Netty】-----Netty的核心组件_chenssy的博客-CSDN博客

Netty原理架构解析 - shoshana~ - 博客园

上一篇下一篇

猜你喜欢

热点阅读