Java IO流(四)Netty理论[模型|核心组件]

news2024/9/30 5:29:39

概述

  • Netty是由JBOSS提供的一个Java开源框架,可从Github获取独立项目
  • Netty是一个异步的、基于事件驱动的网络应用框架,用于快速开发可维护、高性能的网络服务器和客户端(摘录官网)
  • Netty所谓的异步是针对用户使用Channel进行IO操作,会立即返回ChannelFuture。但IO操作的任务是提交给了Netty的NIO底层去进行处理,所以我们说Netty的异步事件驱动与Netty底层基于NIO(同步非阻塞)是不矛盾的
  • Netty主要针对在TCP传输协议下,面对Clients端高并发应用;本质属于NIO框架,适用于服务器通讯相关多种应用场景
    • 阿里分布式服务框架Dubbo的RPC框架使用Dubbo协议,而Dubbo协议默认使用Netty作为基础的通信组件,实现节点间内部通信
    • 大数据领域中Hadoop的RPC框架默认采用的就是Netty进行跨节点通信
  • Netty官网版本推荐:目前稳定版为Netty4.x

Netty模型

Netty基于主从Reactor多线程模型做了一定的改进,其中主从Reactor多线程模型中存在多个Reactor

工作原理执行流程

  • Netty中抽象了两组线程池BossGroup和WorkerGroup,都属于NioEventLoopGroup,其中BossGroup负责接收客户端连接,WorkerGroup负责网络读写
    • NioEventLoopGroup属于事件循环组,这个组中包含多个事件循环,每个事件循环是NioEventLoop
    • NioEventLoop表示一个不断循环的执行处理任务的线程,每个NioEventLoop都有一个selector,用于监听绑定在其上socket的网络通讯
  • BossGroup下每个NioEventLoop执行流程
    • 轮询accept事件
    • 处理accept事件,与client建立连接生成NIOSocketChannel,并将其注册到对应worker下NIOEventLoop上的selector
    • 处理任务队列任务,即runAllTasks
  • WorkerGroup下每个NioEventLoop执行流程
    • 轮询read,write事件
    • 在对应NIOSocketChannel中处理read,write I/O事件
    • 处理任务队列任务,即runAllTasks
  • 每个Worker NioEventLoop处理业务时都会交由pipeLine管道处理,pipeline中包含了channel,即通过pipeline可以获取对应channel通道,管道中维护了很多处理业务的Handler处理器

TaskQueue任务队列

BossGroup和WorkerGroup下每个NioEventLoop中都维护了一个TaskQueue,可以用来异步处理比较耗时的任务,TaskQueue中的Task有三种典型使用场景

  • 用户程序自定义普通任务,该任务会提交到TaskQueue
    public class NettyServerHandler extends ChannelInboundHandlerAdapter {
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { //读取客户端发送的数据
            //ctx:上下文对象,包含管道pipeline,通道等信息
            //用户程序自定义普通任务
            ctx.channel().eventLoop().execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(10 * 1000); //模拟耗时长的任务
                        ctx.writeAndFlush(Unpooled.copiedBuffer("Hello, Client!", CharsetUtil.UTF_8)); //将数据写到缓存并刷新
                    } catch (Exception e) {
                        System.out.println("Exception Info: " + e.getMessage());
                    }
                }
            });
        }
    }
  • 用户自定义定时任务,该任务提交到scheduleTaskQueue
            ctx.channel().eventLoop().schedule(() -> {
                try {
                    Thread.sleep(5 * 1000);
                    ctx.writeAndFlush(Unpooled.copiedBuffer("Hello, Client!", CharsetUtil.UTF_8)); 
                } catch (Exception e) {
                    System.out.println("Exception Info: " + e.getMessage());
                }
            },5, TimeUnit.SECONDS);
  • 非当前Reactor线程调用Channel的各种方法(常用于推送任务场景)

