Netty一文搞懂入门篇<随手笔记>

在这里插入图片描述

1.Java IO的读写原理

IO是Input和Output的缩写,即输入和输出。用户程序进行IO的读写基本上会用到readwrite两大系统调用。

read把数据从内核缓冲区复制到进程缓冲区,write是把数据从进程缓冲区复制到内核缓冲区。 这两大系统的调用都不负责数据在内核换中去和磁盘之间的交换,底层读写交换是由操作系统kernel内核完成的

1.1.内核缓冲区和进程缓冲区

  • 目的: 减少频繁的系统IO调用。因为系统调用需要保存之前的进程数据和状态等信息,而结束调用之后回来还需要恢复之前的信息,为了减少这种损耗时间、也损耗性能的系统调用,于是出现了缓冲区。

    在linux系统中,系统内核中有个缓冲区叫做内核缓冲区;每个进程有自己独立的缓冲区叫做进程缓冲区

  • 实际: 有了缓冲区,操作系统使用read函数和write函数就是对数据在内核缓冲区和进程缓冲区之间的操作。等待缓冲区达到一定的数量时,再进行IO的调用以提升性能。读取和存储的时机由内核来决定,所以用户程序的IO读写大多数情况下并没有进行实际的IO操作,而是在读写自己的进程缓冲区。

1.2.四种主要的IO模型

1.2.1.同步阻塞IO(BIO)

同步阻塞IO即Blocking IO,在linux的java进程中,默认所有的socket都是BIO。在阻塞式IO模型中,应用程序在从IO系统盗用开始一直到系统调用返回,这段时间是阻塞的。返回成功后应用进程开始处理用户空间的缓存数据。

  • 优点: 程序 简单,在阻塞等待数据期间,用户相乘挂起,用户线程基本上不会占用CPU资源。
  • 缺点: 一般会为每个连接配套一条独立的线程,或者说一条线程维护一个连接成功的IO流读写。在并发量较大时,需要大量的线程来维护大量的网络连接、内存,线程切换开销非常巨大。因此BIO在高并发场景下是不可用的。

1.2.2.同步非阻塞IO(NIO非java)

即None Blocking-IO,在linux系统下,可以通过设置socket使其变为non-blocking,应用程序的线程则不断的进行IO系统的调用,轮询数据是否已经准备好,直到数据准备好为止。

  • 优点: 每次发起IO,在内核的等待时间中可以立即返回。用户线程不会阻塞,实用性较好。
  • 缺点: 需不断的重复发起IO,这种轮询会占用大量的CPU时间,系统资源利用率较低。

Java NIO(New IO)不是IO模型中的NIO,而是IO多路复用模型

1.2.3.IO多路复用模型

即IO multiplexing,核心是为了避免NIO中的轮询等待问题。通过一种新的系统调动,一个进程可以监视多个文件描述符,一旦某个描述符就绪(一般为内核缓冲区可读/写),内核kernel能够通知程序进行相应的IO系统调用。

  • 支持IO多复路的系统调用:
    1. select:是目前几乎所有操作系统上都支持,具有良好跨平台特性。
    2. epoll:是linux2.6内核中提出的,是select系统在linux中的增强版。
  • 基本原理:
    单个线程不断轮询select/epoll系统来调用所负责的成百上千的socket连接,当某个或某些socket网络连接有数据到达了就返回这些可以读写的连接。
  • 优势:
    通过一次select/epoll系统的调用就可查询到可以读写的一个甚至是成百上千的网络连接。

1.2.4.异步IO模型(AIO)

即asynchronous IO。

  • 基本流程:
    用户线程通过系统调用告知kernel内核启动某个IO操作,用户线程返回。kernel内核在整个IO操作完成后通知用户程序,用户执行后续的业务操作。
  • 特点:
    在内核kernel的等待数据和复制的两个阶段,用户线程都不是block(堵塞)的。用户线程需要接受kernel的IO操作完成的事件或者说注册IO操作完成的回调函数到操作系统的内核。异步IO有时也叫信号驱动IO。
  • 缺点:
    需要完成时间的注册与传递,这里面需要底层操作系统提供大量的支持。windows系统下通过IOCP实现了真正的异步IO。linux系统在2.6版才引入,目前不完善,所以在linux下高并发网络编程都以IO复用模型为主。

2.Netty概述

