Netty核心原理与线程模型

本文深入浅出的介绍了netty基础及高级使用,并通过分析netty源码来理解netty背后的原理。

netty核心原理

netty介绍

官网:https://netty.io/

原生NIO存在的问题
  1. NIO类库和API繁杂,使用麻烦。需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等。

  2. 需要具备其他额外技能:要熟悉Java多线程编程,因为NIO涉及到Reactor模式,必须对多线程和网络编程非常熟悉,才能编写出高质量的NIO程序。

  3. 开发工作量和难度非常大:例如客户端面临短线重连、网络闪断、半包读写、失败缓存、网络拥塞和异常流处理等。

  4. JDK NIO的bug:臭名昭著的Epoll BUG,它会导致Selector空轮询,最终导致CPU100%。直到JDK1.7版本,该问题依然存在,没有被根本解决。

    在NIO中通过Selector轮询当前是否有IO事件。

    根据JDK NIO api的描述,Selector的select()方法会一致阻塞,直到IO事件到达或者超时。但是在Linux平台上有时候会出现问题,在某些场景下select方法会直接返回,这就是臭名昭著的Epoll Bug。

    这是一比较严重的Bug,它会导致线程陷入死循环,让CPU达到100%,极大的影响系统的可靠性,到目前为止,JDK还没有完全解决这个问题。

参考:NIO的epoll空轮询bug

概述

Netty是由JBOSS提供的一款开源框架。

Netty提供异步的、基于事件驱动的网络应用程序框架,用于快速开发高性能、高可靠性的网络IO程序。

Netty是一个基于NIO的网络编程框架,使用NIO可以快速、简单的开发出一个网络应用,极大的简化了NIO的开发过程。