异步模型

  • 异步和同步相对,指的是当一个异步过程调用发出后,调用者不能立刻得到结果,实际处理这个调用的组件在完成后,会通过状态、通知和回调来通知调用者
  • Netty中的I/O操作是异步的,包括Bind、Write、Connect等会返回一个ChannelFuture
  • Netty的异步模型是建立在future和callback之上的,callback即回调,Future核心思想在于对于处理某些耗时的业务时会立刻返回一个Future(表示异步执行结果,其中通过监听方法检测任务执行情况),通过Future监控该业务处理的过程最终获得I/O操作结果(即Future-Listener机制)
    • Future-Listener机制
      • 当Future对象刚刚创建时,处于非完成状态,调用者可以通过返回的 ChannelFuture来获取操作执行状态,注册监听函数来执行完成后的操作
      • 常见操作方法
        • isDone: 判断当前操作是否完成
        • isSuccess: 判断已完成的当前操作是否成功
        • getCause: 判断当前操作失败的原因
        • isCancelled: 判断已完成的当前操作是否被取消
        • addListener: 用来注册监听器,当前操作已完成,将会通知指定的监听器(如果Future对象已完成,则通知指定的监听器)
      • Future-Listener示例

        ChannelFuture cf = bootstrap.bind(6668).sync(); //绑定指定端口并同步处理
        cf.addListener(new ChannelFutureListener() { //为端口添加监听
        	@Override
        	public void operationComplete(ChannelFuture channelFuture) throws Exception {
        		if (cf.isSuccess()){
        			System.out.println("监听端口成功!");
        		} else{
        			System.out.println("监听端口失败!");
        		}
        	}
        });

核心组件

Netty中根据各组件职责不同可以分为网络通信层、事件调度层、服务编排层

网络通信层核心组件

BootStrap|ServerBootStrap

BootStrap是Netty中客户端启动类,负责客户端启动并连接Netty服务端;ServerBootStrap是服务端启动引导类,负责服务端启动并监听端口;常用方法包括如下

  • group:服务端用来设置BossGroup和WorkerGroup,而对于客户端则是设置一个EventGroup
  • channel:用来设置服务器端的通道类型,如服务器端设置为NioServerSocketChannel类型
  • option:用来给ServerChannel添加配置,如设置线程队列连接个数
  • childOption:用来给接收通道添加配置,如设置活动连接状态
  • handler:该handler作用于BossGroup
  • childHandler:该handler作用于WorkerGroup,设置业务处理类,包括自定义的handler
  • bind:用于服务器端,设置对外暴露的端口号
  • connect:用于客户端,设置连接服务器的ip和端口等信息

Channel

提示:NioSocketChannel和NioServerSocketChannel分别对应客户端和服务端的Channel,两者的直接父类不一致,因此对外提供的功能也是不相同的。比如当发生read事时,NioServerSocketChannel 的主要逻辑就是建立新的连接,而NioSocketChannel则是读取传输的字节进行业务处理

  • Netty中的Channel是完成一系列网络通信的载体
  • Channel可以看成网络编程中的Socket,提供了执行网络异步IO操作API,包括read|write|bind|connect,通过ChannelFuture中已注册的监听器在IO操作成功、失败等回调通知调用结果,大大降低了直接使用Socket类的复杂性
    • Future|ChannelFuture:Netty中所有IO操作都是异步的,不能立刻得知消息是否正确处理,但可以通过Future和ChannelFuture注册监听事件,当操作成功或者失败自动触发注册的监听事件,常用方法如下

      • channel:返回当前正在进行的IO操作的通道
      • sync:等待异步操作执行完毕
  • 通过Channel可获取当前网络连接的通道的状态以及配置参数等
  • 不同协议、不同阻塞类型的连接都有不同的Channel类型与之对应,常用Channel类型如下
    • NioSocketChannel:异步客户端TCP Socket连接
    • NioServerSocketChannel:异步服务器端TCP Socket连接
    • NioDatagramChannel:异步的UDP连接

事件调度层核心组件

EventLoopGroup

就是一个线程池,负责接收I/O请求并分配线程执行处理请求,在Netty服务端中的bossGroup中的线程用来处理新连接的建立,当连接建立后分发给workerGroup,workerGroup中每个线程则都会和唯一的客户端Channel连接进行绑定并处理该Channel上的读、写事件

EventLoop

就相当于线程池中的一个线程

服务编排层核心组件

Channel&&ChannelPipeline&&ChannelHandler关系

