【netty系列-04】反应堆模式的种类和具体实现

Netty系列整体栏目


内容链接地址
【一】深入理解网络通信基本原理和tcp/ip协议https://zhenghuisheng.blog.csdn.net/article/details/136359640
【二】深入理解Socket本质和BIOhttps://zhenghuisheng.blog.csdn.net/article/details/136549478
【三】深入理解NIO的基本原理和底层实现https://zhenghuisheng.blog.csdn.net/article/details/138451491
【四】深入理解反应堆模式的种类和具体实现https://zhenghuisheng.blog.csdn.net/article/details/140113199

反应堆模式的种类和具体实现

  • 一,反应堆模式的种类和底层原理
    • 1,单线程反应堆模式
    • 2,单线程-work工作者线程池模式
    • 3,多线程主从模式
    • 4,redis中的reactor反应堆模式

一,反应堆模式的种类和底层原理

前面文章中讲解了什么是socket,socket的本质就是操作系统为我们开发人员提供的一些列api,内部封装了从客户端a 从传输层,网络层,数据链路层,物理层再到 客户端b 中的物理层,数据链路层,网络层再到传输层之间的内部协议,如下图,让开发人员秩序更加关注应用层之间的开发,不需要关注底层的具体实现

socket内部会在网络通信中,去实现 tcp的三次握手,处理丢包后的网络重传流量控制等

请添加图片描述

上一篇中讲解了Reactor反应堆模式的核心以及组成部分,接下来详细的讲解一下反应堆模式的种类以及底层的实现原理

1,单线程反应堆模式

首先第一种就是单线程的反应堆模式,就是说不管是客户端的网络连接,还是读取网络上的数据,或者说具体的相关的业务处理,都是通过一个线程里面负责和处理的

在这里插入图片描述

如下面这段代码,首先创建一个 ServerHandle 的线程任务类,并且在构造方法中设置对应的 selector 选择器和一个处理请求的 ServerSocketChannel 的通道,由于将该类作为服务端,并且为了设置是单线程的模式,因此将这个selector和socket设置为唯一