作为当前最流行的NIO框架,Netty在互联网领域、大数据分布式计算领域、游戏行业、通信行业,获得了广泛应用,知名的Elasticsearch、Dubbo等内部都采用了Netty框架。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u4DHvANQ-1651643125578)(https://cdn.jsdelivr.net/gh/terwer/upload/img/image-20220419113521663.png)]

Netty的强大之处:零拷贝、可扩展事件模型,支持TCP、UDP、HTTP、WebSocket等协议,提供安全传输、压缩、大文件传输、编解码等。

Netty有以下优点:

  1. 设计优雅,提供阻塞和非阻塞Socket,提供灵活可扩展的事件模型,提供高度可定制的线程模型。
  2. 具备更高的性能和更大的吞吐量,使用零拷贝技术最小化不必要的内存复制,减少资源的消耗。
  3. 提供安全传输特性。
  4. 支持多种主流协议,预置多种编解码功能,支持用户开发私有协议。

线程模型

线程模型基本介绍

不同的线程模型对性能影响很大,目前存在的线程模型有:

  • 传统阻塞I/O服务模型

  • Reactor模型

    根据Reactor的数量和处理资源池线程的数量不同,可以分为3种

    • 单Reactor单线程
    • 单Reactor多线程
    • 多Reactor多线程
传统阻塞I/O服务模型

采用阻塞I/O模式获取输入的数据,每个连接都需要独立的线程完成数据的输入,业务处理和数据返回工作。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V2KcrRTT-1651643125580)(https://cdn.jsdelivr.net/gh/terwer/upload/img/image-20220419161322076.png)]

存在的问题:

  1. 当并发数很大时,会创建大量线程,占用系统资源。
  2. 连接创建后,如果当前线程没有数据可读,该线程会阻塞在read操作,造成线程资源浪费。
Reactor模型

通过一个或者多个输入传递给服务器的模式,服务端程序处理传入的多个请求,并将他们同步分派到处理线程,Reactor模式也叫Dispatch模式。

Reactor模式使用IO复用监听事件,分发给某个线程(进程),这是网络服务器高并发处理的关键。

  1. 单Reactor单线程

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C7Q1KFa3-1651643125580)(https://cdn.jsdelivr.net/gh/terwer/upload/img/image-20220419181815189.png)]

    • Selector可以实现应用程序通过一个阻塞对象监听多路连接请求
    • Reactor对象通过Selector监控客户端请求事件,收到事件后通过Dispatch进行分发
    • 如果是建立连接请求事件,则由Acceptor通过Accept处理连接请求,然后创建一个handler处理连接完成后的后续业务逻辑
    • Handler会完成Read->业务处理->Send的完整业务流程

    优点:

    模型简单,没有多线程、进程通信、竞争的问题,全部都在一个线程完成。

    缺点

    1. 性能问题

      只有一个线程,无法发挥多核CPU的性能。Handler在处理某个连接上的业务时,整个线程无法处理其他连接事件,容易造成性能瓶劲

    2. 可靠性问题

      线程意外终止或者陷入死循环,会导致整个通信模块不可用,不能接收和处理消息,造成节点故障。

  2. 单Reactor多线程

    image-20220419212849627

    • Reactor对象通过Selector监控客户端请求事件,收到时间后通过Dispatch进行分发
    • 如果建立连接请求,则由Acceptor通过Accept处理请求
    • 如果不是连接请求,则由Reactor分发,调用连接对应的handler进行处理
    • handler只负责响应事件,不做具体的业务处理,通过read读取数据后,会分发给后面的worker线程池的某个线程进行处理
    • worder线程池会分配独立线程完成真正的业务,并将结果返回给handler
    • handler收到响应后,通过send将结果返回给client

    优点

    可以充分利用多核CPU的处理能力

    缺点

    多线程数据共享和访问复杂。

    reactor处理所有事件的监听和响应,在单线程运行,在高并发场景容易出现性能瓶劲

  3. 主从Reactor多线程

    image-20220419213713256

    • Reactor主线程MainReactor对象通过select监听客户端事件,收到事件后,通过Acceptor处理客户端连接事件。

    • 当Acceptor处理完客户端连接事件之后(与客户端建立好socket连接),MainReactor将连接分配给SubReactor。

      MainReactor只负责监听客户端连接请求,和客户端建立连接之后,交由SubReactor监听后面的IO事件。

    • SubReactor将连接加入到自己的连接队列,进行监听,并创建handler对各种事件进行处理。

    • handler通过read从连接中读取数据,将请求数据分发给worker线程,进行业务处理。

    • worder线程池分别独立的线程进行真正的业务处理,并将处理结果返回给handler。

      handler通过send向客户端发送数据。

    • 一个MainReactor可以对应多个SubReactor,即一个MainReactor线程可以对应多个SubReactor线程。

    优点

    1. MainReactor线程与SubReactor线程数据交互简单,职责明确,MainReactor线程只需要接收新连接,SubReactor完成后续的线程处理
    2. MainReactor线程与SubReactor线程数据交互简单,MainReactor线程只需要把新连接传递给SubReactor,SubReactor无需返回数据
    3. 多个SubReactor能够应对高并发的需求

    缺点

    编程复杂度高。

    由于优点明显,多个项目广泛使用。例如Nginx、Mencached、Netty等。

    这种模式也叫做服务器的1+M+N模式,即该模式开发包含1个(或者多个,1代表相对较少)服务器+M个连接建立的线程+N个业务处理线程。

    这是业界成熟的服务器设计模式。

Netty线程模型

Netty的设计主要基于主从Reactor的多线程模式,并做了一定的改进。

  1. 简单版Netty模型

    image-20220419225802316

    • BossGroup线程维护Selector,ServerSocketChannel注册到Selector上,只关注连接请求处理事件(主Reactor)
    • 当接收到来自客户端的连接请求事件时,通过ServerSocketChannel的accept方法获得对应的SocketChannel,并封装成NIOSocketChannel注册到WorkerGroup中的Selector,每个Selector运行在一个线程中(从Selector)
    • 当WorkerGroup中的Selector监听到对应的IO事件后,就调用对应的handler进行处理
  2. 进阶版Netty模型

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qd2CefuS-1651643125582)(https://cdn.jsdelivr.net/gh/terwer/upload/img/image-20220419231353965.png)]

    • 有两组线程池:BossGroup和WorkerGroup,BossGroup中的线程专门负责和客户端建立连接,WorkerGroup中的线程专门负责连接上的读写
    • BossGroup和WorkerGroup含有多个不断循环的执行事件处理的线程,每个线程包含一个Selector,用于监听注册在它上面的Channel
    • 每个BossGroup中的线程循环执行以下三个步骤
      • 轮询注册在其上的ServerSocketChannel的accept事件,OP_ACCEPT事件
      • 处理accept事件,与客户端建立连接,生成一个NIOSocketChannel,并将其注册到WorkerGroup中某个线程的Selector上
      • 以此循环处理任务队列中的下一个事件
    • 每个WorkerGroup中的线程循环执行以下三个步骤:
      • 轮询注册在其上的NIOSocketChannel的read/write事件,OP_READ/OP_WRITE事件
      • 在对应的NIOSocketChannel上处理read/write事件
      • 以此循环处理任务队列中的下一个事件
  3. 详细版Netty模型

    image-20220419233502608

    • Netty抽象出两组线程池:BossGroup和WorkerGroup,也可以叫BossNioEventLoopGroup和WorkerNioEventLoopGroup。

      每个线程池都有NioEventLoop线程。

      BossGroup中的线程专门负责和客户端建立连接,WorkerGroup中的线程专门负责处理连接上的读写。

      BossGroup和WorkerGroup的类型都是NioEventLoopGroup。

    • NioEventLoopGroup相当于一个事件循环组,这个组中含有多个事件循环,每个事件循环就是一个NioEventLoop。

    • NioEventLoop表示一个不断循环的执行事件处理的线程,每个NioEventLoop都包含一个Selector,用于监听注册在其上的socket连接(Channel)。

    • NioEventLoopGroup可以包含多个线程,即含有多个NioEventLoop。

    • 每个BossNioEventLoop循环执行以下三个步骤

      • select:轮询注册在其上的ServerSocketChannel的accept事件,OP_ACCEPT事件
      • processSelectedKeys:处理accept事件,与客户端建立连接,生成一个NIOSocketChannel,并将其注册到WorkerNioEventLoop的Selector上
      • runAllTasks:以此循环处理任务队列中的其他任务
    • 每个WorkerNioEventLoop循环执行以下三个步骤

      • select:轮询注册在其上的NIOSocketChannel的read/write事件,OP_READ/OP_WRITE事件
      • processSelectedKeys:在对应的NIOSocketChannel上处理read/write事件
      • runAllTasks:以此循环处理任务队列中的其他任务
    • 在以上两个processSelectedKeys步骤中,会使用Pipline(管道),Pipline中引用了Channel。

      通过Pipline可以获取对应的Channel,Pipline中维护了很多处理器(拦截处理器、过滤处理器、自定义处理器等)。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值