Netty是一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务端和客户端。对JDK自带的NIO的API进行了封装解决了上述问题。且具有高性能、高吞吐量、低延迟、低资源消耗、最小化不必要的内存复制等优点。

2.1.Reactor线程模式

根据Reactor的数量和处理资源的线程数不通分为:Reactor单线程、Reactor多线程、主从Reactor多线程Netty线程模式是基于主从Reactor多线程做了改进。

基于传统堵塞IO模型做了一下两种改进:

  1. 基于IO复用模型
  2. 基于线程池复用线程资源

Reactor模式的核心组成:

  1. Reactor:reactor就是多个客户端共用一个阻塞对象,它单独起一个线程运行,负责监听和分发事件,将请求分发给适当的处理程序进行处理。
  2. Handler:处理程序要完成的实际事件,也就是真正执行业务逻辑的程序,它是非阻塞的。

2.1.1.单线程Reactor

多个客户端请求连接,然后Reactor通过selector轮询判断那些通道是有事件发生的,如果是连接事件就到了Acceptor中建立连接;如果是其他读写事件就由dispatch分发到对应的handler中进行处理。

缺点: Reactor和Handler在一个线程中,如果Handler阻塞了,name程序就阻塞了。

2.1.2.多线程Reactor

处理流程:

  1. Reactor对象通过Selector监听客户端请求事件,通过dispatch进行分发。
  2. 如果是连接事件,则由Acceptor通过accept方法处理连接请求,然后创建一个Handler对象响应事件。
  3. 如果不是连接事件,则由Reactor对象调用对应Handler对象进行处理;Handler只响应事件不做具体的业务处理,它通过read方法读取数据后,会分发给线程池的某个线程进行业务处理,并将处理结果返回未Handler
  4. Handler收到响应后,通过send方法将结果返回给Handler

优点:
相比单线程Reactor,这里将业务处理的事情交给了不同的线程去做,发挥了多核CPU的性能

缺点:
Reactor只有一个,所有事件的监听和响应都由一个Reactor去完成,并发性并不好。

2.1.3.主从Reactor多线程

与单Reactor多线程的区别就是:专门搞了一个MainReactor来处理连接事件,如果不是连接事件,就分发给SubReactor进行处理。SubReactor可有多个。

优点:
父线程与子线程的交互简单、职责明确,父线程负责接受连接,子线程负责完成后续的业务处理。
缺点: 编程复杂度高

2.2.Netty线程模型

Netty模型时局域主从Reactor多线程模型设计的。

  • Netty有两组线程池,一个BossGroup,它专门负责客户端连接;另一个WorkGroup专门负责网络读写。
  • NIOEventLoopGroup相当于一个事件循环组,这个组包含了多个事件循环,每个循环都是NIOEventLoop
  • NIOEventLoop便是一个不断循环执行处理任务的线程,每个NIOEventLoop都有一个Selector用于监听绑定在其上的SocketChannel的网络通讯。
  • BoosGroup下的每个NIOEventLoop的执行步骤有三步:
    1. 轮询accept连接事件
    2. 处理accept事件,与client建立连接,生成一个NIOSocketChannel,并将其注册到某个WorkGroup下的NIOEventLoopSelector上
    3. 处理任务队列的任务,即runAllTask
  • 每个WorkGroup下的NIOEventLoop循环执行以下步骤:
    1. 轮询read、write事件
    2. 处理read、write事件,在对应的NIOSocketChannel处理
    3. 处理任务,及RunAllTasks
  • 每个WorkGroup下的NIOEventLoop在处理NIOSocketChannel业务时,会使用pipeline(管道),管道中维护了很多handler处理器用来处理channel中的数据

2.1.1.架构图

在这里插入图片描述

