【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设为1024
            serverChannel.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() {
   //循环遍历selector
   while(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()){
            //获得关心当前事件的channel
            ServerSocketChannel 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();
    //根据数组容量创建ByteBuffer
    ByteBuffer 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主线程去执行,从而保证减少一些并发冲突

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

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

相关文章

沙箱在“一机两用”新规下的价值体现

在数字化时代&#xff0c;随着企业信息化建设的深入&#xff0c;数据安全问题愈发凸显其重要性。一机两用新规的出台&#xff0c;旨在通过技术创新和管理手段&#xff0c;实现终端设备的安全可控&#xff0c;确保敏感数据的安全存储与传输。SDC沙箱技术作为一种创新的安全防护手…

NLP篇1

场景&#xff1a;假设给你一篇文章。 目标&#xff1a;说白了&#xff0c;就是数学的分类。但是如何实现分类呢。下面将逐步一 一 分析与拆解。先把目标定好了和整体框架定好了。而不是只见树木而不见森林。 情感分类&#xff08;好评、差评&#xff0c;中性&#xff09; 整体…

基于“香港世界”的SLAM技术介绍

在视觉感知技术中&#xff0c;理解和描述复杂的三维室外场景至关重要&#xff0c;尤其是自动驾驶技术的发展要求对陌生环境具有更强的适应能力和鲁棒性。传统上&#xff0c;使用“曼哈顿世界”和“亚特兰大世界”模型来描述具有垂直和水平结构的城市场景。 当遇到像香港这样地形…

burpsuite 设置监听窗口 火狐利用插件快速切换代理状态

一、修改burpsuite监听端口 1、首先打开burpsuite&#xff0c;点击Proxy下的Options选项&#xff1a; 2、可以看到默认的监听端口为8080&#xff0c;首先选中我们想要修改的监听&#xff0c;点击Edit进行编辑 3、将端口改为9876&#xff0c;并保存 4、可以看到监听端口修改成功…

云卓SKYDROID-H30——科技改变未来

云卓H30采用高通处理器、搭载安卓嵌入式系统&#xff0c;拥有三个工作频率&#xff0c;让图像更清晰、延迟更低、距离远、抗干扰性强&#xff0c;支持多种接口&#xff0c;更有10.1寸高清工业级阳光可视屏&#xff0c;防尘耐磨&#xff0c;结构强度高&#xff0c;适用于各种严苛…

前端利用vue如何实现导入和导出功能.md

1. 前端利用vue如何实现导入和到处功能 1.1. 导入功能&#xff08;以导入Excel文件为例&#xff09; 1.1.1. 实现步骤: 1.1.1.1. 安装依赖: 首先&#xff0c;你需要安装处理Excel文件的库&#xff0c;如xlsx。1.1.1.2. 创建上传组件: 使用Element UI的<el-upload>组件或其…

windows USB 驱动开发-URB结构

通用串行总线 (USB) 客户端驱动程序无法直接与其设备通信。 相反&#xff0c;客户端驱动程序会创建请求并将其提交到 USB 驱动程序堆栈进行处理。 在每个请求中&#xff0c;客户端驱动程序提供一个可变长度的数据结构&#xff0c;称为 USB 请求块 (URB) &#xff0c;URB 结构描…

zdppy_api+vue3+antd开发前后端分离的tab卡片

后端代码 import api import uploadsave_dir "uploads"async def rand_content(request):key api.req.get_query(request, "key")return api.resp.success(f"{key} " * 100)app api.Api(routes[api.resp.get("/", rand_content),u…

2024科技文化节程序设计竞赛

补题链接 https://www.luogu.com.cn/contest/178895#problems A. 签到题 忽略掉大小为1的环&#xff0c;答案是剩下环的大小和减环的数量 #include<bits/stdc.h> #include<iostream> #include<cstdio> #include<vector> #include<map> #incl…

Victor CMS v1.0 SQL 注入漏洞(CVE-2022-28060)

前言 CVE-2022-28060 是 Victor CMS v1.0 中的一个SQL注入漏洞。该漏洞存在于 /includes/login.php 文件中的 user_name 参数。攻击者可以通过发送特制的 SQL 语句&#xff0c;利用这个漏洞执行未授权的数据库操作&#xff0c;从而访问或修改数据库中的敏感信息。 漏洞详细信…

mac安装达梦数据库

参考&#xff1a;mac安装达梦数据库​​​​​​ 实践如下&#xff1a; 1、下载达梦Docker镜像文件 同参考链接 2、导入镜像 镜像可以随便放在某个目录&#xff0c;相当于安装包&#xff0c;导入后就没有作用了。 查找达梦镜像名称&#xff1a;dm8_20240613_rev229704_x86…

第11章 规划过程组(11.6规划进度管理)

第11章 规划过程组&#xff08;二&#xff09;11.6规划进度管理&#xff0c;在第三版教材第385页&#xff1b;#软考中级##中级系统集成项目管理师# 文字图片音频方式 第一个知识点&#xff1a;主要输出 1、进度管理计划 准确度 定义活动持续时间估算的可接受区间&#xff0…

Pycharm常用快捷键整理

1&#xff0c;格式化代码 【ctrlAltL】 写代码的时候会发现有很多黄色的波浪号&#xff0c;这个时候可以点击任意黄色波浪号的代码&#xff0c;然后按下【Ctrl Alt L】进行代码格式化 2&#xff0c;快速往返 ctrll Alt ⬅ &#xff0c;表示查看上一步调用函数位置&#xff0…

Oracle 视图、存储过程、函数、序列、索引、同义词、触发器

优质博文&#xff1a;IT-BLOG-CN 一、视图 从表中抽出的逻辑上相关的数据集合&#xff0c;视图是一种虚表&#xff0c;视图是建立在已有表的基础之上&#xff0c;视图赖以建立的这些表称为基表。向视图提供数据的是 SELECT语句&#xff0c;可以将视图理解为存储起来的SELECT语…

KV260视觉AI套件--PYNQ-DPU-Resnet50

目录 1. 简介 2. 代码解析 3. 全部代码展示 4. 总结 1. 简介 Resnet50 一种深度卷积神经网络&#xff08;CNN&#xff09;&#xff0c;它由50层构成。这种网络特别设计用于图像识别任务&#xff0c;并且在2015年的ImageNet大规模视觉识别挑战赛&#xff08;ILSVRC&#x…

notepad++安装并打开json文件

1、notepad安装 1、首先下载Notepad.exe 2、选择简体中文安装 点击下一步 点击“我接受” 选择安装目录&#xff0c;进行下一步安装 默认下一步 选择安装 等待安装完成 点击完成 2、保存json文件 复制返回结果 先把返回结果复制出来。保存到text里面 把文件另存为json格式 3、…

Mac搭建anaconda环境并安装深度学习库

1. 下载anaconda安装包 根据自己的操作系统不同&#xff0c;选择不同的安装包Anaconda3-2024.06-1-MacOSX-x86_64.pkg&#xff0c;我用的还是旧的intel所以下载这个&#xff0c;https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/&#xff0c;如果mac用的是M1&#xff0…

通过百度文心智能体创建STM32编程助手-实操

一、前言 文心智能体平台AgentBuilder 是百度推出的基于文心大模型的智能体&#xff08;Agent&#xff09;平台&#xff0c;支持广大开发者根据自身行业领域、应用场景&#xff0c;选取不同类型的开发方式&#xff0c;打造大模型时代的产品能力。开发者可以通过 prompt 编排的…

拍摄的vlog视频画质模糊怎么办?视频画质高清修复

在短视频逐渐成为主流的今天&#xff0c;许多朋友都会通过vlog的形式记录下自己的生活。但我们会发现&#xff0c;自己拍摄的视频与专业博主拍摄的视频&#xff0c;在画质上就会有所差别&#xff0c;拍摄的vlog视频画质模糊不清晰怎么办&#xff1f; 拍摄的vlog视频画质模糊怎么…

昇思第6天

函数式自动微分 神经网络的训练主要使用反向传播算法&#xff0c;模型预测值&#xff08;logits&#xff09;与正确标签&#xff08;label&#xff09;送入损失函数&#xff08;loss function&#xff09;获得loss&#xff0c;然后进行反向传播计算&#xff0c;求得梯度&#…