七.Netty入门到超神系列-Netty介绍和线程模型

前言

千呼万唤始出来,经过5章的NIO学习,终于迎来了Netty,本章主要是对Netty做一个介绍和Netty的线程模型做一个分析。撸起袖子,准备好卫生纸,一起来看吧。

七.Netty入门到超神系列-Netty介绍和线程模型_第1张图片

Java NIO的问题

经过前面章节的学习你应该能感受到NIO的问题,就是类比较多,方法也比较多,而且复杂,开发工作量和难度都非常大,还需要考虑网络问题、数据丢包和异常流的处理等等。

NIO是底层API,它的实现依赖于操作系统针对IO操作的APIs,使用NIO会经常发现代码在Linux上正常运行,但在Windows上就会出现问题。JDK的NIO还有一个Epoll Bug,它会导致 Selector 空轮询,最终导致 CPU 100%。

总之直接使用Java NIO会面临一系列的问题,Netty的其出现就是为了解决NIO的不足

Netty的介绍

下面是从Netty官网趴下来的对Netty的定义

Netty是由JBOSS提供的一个java开源框架,Netty 是 一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。Netty 是一个 NIO 客户端服务器框架,它可以快速轻松地开发网络应用程序,例如协议服务器和客户端。它极大地简化和精简了 TCP 和 UDP 套接字服务器等网络编程。

简单理解:Netty是基于NIO(Nonblocking I/O,非阻塞IO)实现的网络通信框架,相比传统IO他的并发性能得到了很大提高,它在NIO基础上封装了NIO的使用细节,让网络编程变得更加高效简洁,大大简化了NIO的开发过程。

Netty拥有高性能、高吞吐量、低延迟、消耗资源少;零拷贝的特点,同时支持SSL/TLS 和 StartTLS ,社区活跃、版本迭代周期短。

Netty是互联网最流行的NIO框架,广泛应用于互联网领域,游戏领域,大数据领域,比较知名的ElasticSearch,Dubbo等技术都用到了Netty。

Netty的线程模型

线程模型的学习对于理解Netty是比不可少的,我们从线程模型的演变一步一步来讲解

BIO线程模型

首选是传统的BIO线程模型,这个在第一章我们有过了解,它的特点是:

  • 一个请求需要创建一个线程去处理
  • 如果read不到数据,线程会阻塞

七.Netty入门到超神系列-Netty介绍和线程模型_第2张图片
所以这种线程模型导致的问题就是

  • 高并发场景下会频繁创建很多线程,频繁的创建和销毁线程对系统性能损耗非常大
  • read不到数据线程进行阻塞,这导致线程资源的浪费

Reactor线程模型

Reactor(也叫Dispacher)线程模型不再为每个请求创建线程 ,而是基于 I/O 复用模型,多个请求连接同一个阻塞对象,应用程序只需要在一个阻塞对象等待,无需阻塞等待所有连接。当请求中有数据,操作系统通知应用程序,线程从阻塞状态返回,开始进行业务处理,一个线程可以处理多个连接的业务,如图:
七.Netty入门到超神系列-Netty介绍和线程模型_第3张图片
先来理解两个概念

  • Reactor:即图中的ServiceHandler,Reactor负责监听和事件分发,分发给适当的处理程序来对 IO 事件做出反应, 在单独的线程中执行。就像公司的电话接线员,它接听来自客户的电话并将线路转移到适当的联系人;
  • Handlers:Reactor监听到IO事件,需要调度应用程序来处理即:Handler,Handler是真正处理 I/O 事件的程序,类似于接电话的实际的人员。

简单理解就是Reactor线程模型有两个角色 ,一个负责接待请求(Reactor), 一个负责处理请求(Handlers)

Reactor单线程

另外Reactor有三种模式 :Reactor 单线程模型 、单 Reactor 多线程模型 、
Reactor主从多线程模型 。Netty框架是基于主从Reactor多线程模型进行改进(多个主Reactor)。 下面是Reactor单线程模型,如图:
七.Netty入门到超神系列-Netty介绍和线程模型_第4张图片
Reactor单线程模型工作流程如下

  1. Reactor 通过 Select 监控客户端请求,收到事件后通过 Dispatch 进行请求分发
  2. 如果是请求事件是建立连接,则由 Acceptor 通过 accept 处理连接请求,然后创建一个 Handler 。
  3. 如果不是连接事件,则 Reactor 会分发调用连接对应的 Handler 来处理请求
  4. Handler 会完成数据read,进行业务处理最后通过 Send 将响应结果返回给 Client

Reactor单线程模型优点是模型简单,缺点也是比较明显

  • 线程单一,如果并发高,业务处理慢,很容易导致性能瓶颈,
  • 如果线程故障或终止,整个系统不可用