2.2.2.架构核心解析

  • Bootstrap、ServerBootstrap:

    一个Netty应用通常由一个Bootstrap开始,主要作用是配置整个Netty程序,串联各个组件。
    Netty中Bootstrap类是客户端程序的启动引导类,ServerBootstrap是服务端的启动引导类。
    Bootstrap创建启动器的步骤:

    1. 设置EventLoopGroup线程组group(bossGroup和workerGroup)
    2. 设置channel通道类型
    3. 设置option参数
    4. 设置handler流水线
    5. 进行端口绑定
    6. 启动
    7. 等待通道关闭
    8. 优雅关闭EventLoopGroup
  • NIOEventLoopGroup和NIOEventLoop:

    NIOEventLoopGroup主要管理eventLoop的生命周期,可以理解为一个线程池,内部维护了一组线程,每个线程(NIOEventLoop)负责处理每个Channel上的事件,而一个Channel只对应一个线程。每个EventLoopGroup里包括一个或多个EventLoop,每个EventLoop中维护了一个Selector实例。

    NIOEventLoop中维护了一个线程和任务队列,支持异步提交执行任务,线程启动时会调用NIOEventLoop中的run方法,执行IO任务和非IO任务。

    • IO任务: selectionKeyready的事件,如accept、connect、read、write等,由processSelectedKeys方法触发。
    • 非IO任务: 添加到taskQueue中的任务,如register()、bind()等任务,由runAllTasks方法触发

    理解NIOEventLoopGroup和NIOEventLoop:

    1. NIOEventLoop实际上就是工作线程。NIOEventLoopGroup是一个线程池,线程池中的线程就是NIOEventLoop。实际上boosGroup中有多个NIOEventLoop线程,每个NIOEventLoop绑定一个端口,也就是说如果程序只需要监听一个端口的话,bossGroup里面只需要有一个NIOEventLoop线程就行了。
    2. 每个NIOEventLoop都绑定了一个selector,所以在Netty的线程模型中是由多个selector在监听IO就绪事件,而channel注册到selector
    3. 一个channel绑定一个NIOEventLoop,相当于一个连接绑定一个线程,这个连接所有的channelHandler都是在一个线程中执行的,避免了多线程干扰。更重要的是channelPipeline链表必须严格按照顺序执行的,单线程的设计能够保证channelHandler的顺序执行。
    4. 一个NIOEventLoopselector可以被多个channel注册,也就是说多个channel共享一个EventLoop。EventLoop的selector对这些channel进行检查。
  • Group:

    服务端要使用两个线程组,bossGroup用于监听客户端连接专门负责与客户端创建连接,并把连接池注册到workerGroup的selector中。workerGroup用于处理每个连接发生的读写事件。一般创建线程组如下:

    // 线程组默认的线程数是CPU核数的两倍
    EventLoopGroup bossGroup = new NioEventGroup();  // 参数可自定义线程数
    EventLoopGroup workerGroup = new NioEventGroup();
    
  • Channel:

    网络通信的组件,能够用于执行网络IO操作。
    channel为用户提供:

    1. 当前网络连接的通道状态
    2. 网络连接的配置参数
    3. 提供异步的网络IO操作(如:建立连接、读写、绑定端口),异步调用意味着任何IO调用都将立即返回,并且不保证正在调用结束时所请求的IO操作完成。
    4. 调用立即返回一个channelFuture实例,通过注册监听器到channelFuture上,可以IO操作成功,失败或取消时回调通知调用方
    5. 支持关联IO操作与对应的处理程序

    常用Channel类型:

    • NIOSocketChannel:异步客户端TCP Socke连接;
    • NIOServerSocketChannel:异步服务端TCP Socke连接;
    • NIODatagramChannel:异步的UDP Socke连接;
    • NIOSctpChannel:异步Sctp连接;
    • NIOSctpServerChannel:异步Sctp服务端连接;

    获取Channel的状态:

    boolean isOpen(); // 如果通道打开则返回true
    boolean isRegistered(); // 如果通道注册到EventLoop则返回true	
    boolean isActive(); // 如果通道处于活动状态并且已连接则返回true
    boolean isWriteAble(); // 当且仅当IO线程将立即执行请求的写入操作时返回true
    
  • Selector:

    Netty基于Selector对象实现IO多路复用,通过Selector一个线程可以监听多个连接的Channel事件。当向一个Selector中注册Channel后,Selector内部的机制就可以自动不断地查询(select)这些注册的Channel是否有已就绪的IO事件(如可读、可写、网络连接完成等),这样程序就可以很简单地使用一个线程高效的管理多个Channel。

  • Pipeline(ChannelPipeline):

    处理器容器,初始化Channel时,把ChannelHandler按顺序装在pipeline中,就可按顺序执行ChannelHandler。ChannelPipeline的list用于处理或拦截Channel的入站事件和出站事件。它实现了一种高级形式的拦截过滤器模式,使用户完全控制事件的处理方法以及Channel中各个ChannelHandler如何交互。

    在Netty中每个Channel都仅有一个ChannelPipeline与之对应,一个Channel包含了一个ChannelPipeline,而channelPipline中又维护了一个由channelHandlerContext组成的双向链表,并且每个channelHandlerContext中又关联着一个channelhandler

    read事件(入站事件)和write事件(出站事件)在一个双向链表中,入站事件会从链表head往后传递到最后一个入站的handler,出站事件会从链表tail往前传递到最后一个出站的handler,两种类型的handler互不干扰。

  • ChannelHandler:

    一个接口,处理IO事件或拦截IO操作,并将其转发到其产ChannelPipeline中的下一个处理程序。它本身并没有提供很多方法,因为这个接口又许多的方法需要实现,方便使用期间可以继承它的子类:

    • channelInboundHandler用于处理入站IO事件
    • channelOutboundHandler用于处理出站IO事件

    或使用以下适配器类:

    channelInboundHandlerAdapter用于处理入站IO事件:

    • 注册事件firechannelRegistered
    • 连接建立事件firechannelActive
    • 读时间和读完成事件firechannelRead、firechannelReadComplete
    • 异常通知事件fireExceptionLaught
    • 用户自定义时间fireUserEventTriggered
    • channel可写入状态变化事件fireChannelWriteBilityChannel
    • 连接关闭事件firechannelInactive

    channelOutboundHandlerAdapter用于处理出站IO事件:

    • 端口绑定bind
    • 连接服务器
    • 写事件write
    • 刷新时间flush
    • 读事件read
    • 主动断开连接disconnect
    • 关闭channel事件close

    channelHandlerContext可以在Handler中拿到channel、pipelined等对象

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/598821.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Jira Server 不维护了,如何将 Jira 平滑迁移到阿里云云效