ChannelPipeline

  • ChannelPipeline是Handler集合,它负责处理和拦截inbound或者outbound的事件和操作,即用于处理Channel的入站和出站操作
  • Netty中每个Channel都有一个ChannelPipeline与之对应,两者可以互相获取;而且Pipeline维护了一个channelHandlerContext组成的双向链表,并且channelHandlerContext关联一个ChannelHandler
  • 当Channel中发生指定事件时,该事件就会在ChannelPipeline中沿着双向链表进行传播并调用各个ChannelHandler中的指定方法完成相应的业务处理
  • ChannelPipeline常用方法如下
    • addFirst:把业务处理类Handler添加到链表中第一个位置
    • addLast:把业务处理类Handler添加到链表中最后一个位置

提示:Netty正是通过ChannelPipeline这种数据结构为用户提供了自定义业务逻辑的扩展点,用户只需要向ChannelPipeline中添加处理对应业务逻辑的ChannelHandler即可,当指定事件发生时,该ChannelHandler中的对应方法就会进行回调进而实现业务的处理

ChannelHandler

ChannelInboundHandler&&ChannelOutboundHandler分别用来处理读&&写事件
  • ChannelHandler是Netty 中业务处理的核心类,当有 IO 事件发生时,该事件会在ChannelPipeline中进行传播,并依次调用具体的ChannelHandler进行业务处理

ChannInboundHandler

  • 在ChannelInboundHandler中定义了一系列的回调方法,用户可以实现该接口并重写相应的方法来自定义的业务逻辑
    • channelRegistered: 当 Channel 绑定到了对应 Selector 上之后就会进行回调

    • channelActive: 当 Channel调用bind()完成端口绑定之后,channelActive() 方法会进行回调

    • channelRead0

      • 服务端channelRead0
        • 服务端Channel绑定到Selector上时监听的是Accept事件,当客户端有新连接接入时回调channelRead()方法并完成新连接的接入
      • 客户端channelRead0
        • 当服务端处理完Accept事件后会生成一个和客户端通信的Channel,该Channel也会注册到对应的Selector上并监听read事件,当客户端向该Channel中发送数据时就会触发read事件,调用channelRead()方法
    • exceptionCaught:当前ChannelHandler中各回调方法处理过程中若发生异常则回调该方法

ChannelHandlerContext

  • ChannelHandlerContext主要保存了Channel通道、ChannelHandler通道处理者相关信息,它们之间对应关系如下
    • ChannelPipeline与ChannelHandlerContext对应关系: 1:n
    • ChannelHandlerContext与ChannelHandler 对应关系: 1:1
  • ChannelHandlerContext 常用方法如下
    • channel(): 获取当前通道
    • pipeline(): 获取当前管道
    • handler(): 获取当前handler处理器
    • close(): 关闭当前通道
    • writeAndFlush(): 将数据写出到ChannelPipeline管道中

其他

Unpooled类

  • Unpooled是Netty提供的专门操作缓冲区的工具类
  • copiedBuffer:用来设置数据以及字符编码并返回一个ByteBuf对象
    • ByteBuf:Netty中提供的缓冲区,底层就是一个数组
    • ByteBuf不需要调用flip方法进行模式切换,底层通过readerIndex|writerIndex|capactiy将byteBuf分成三个区域
      • 0-readerIndex:表示已经读取的区域
      • readerIndex-writerIndex:可读的区域
      • writerIndex-capactiy:可写的区域

Netty的编码解码机制

Netty提供的编码和解码器包括如下

  • StringEncoder:对字符串数据进行编码
  • ObjectEncoder:对Java对象进行编码
  • StringDecoder:对字符串数据进行解码
  • ObjectDecoder:对Java对象进行解码

存在问题

Netty的对java对象进行解码编码的底层使用的是Java序列化技术,但其效率不高,且无法跨语言,序列化体积是二进制编码5倍多,性能太低

解决方案

可以使用Google的Protobuf,它是一种轻便高效的结构化数据存储格式,用于结构化数据串行化,很适合数据存储或RPC数据交换格式,而且它支持跨语言

常见问题

