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

简介: 经过前面章节的学习你应该能感受到NIO的问题,就是类比较多,方法也比较多,而且复杂,开发工作量和难度都非常大,还需要考虑网络问题、数据丢包和异常流的处理等等。NIO是底层API,它的实现依赖于操作系统针对IO操作的APIs,使用NIO会经常发现代码在Linux上正常运行,但在Windows上就会出现问题。JDK的NIO还有一个Epoll Bug,它会导致 Selector 空轮询,最终导致 CPU 100%。总之直接使用Java NIO会面临一系列的问题,Netty的其出现就是为了解决NIO的不足

前言

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

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不到数据,线程会阻塞

所以这种线程模型导致的问题就是

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

Reactor线程模型

Reactor(也叫Dispacher)线程模型不再为每个请求创建线程 ,而是基于 I/O 复用模型,多个请求连接同一个阻塞对象,应用程序只需要在一个阻塞对象等待,无需阻塞等待所有连接。当请求中有数据,操作系统通知应用程序,线程从阻塞状态返回,开始进行业务处理,一个线程可以处理多个连接的业务,如图:

先来理解两个概念

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

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

Reactor单线程

另外Reactor有三种模式 :Reactor 单线程模型 、单 Reactor 多线程模型 、

Reactor主从多线程模型 。Netty框架是基于主从Reactor多线程模型进行改进(多个主Reactor)。 下面是Reactor单线程模型,如图:

Reactor单线程模型工作流程如下

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

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

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

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

Reactor多线程

接下来看一下单Reactor 多线程模型 ,先看图把:

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多线程就解决了这个问题 ,看图:

主从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线程模型中有两个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。

目录
相关文章
|
2月前
|
安全 数据处理 开发者
Python中的多线程编程:从入门到精通
本文将深入探讨Python中的多线程编程,包括其基本原理、应用场景、实现方法以及常见问题和解决方案。通过本文的学习,读者将对Python多线程编程有一个全面的认识,能够在实际项目中灵活运用。
|
1月前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
1月前
|
消息中间件 编解码 网络协议
Netty从入门到精通:高性能网络编程的进阶之路
【11月更文挑战第17天】Netty是一个基于Java NIO(Non-blocking I/O)的高性能、异步事件驱动的网络应用框架。使用Netty,开发者可以快速、高效地开发可扩展的网络服务器和客户端程序。本文将带您从Netty的背景、业务场景、功能点、解决问题的关键、底层原理实现,到编写一个详细的Java示例,全面了解Netty,帮助您从入门到精通。
148 0
|
2月前
|
并行计算 JavaScript 前端开发
单线程模型
【10月更文挑战第15天】
|
2月前
|
算法 NoSQL Java
Springboot3新特性:GraalVM Native Image Support和虚拟线程(从入门到精通)
这篇文章介绍了Spring Boot 3中GraalVM Native Image Support的新特性,提供了将Spring Boot Web项目转换为可执行文件的步骤,并探讨了虚拟线程在Spring Boot中的使用,包括如何配置和启动虚拟线程支持。
138 9
Springboot3新特性:GraalVM Native Image Support和虚拟线程(从入门到精通)
|
1月前
|
安全 Java 调度
Java中的多线程编程入门
【10月更文挑战第29天】在Java的世界中,多线程就像是一场精心编排的交响乐。每个线程都是乐团中的一个乐手,他们各自演奏着自己的部分,却又和谐地共同完成整场演出。本文将带你走进Java多线程的世界,让你从零基础到能够编写基本的多线程程序。
37 1
|
1月前
|
Java 数据处理 开发者
Java多线程编程的艺术:从入门到精通####
【10月更文挑战第21天】 本文将深入探讨Java多线程编程的核心概念,通过生动实例和实用技巧,引导读者从基础认知迈向高效并发编程的殿堂。我们将一起揭开线程管理的神秘面纱,掌握同步机制的精髓,并学习如何在实际项目中灵活运用这些知识,以提升应用性能与响应速度。 ####
51 3
|
2月前
|
Java
Java中的多线程编程:从入门到精通
本文将带你深入了解Java中的多线程编程。我们将从基础概念开始,逐步深入探讨线程的创建、启动、同步和通信等关键知识点。通过阅读本文,你将能够掌握Java多线程编程的基本技能,为进一步学习和应用打下坚实的基础。
|
2月前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
25 1
|
2月前
|
NoSQL Redis 数据库
Redis单线程模型 redis 为什么是单线程?为什么 redis 单线程效率还能那么高,速度还能特别快
本文解释了Redis为什么采用单线程模型,以及为什么Redis单线程模型的效率和速度依然可以非常高,主要原因包括Redis操作主要访问内存、核心操作简单、单线程避免了线程竞争开销,以及使用了IO多路复用机制epoll。
60 0
Redis单线程模型 redis 为什么是单线程?为什么 redis 单线程效率还能那么高,速度还能特别快