public class ServerHandle implements Runnable{private Selector selector;private ServerSocketChannel serverChannel;/*** 构造方法* @param port 指定要监听的端口号*/public ServerHandle(int port) {try{//创建选择器selector = Selector.open();serverChannel = ServerSocketChannel.open();打开监听通道serverChannel.configureBlocking(false);//开启非阻塞模式//绑定端口 backlog设为1024serverChannel.socket().bind(new InetSocketAddress(port),1024);//监听客户端连接请求serverChannel.register(selector, SelectionKey.OP_ACCEPT);started = true}catch(IOException e){e.printStackTrace();System.exit(1);}}
}

随后再这个类里面重写这个 run 方法,都是遍历这个selector 轮询器,获取里面的读写或者监听事件,将即将处理的事件从轮询器中删除,如果抛出异常则取消这个key的事件处理,事件全部处理完成则将轮询器close关闭

@Override
public void run() {//循环遍历selectorwhile(started){try{//阻塞,只有当至少一个注册的事件发生的时候才会继续.selector.select();Set<SelectionKey> keys = selector.selectedKeys();Iterator<SelectionKey> it = keys.iterator();SelectionKey key = null;while(it.hasNext()){key = it.next();it.remove();try{handleInput(key);}catch(Exception e){if(key != null){key.cancel();if(key.channel() != null){key.channel().close();}}}}}catch(Throwable t){t.printStackTrace();}}//selector关闭后会自动释放里面管理的资源if(selector != null){try{selector.close();}catch (Exception e) {e.printStackTrace();}}

真正处理时间的方法在这个 handleInput 方法中,里面会去判断这个key是属于哪一个事件的,比如是读事件,还是写事件,还是监听事件,都会进行相应的处理。

private void handleInput(SelectionKey key) throws IOException{if(key.isValid()){//处理新接入的请求消息if(key.isAcceptable()){//获得关心当前事件的channelServerSocketChannel ssc = (ServerSocketChannel) key.channel();//通过ServerSocketChannel的accept创建SocketChannel实例//完成该操作意味着完成TCP三次握手,TCP物理链路正式建立SocketChannel sc = ssc.accept();System.out.println("======socket channel 建立连接=======");//设置为非阻塞的sc.configureBlocking(false);//连接已经完成了,可以开始关心读事件了sc.register(selector, SelectionKey.OP_READ);}//读消息if(key.isReadable()){System.out.println("======socket channel 数据准备完成," +"可以去读==读取=======");SocketChannel sc = (SocketChannel) key.channel();//创建ByteBuffer,并开辟一个1M的缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);//读取请求码流,返回读取到的字节数int readBytes = sc.read(buffer);//读取到字节,对字节进行编解码if(readBytes>0){//将缓冲区当前的limit设置为position,position=0,// 用于后续对缓冲区的读取操作buffer.flip();//根据缓冲区可读字节数创建字节数组byte[] bytes = new byte[buffer.remaining()];//将缓冲区可读字节数组复制到新建的数组中buffer.get(bytes);String message = new String(bytes,"UTF-8");System.out.println("服务器收到消息:" + message);//处理数据String result = response(message) ;//发送应答消息doWrite(sc,result);}//链路已经关闭,释放资源else if(readBytes<0){key.cancel();sc.close();}}if(key.isWritable()){SocketChannel sc = (SocketChannel) key.channel();ByteBuffer buffer = (ByteBuffer)key.attachment();if(buffer.hasRemaining()){int count = sc.write(buffer);System.out.println("write :"+count+"byte, remaining:"+buffer.hasRemaining());}else{/*取消对写的注册*/key.interestOps(SelectionKey.OP_READ);}}}
}

在写事件完成之后,buffer缓冲区会有新的空间,因此可以将读到的数据写入到写缓冲区中,由于tcp全双工的特性,因此可以实现服务端边读边写的功能。

//发送应答消息
private void doWrite(SocketChannel channel,String response)throws IOException {//将消息编码为字节数组byte[] bytes = response.getBytes();//根据数组容量创建ByteBufferByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);//将字节数组复制到缓冲区writeBuffer.put(bytes);//flip操作writeBuffer.flip();channel.register(selector,SelectionKey.OP_WRITE|SelectionKey.OP_READ,writeBuffer);
}

通过以上案例,可以完整的说明白单线程是完全可以实现一个bio的,通过Reactor单线程反应堆的模式去完成所有的请求和连接。

虽然单线程可以完整的实现整个BIO的流程,但是单线程也有单线程的弊端,如由于业务响应也是通过单线程去处理,如果涉及到某个业务需要花费太长的时间,那么整个系统就会处于一个阻塞的状态,只有等这个任务结束之后,才能继续进行下一步的任务。因此单线程的反应堆模式也是有却缺陷的

2,单线程-work工作者线程池模式

在纯粹的单线程模式中,可能会因为某一个具体的业务导致整个系统处于一个阻塞的瘫痪状态,都是因为全部任务都共用一个线程,那么这就好办了,就是只有监听事件,读事件和写事件共用同一个单线程,如果是涉及到需要处理业务的任务,那么就将这部分丢到线程池中,通过线程池中的线程去处理,这样就不会影响主线程的执行,并且通过这种异步的方式,从而增快主线程处理任务的效率,提升整个系统的响应

在这里插入图片描述

这部分代码不做详细解释,就是再创建一个 ServerHandleWorker 的Task任务类,并且实现Callable接口,然后再这个类中重写的run方法去做对应的业务即可

3,多线程主从模式

在2中已经对1进行了很大的优化,但是在reacotr模式的线程中,还是需要处理read读事件和write写事件,因此为了让这个reactor单线程更快,那么又可以将一个线程拆分成两个线程,让主reactor只需要负责处理接收事件,让从reactor异步的去处理读事件和写事件,然后处理其他业务的事件存放在线程池中,从而完全的提升整个系统的吞吐量和效率

在这里插入图片描述

4,redis中的reactor反应堆模式

在redis的6.0之前,redis内部就是使用一个标准的单线程 reactor 反应堆模式,通过一个线程去执行连接事件,读事件,写事件和其他的一些业务事件等等

在redis6.0开始,redis内部多线程的主从模式基本一致,通过mainReactor主线程处理接收事件和读事件,但是由子线程去执行读事件和写事件,同时业务线程还是通过mainReactor主线程去执行,从而保证减少一些并发冲突

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

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

相关文章

数字化转型中,数字化如何重塑中小企业发展力?

引言&#xff1a;当前&#xff0c;我国中小微企业数字化转型处于“不平衡、不充分、不规范”阶段&#xff0c;普遍面临“不会转”“不能转”“不敢转”的困境。数字化转型可以帮助企业突破这些困境&#xff0c;实现更大的发展。更进一步&#xff0c;数字化转型是中小企业高质量…

学习笔记——动态路由——IS-IS中间系统到中间系统(基本概念)

二、IS-IS基本概念 1、IS-IS概述 IS-IS是ISO定义的OSI协议栈中的无连接网络服务(ConnectionLess Network Service&#xff0c;CLNS)的一部分&#xff0c;IS-IS是一种链路状态路由协议&#xff0c;IS-IS与OSPF在许多方面非常相似&#xff0c;例如&#xff0c;运行IS-IS协议的直…

4.BeanFactory

可以看出BeanFactory表面上只有getBean相关的方法。 实际上控制反转、基本的依赖注入、Bean的生命周期的各种功能&#xff0c;都是由BeanFactory的实现类来实现的。&#xff08;DefaultListableBeanFactory&#xff09; DefaultListableBeanFactory管理单例对象DefaultSinglet…

杨万里,诚斋体的开创者

杨万里&#xff0c;字廷秀&#xff0c;号诚斋&#xff0c;生于南宋绍兴元年&#xff08;公元1127年&#xff09;&#xff0c;卒于南宋庆元二年&#xff08;公元1206年&#xff09;&#xff0c;享年79岁。在中国古代文学的璀璨星河中&#xff0c;南宋诗人杨万里以其清新脱俗、贴…

Android- Framework 非Root权限实现修改hosts

一、背景 修改system/etc/hosts&#xff0c;需要具备root权限&#xff0c;而且remount后&#xff0c;才能修改&#xff0c;本文介绍非root状态下修改system/etc/hosts方案。 环境&#xff1a;高通 Android 13 二、方案 非root&#xff0c;system/etc/hosts只有只读权限&…

机器学习python实践——关于管道模型Pipeline和网格搜索GridSearchCV的一些个人思考

最近在利用python跟着指导书进行机器学习的实践&#xff0c;在实践中使用到了Pipeline类方法和GridSearchCV类方法&#xff0c;并且使用过程中发现了一些问题&#xff0c;所以本文主要想记录并分享一下个人对于这两种类方法的思考&#xff0c;如果有误&#xff0c;请见谅&#…

【微服务】微服务之Feign 与 Ribbon

文章目录 强烈推荐引言优点Feign示例什么是Ribbon&#xff1f;Ribbon 的优点Netflix Feign 和 Ribbon整合Feign 与 Ribbon 的关系Feign 与 Ribbon 结合使用的示例配置文件&#xff08;application.yml&#xff09;说明&#xff1a; Feign 与 Ribbon 结合使用的应用场景1. 动态服…

物联网的技术和应用有哪些?

随着科技的飞速发展&#xff0c;物联网已经成为连接世界的重要纽带&#xff0c;塑造着我们未来的生活。我们一起深入探索物联网的前沿技术和前瞻性应用&#xff0c;一窥未来的可能性。 获取物联网解决方案&#xff0c;YesPMP平台一站式物联网开发服务。 提示&#xff1a;智慧家…

图形的搭建

例一&#xff1a; 输入描述&#xff1a; 多组输入&#xff0c;一个整数&#xff08;2~20&#xff09;&#xff0c;表示输出的行数&#xff0c;也表示组成“X”的反斜线和正斜线的长度。 输出描述&#xff1a; 针对每行输入&#xff0c;输出用“*”组成的X形图案。 示例一&…

爬数据是什么意思?

爬数据的意思是&#xff1a;通过网络爬虫程序来获取需要的网站上的内容信息&#xff0c;比如文字、视频、图片等数据。网络爬虫&#xff08;网页蜘蛛&#xff09;是一种按照一定的规则&#xff0c;自动的抓取万维网信息的程序或者脚本。 学习一些爬数据的知识有什么用呢&#x…

IPIDEA代理IP助力高效数据采集

IPIDEA代理IP助力高效数据采集 文章目录 IPIDEA代理IP助力高效数据采集&#x1f4d1;前言一、爬虫数据采集痛点二、代理IP解决爬虫痛点2.1 为什么可以2.2 选择代理IP的关键因素 三、IPIDEA海外IP代理的优势3.1 IPIDEA的显著优势3.2 IPIDEA的代理类型及应用 四、IPIDEA爬虫实战4…

Fragment+Viewpage2+FragmentStateAdapter实现滑动式标签布局

大家好&#xff0c;我是网创有方&#xff0c;今天记录下标签布局的实现方法&#xff0c;先看下效果图。 第一步&#xff1a;编写一个activity或者fragment。内含有一个viewpager2的适配器&#xff0c;适配器类型为FragmentStateAdapter。 ​ public class MediaCreateFragment…

VideoView视频组件

简介 VideoView 在Android中是一个用户界面组件&#xff0c;它允许开发者在Android设备的屏幕上播放视频文件。它是Android SDK的一部分&#xff0c;位于android.widget包中。VideoView提供了一种简单的方法来嵌入和控制视频播放&#xff0c;包括设置视频源、开始、暂停、 seek…

CV每日论文--2024.6.27

1、Text-Animator: Controllable Visual Text Video Generation 中文标题&#xff1a;Text-Animator&#xff1a;可控视觉文本视频生成 简介&#xff1a;视频生成是各行业中具有重要价值但同时也极具挑战性的任务,例如在游戏、电子商务和广告领域。在文本到视频(T2V)生成中,一…

前端Bug 修复手册

1.前端长整数精度丢失问题 &#xff08;1&#xff09;问题 在前后端联调时&#xff0c;发现后端有一个接口返回的值和前端页面上展示的值不一致。 后端Java实现的接口如下&#xff0c;返回一个json格式的大整数 123456789123456789&#xff1a; 但是前端请求这个接口后&…

Spring每日面试题(day1)

目录 JavaWeb三大组件依赖注入的方式Autowire和Resurce有什么区别&#xff1f;Spring Boot的优点Spring IoC是什么&#xff1f;说说Spring Aop的优点Component和Bean的区别自定义注解时使用的RetentionPolicy枚举类有哪些值&#xff1f;Spring Boot自动装配原理Spring MVC工作原…

Charles抓包工具系列文章(三)-- 接口映射工具(Map Remote和Map Local)

一、背景 前文的http请求都是静态的&#xff0c;像compose a new request&#xff0c;仅适用于接口的自测。 回放repeat 一个 http 请求&#xff0c;也无法做到动态调试。 这里的动态还是静态&#xff0c;是站在客户端的角度&#xff0c;数据是实时的&#xff0c;可调试的。 …

提升入住率|智慧酒店解决方案,打造有温度的居住体验!

近年来&#xff0c;智慧酒店被越来越多的人关注&#xff0c;由生物识别、物联网技术和互联网技术融合产生的智慧酒店解决方案&#xff0c;不仅可以提升顾客在酒店的入住体验&#xff0c;还可以帮助酒店降低运营成本&#xff0c;这也让越来越的酒店选择了智慧酒店的赛道&#xf…

人工智能--目标检测

欢迎来到 Papicatch的博客 文章目录 &#x1f349;引言 &#x1f349;概述 &#x1f348;目标检测的主要流程通常包括以下几个步骤 &#x1f34d;数据采集 &#x1f34d;数据预处理 &#x1f34d;特征提取 &#x1f34d;目标定位 &#x1f34d;目标分类 &#x1f348;…

文件夹读取难题:详解原因与数据恢复策略

一、文件夹读取不到文件的现象描述 在日常的计算机使用中&#xff0c;有时我们会遇到文件夹读取不到文件的情况。这通常表现为在尝试打开某个文件夹时&#xff0c;其中的文件列表并未正常显示&#xff0c;或者文件虽然显示但无法访问。这种问题不仅影响用户的工作效率&#xf…