已经存在NIO,为何还要开发出Netty框架?

  • NIO类库和API繁杂,使用麻烦
  • NIO属于单Ractor单线程模式,需要熟悉掌握线程和网络编程,才可以编写高质量NIO程序
  • 开发工作量和难度大,如面临断连重连、失败缓存、网络拥塞等问题
  • NIO本身存在的Epoll bug,产生空轮询导致CPU爆满

而Netty则解决了如上问题,它基于NIO进行了封装优化,具有如下优点

  • 设计优雅:适用于各种传输类型的统一
  • 高性能、吞吐量更高,延迟更低,减少资源消耗
  • 安全:完整的SSL/TLS支持
  • 社区活跃、版本迭代周期短

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AutoSAR配置与实践(基础篇)3.5 BSW 的模式管理

传送门 -> AUTOSAR配置与实践总目录 AutoSAR配置与实践(基础篇)3.5 BSW 的模式管理 一、模式管理的组成二、模式项内容简介一、模式管理的组成 AUTOSAR为ECU的运行时软件的状态处理提供了模式管理组件,如下 • BswM模式管理器 • NM网络管理 • EcuM状态管理器 • ComM通…

Kaggle分类问题Titanic——Machine Learning from Disaster

目录 前言1 题目介绍2 数据清洗3 数据可视化分析4 模型训练5 源码 前言 这是我在大三选修课的课程设计,内容参考了Kaggle上高赞的代码,有详细批注,整体比较基础,结构相对完整,便于初学者学习。这个是一个分类问题&am…

vue 简单实验 v-if 条件判定

1.代码 <script src"https://unpkg.com/vuenext" rel"external nofollow" ></script> <div id"conditional-rendering"><span v-if"seen">现在你看到我了</span> </div> <script> const C…

纷享销客入选“寻找创新的「踏脚石」|36氪数字创新标杆案例”

近日&#xff0c;36氪重磅发布“数字创新标杆案例&先进团队名册”&#xff0c;本期围绕寻找创新的「踏脚石」的主题&#xff0c;共遴选出36个数字创新标杆案例与10个先进团队&#xff0c;纷享销客连接型CRM凭借过硬的产品及自主科研创新实力和服务实践有幸入选。 此次评选历…

分布式定时任务框架Quartz总结和实践(2)—持久化到Mysql数据库

本文主要介绍分布式定时任务框架Quartz集成SpringBoot持久化数据到Mysql数据库的操作&#xff0c;上一篇文章使用Quartz创建定时任务都是保存在内存中&#xff0c;如果服务重启定时任务就会失效&#xff0c;所以Quartz官方也提供将定时任务等信息持久化到Mysql数据库的功能&…

[docker][WARNING]: Empty continuation line found in:

报警内容&#xff1a; 下面展示一些 内联代码片。 //执行 sudo docker build ubuntu:v1.00 . [WARNING]: Empty continuation line found in:出现上述错误原因为18行多了一个 " \" 符号&#xff0c;去除即可

Hexo添加相册

首发博客地址 https://blog.zysicyj.top/ 视频教学地址 https://www.bilibili.com/video/BV1Fw411Q7pS/ 打开butterfly配置文件&#xff0c;在menu处新增一行 menu: 主页: / || fas fa-home 时间轴: /archives/ || fas fa-archive 标签: /tags/ || fas fa-tags 分类: /cate…

函数模版c++

泛型编程 如何实现一个通用的交换函数呢&#xff0c;利用c函数重载的方式 void Swap(int& left, int& right) { int temp left; left right; right temp; } void Swap(double& left, double& right) { double temp left; left right; right temp; } vo…

AMBA总线协议(9)——AHB(七):终章

一、前言 在之前的文章中我们讲述了AHB协议的分割传输机制&#xff0c;它使得从机可以决定一次传输是否继续进行&#xff0c;以防止 传输的执行将占据大量的时钟周期&#xff0c;有效提高了总线的公平性与效率问题&#xff0c;本文中我们将一次性学习完AHB最后的内容&#xff0…

蚂蚁 SOFAServerless 微服务新架构的探索与实践