所以这种模型不太适合高并发场景,和业务处理比较耗时的场景。

Reactor多线程

接下来看一下单Reactor 多线程模型 ,先看图把:
七.Netty入门到超神系列-Netty介绍和线程模型_第5张图片
Reactor多线程模式工作流程如下

  1. Reactor 通过 Select 监控客户端请求,收到事件后通过 Dispatch 进行请求分发
  2. 如果是请求事件是建立连接,则由 Acceptor 通过 accept 处理连接请求,然后创建一个 Handler 。
  3. 如果不是连接事件,则 Reactor 会分发调用连接对应的 Handler 来处理请求
  4. Handler 只负责响应事件,不做具体业务处理,通过 Read 读取数据后,会分发给后面的 Worker 线程池进行业务处理
  5. Worker 线程池会分配独立的线程完成真正的业务处理,将响应结果发给 Handler 进行处理。
  6. Handler 收到响应结果后通过 Send 将响应结果返回给 Client。

这种模型引入了线程池,Reactor线程负责接收连接和响应事件,具体IO事件交给线程池分配的线程处理。优点是可以充分利用CPU,提供整体性能,但是Reactor承担所有的事件监听和分发,容易成为性能瓶颈,多线程的数据共享也是一个问题。

主从Reactor多线程

单Reactor多线程的问题是Reactor在单线程中执行可能会成为瓶颈,那么主从Reactor多线程就解决了这个问题 ,看图:
七.Netty入门到超神系列-Netty介绍和线程模型_第6张图片
主从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

这种模式的优点比较明显,Reactor主线程只需要接收新连接,子线程完成后续的业务处理,Reactor的压力得到了分担。这种模型在许多项目中广泛使用,包括 Nginx 主从 Reactor 多进程模型,Memcached 主从多线程,Netty 主从多线程模型的支持。

Netty的线程模型

Netty是基于主从Reactor多线程模型实现,Netty的所有IO操作都是异步非阻塞的,通过Future-Listener机制,用户可以方便的主动获取或者通过通知机制获得IO操作结果,Netty线程模型如下图:
七.Netty入门到超神系列-Netty介绍和线程模型_第7张图片

Netty线程模型中有两个Group,分别是Boss Group和Worker Group,的BossGroup 用于Accetpt连接建立事件并分发请求,workerGroup用于处理I/O读写事件和业务逻辑。其中关键组件如下

  • Channel

    Netty网络通信的组件,能够用于执行网络IO操作。

  • Selector

    Netty基于Selector对象实现I/O多路复用,即:一个线程可以监听多个连接的Channel事件, 当channel向Selector注册 后,Selector 就可以自动不断地查询(select) 注册的Channel是否有已就绪的I/O事件(例如可读, 可写, 网络连接完成等)。

  • NioEventLoop

    NioEventLoop表示一个不断循环的执行处理任务的线程, 每个NioEventLoop 都有一个 selector。用于监听注册的Channel的IO事件。

    NioEventLoop中维护一个线程和任务队列,支持异步提交任务,线程启动时会调用NioEventLoop的run方法,执行IO和非IO任务。 其中IO任务由prossSelectedKeys触发,非IO任务由runAlltasks触发。

  • NioEventLoopGroup

    NioEventLoopGroup用来管理EventLoop,可以理解为线程组,内部维护了一组线程

Netty线程模型工作流程如下:

  1. Boss Group里面的NioEventLoop会轮询accept事件,遇到有新的连接,就生成NioSocketChannel,并把这个Channel注册到Worker Group的Selector上
  2. Worker Group轮询read和write事件,在可读或者可写条件满足时,就进行处理

Worker Group和Boss Group都是通过里面的NioEventLoop来操作的。NioEventLoop中维护了一个线程和任务队列,支持异步提交执行任务,线程启动时会调用NioEventLoop的run方法

每个Boss NioEventLoop循环执行的步骤有3步

  1. 轮询检查accept事件
  2. 处理accept事件,与client建立连接, 生成NioSocketChannel, 并将其注册到某个 Worker NIOEventLoop上的 selector ,这样Channel的IO事件就交给WorkGroup去监听了。
  3. 最后执行一个runAllTasks方法,用于处理任务队列中的任务

每个 Worker NIOEventLoop 循环执行的步骤:

  1. 轮询read, write 事件
  2. 处理 I/O 事件, 即 read, write 事件, 再对应NioSocketChannel 处理
  3. 最后执行一个runAllTasks方法,用于处理任务队列中的任务

总结

本篇文件的内容主要介绍了一下Netty,以及三种 Reactor 线程模型,以及Netty的线程模型,Netty的线程模型主要是基于主从Reactor多线程模型升级,在下一章节我们来实战Netty。

你可能感兴趣的:(《Netty入门到超神》,netty)