作者:天彤 Atlassian 在 2020 年官方发布公告,从 2021 年起停止 Jira Server 产品的销售,并且在 2024 年彻底停止 Server 端产品的服务支持,这对于国内使用 Jira 产品的企业和研发团队造成了不小的影响。而此时国内很多 DevOps 产…

LeetCode面试298,二叉树最长连续序列(Python)

开始想着dfs,两种情况 1.以root为根 2.不以root为根 但是这样需要两个dfs分别进行,那么时间复杂度就上去了。 class Solution:def longestConsecutive(self, root: Optional[TreeNode]) -> int:def dfs(root):# 以root为根节点,可以延…

【系统分析师】系统分析部分

文章目录 1、系统分析概述2、详细调查2.1 为什么要做详细调查?2.2 详细调查的原则2.3 详细调查的内容2.4 详细调查的方法 3、现有系统分析3.1 获得系统的物理模型3.2 抽象出现有系统的逻辑模型3.3 建立新系统的逻辑模型3.4 建立新系统的物理模型 4、组织结构分析4.1…

文件夹批量重命名,轻松实现简体中文翻译成繁体中文,文件夹批量改名新体验

文件夹的管理和命名显得尤为重要。你是否曾为了给文件夹取一个合适的名字而 绞尽脑汁?是否因为需要批量修改文件夹名而苦恼不已?现在,我们为你带来一款强大的文件夹批量改名工具,不仅能轻松实现简体中文到繁体中文的转换&#xf…

5月7日监控二叉树+斐波那契数

968.监控二叉树 给定一个二叉树,我们在树的节点上安装摄像头。 节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。 计算监控树的所有节点所需的最小摄像头数量。 示例 1: 输入:[0,0,null,0,0] 输出:1 解释&#xff…

LeCun转发,AI让失语者重新说话!纽约大学发布全新「神经-语音」解码器 | 最新快讯

新智元报道 编辑:LRT 通过采集皮层电图(ECoG)的数据信号,模型可以将其转换为可解释的语音参数(如音高,响度,共振峰频率等),并合成出既准确又自然的语音波形。 脑机接口&a…

【C++ | 函数】默认参数、哑元参数、函数重载、内联函数

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C、数据结构、音视频🍭 ⏰发布时间⏰:2024-05-04 1…

【Flutter】App内购支付集成 Google和Apple支付和服务器验证全流程

Flutter支付集成 前言: 以谷歌内购为例,我们需要做的总共为三步 需要在谷歌市场配置商品,设置测试渠道,配置开发者账号,设置对应权限。配置完商品之后,如何在 Flutter 中获取到商品,购买指定…

如何为数据库中新建用户B复制用户A的表和视图权限?