赵真灵&#xff08;有济&#xff09; 蚂蚁集团技术专家 Serverless 和微服务领域专家曾负责基于 K8s Deployment 的应用发布运维平台建设、K8s 集群的 Node/pod 多级弹性伸缩与产品建设。当前主要负责应用架构演进和 Serverless 相关工作。同时也是 SOFAArk 社区的开发和维护者…

【CP2K学习】-在Ubuntu上安装CP2K的全过程(包括gcc,gfortran,MKL等配置)

在Ubuntu中安装CP2K CP2K的安装检查系统是否安装gcc,gfortranMKL数学库的安装CP2K安装包下载CP2K的编译CP2K的测试ssmp版本测试popt版本测试 CP2K是第一性原理计算程序中发展迅速的程序之一&#xff0c;因其开源性、速度性等优点&#xff0c;是广大计算化学研究者的选择。 本文…

2023.8各大浏览器11家对比:Edge/Chrome/Opera/Firefox/Tor/Vivaldi/Brave,安全性,速度,体积,内存占用

测试环境&#xff1a;全默认设置的情况下&#xff0c;均在全新的系统上进行测试&#xff0c;系统并未进行任何改动&#xff0c;没有杀毒软件&#xff0c;浏览器进程全部在后台&#xff0c;且为小窗模式&#xff0c;小窗分辨率均为浏览器厂商默认缩放大小(变量不唯一)&#xff0…

C#|如何调试进依赖动态库中

第一步&#xff1a;打开项目属性 第二步 打开debug的本地调试可用 第三步 把要调试的代码拖进主界面打断点就可以进断点了

测试分类

测试分类&#xff08;全是概念&#xff1b;非常抽象&#xff09;按对象划分界面测试可靠性测试容错性测试文档测试兼容性测试易用性安装卸载测试安全测试性能测试内存泄漏测试 按是否查看代码划分黑盒测试白盒测试灰盒测试 按开发阶段划分单元测试集成测试系统测试回归测试冒烟…

js判断类型:typeof Object.prototype.toString instanceof constructor有什么区别?一文讲清楚

相信很多小伙伴在使用js的过程中&#xff0c;经常会需要对js的数据类型进行判断&#xff0c;而js中可以对数据类型进行判断的方法有很多种&#xff0c;最常见的有typeof、Object.prototype.toString、instanceof、constructor这四种&#xff0c;那么他们有什么区别呢&#xff1…

ssm+vue游戏攻略网站源码和论文

ssmvue游戏攻略网站源码和论文052 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 一、主要内容和基本要求 游戏攻略网站分为管理员与用户两种角色。 管理员的功能包括登录&#xff0c;用户管理&#xff0c;游…

Laravel 框架构造器的查询表达式构造器的 Where 派生查询 ⑥

作者 : SYFStrive 博客首页 : HomePage &#x1f4dc;&#xff1a; THINK PHP &#x1f4cc;&#xff1a;个人社区&#xff08;欢迎大佬们加入&#xff09; &#x1f449;&#xff1a;社区链接&#x1f517; &#x1f4cc;&#xff1a;觉得文章不错可以点点关注 &#x1f44…

QCC_BES 音频重采样算法实现

+V hezkz17进数字音频系统研究开发交流答疑群(课题组) 这段代码是一个用于将音频数据进行立体声重采样的函数。以下是对代码的解读: 函数接受以下参数: pcm_buf:16位有符号整型的音频缓冲区,存储了输入的音频数据。pcm_len:音频缓冲区的长度。mic1:16位有符号整型的音频…

SpringBoot 01 如何创建 和pom的解析

目录 1 Springboot的创建 步骤 2 项目的书写和运行 创建service包并在其下写一个service文件 项目的运行 pom文件的一些配置 parent web test 打包 打包过程 1 Springboot的创建 步骤 首先new一个新项目 然后依照如下创建 2 项目的书写和运行 创建service包并…

企业网络日志安全与 EventLog Analyzer

企业的网络日志安全是一项至关重要的任务。随着信息技术的迅猛发展&#xff0c;网络攻击和数据泄露的威胁也与日俱增。为了应对这些威胁&#xff0c;企业需要强大的工具来监控、分析和保护其网络日志。而ManageEngine的EventLog Analyzer正是这样一款卓越的解决方案。 网络日志…