故事背景: 公司使用的是SQL Server数据库,经常会碰到一种情况,需要为新入职的员工赋予同组内其他同事的权限。 常用方法: 1) 为同一组申请创建统一的Security Group(安全组),为创建的组分配相关表和视图的访问权限。不管员工入职…

基于POSIX标准库的读者-写者问题的简单实现

文章目录 实验要求分析保证读写、写写互斥保证多个读者同时进行读操作 读者优先实例代码分析 写者优先示例代码分析 实验要求 创建一个控制台进程,此进程包含n个线程。用这n个线程来表示n个读者或写者。每个线程按相应测试数据文件的要求进行读写操作。用信号量机制…

FileLink跨网文件交换,推动企业高效协作|半导体行业解决方案

随着信息技术的迅猛发展,全球信息产业已经迎来了前所未有的繁荣与变革。在这场科技革命中,半导体作为信息产业的基础与核心,其重要性日益凸显,半导体的应用场景和市场需求将进一步扩大。 然而,在这一繁荣的背后&#x…

解决 SyntaxError: Unexpected token ‘.‘ 报错问题

这个报错一般是编译问题&#xff0c;浏览器的版本过低没通过代码 解决办法&#xff1a; 在package.json文件中加上这个 "browserslist": ["> 1%","last 2 versions","not dead","not ie < 6","Android > 4&…

源代码防泄露可以通过哪些方法实现?七种有效方法分享

在当今数字化时代&#xff0c;访问安全和数据安全成为企业面临的重要挑战。传统的边界防御已经无法满足日益复杂的内网办公环境&#xff0c;层出不穷的攻击手段已经让市场单一的防御手段黔驴技穷。当企业面临越来越复杂的网络威胁和数据泄密风险时&#xff0c;更需要一种综合的…

stable-diffusion-webui配置

源码地址 https://github.com/AUTOMATIC1111/stable-diffusion-webui.git报错Fresh install fail to load AttributeError: NoneType object has no attribute _id pydantic降级 pip uninstall pydantic pip install pydantic1.10.11记得要把clip-vit-large-patch14放在opena…

Java集合 总结篇(全)

Java集合 集合底层框架总结 List 代表的有序&#xff0c;可重复的集合。 ArrayList -- 数组 -- 把他想象成C中的Vector就可以&#xff0c;当数组空间不够的时候&#xff0c;会自动扩容。 -- 线程不安全 LinkedList -- 双向链表 -- 可以将他理解成一个链表&#xff0c;不支持…

C语言猜数字游戏

用C语言实现猜数字游戏&#xff0c;电脑随机给出一个范围内的数字&#xff0c;用户在终端输入数字&#xff0c;去猜大小&#xff1b;对比数字&#xff0c;电脑给出提示偏大还是偏小&#xff1b;不断循环&#xff0c;直到正确 #include <stdio.h> #include <time.h>…

【系统架构师】-选择题(十一)

1、紧耦合多机系统一般通过&#xff08;共享内存&#xff09;实现多机间的通信。对称多处理器结构&#xff08;SMP&#xff09;属于&#xff08; 紧耦合&#xff09;系统。 松耦合多机系统又称间接耦合系统,—般是通过通道或通信线路实现计算机间的互连。 2、采用微内核的OS结构…

从互联网医院源码到搭建:开发视频问诊小程序的技术解析

如今&#xff0c;视频问诊小程序作为医疗服务的一种新形式&#xff0c;正逐渐受到人们的关注和青睐。今天&#xff0c;小编将为您详解视频问诊小程序的开发流程。 一、背景介绍 互联网医院源码是视频问诊小程序开发的基础&#xff0c;它提供了一套完整的医疗服务系统框架&…

【vue-echarts】 报错问题解决 “Error: Component series.pie not exists. Load it first.“

目录 问题描述解决【解决1】【解决2】 问题描述 使用 vue-echarts 时导入的文件 import VChart from vue-echarts/components/ECharts import echarts/lib/chart/line import echarts/lib/chart/bar import echarts/lib/chart/pie import echarts/lib/component/legend impor…

MySQL 报错: “Host ‘xxx‘ is not allowed to connect to this MySQL server“

MySQL 报错 “Host ‘xxx’ is not allowed to connect to this MySQL server” 通常是因为数据库服务器上的权限设置不允许来自特定主机&#xff08;‘xxx’&#xff09;的连接。解决这个问题通常涉及修改 MySQL 的访问控制设置。 以下是一些可能的解决步骤&#xff1a; 使用…