当前位置: 首页 > news >正文

Redis 篇-深入了解分布式锁 Redisson 原理(可重入原理、可重试原理、主从一致性原理、解决超时锁失效)

🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍

本章目录

        1.0 基于 Redis 实现的分布式锁存在的问题

        2.0 Redisson 功能概述

        3.0 Redisson 具体使用

        4.0 Redisson 可重入锁原理

        5.0 Redisson 锁重试原理

        6.0 Redisson WatchDog 机制

        6.1 Redisson 是如何解决超时释放问题的呢?

        7.0 Redisson MultiLock 原理

        7.1 Redisson 分布式锁是如何解决主从一致性问题的呢?


        1.0 基于 Redis 实现的分布式锁存在的问题

        首先,在之前基于 setnx 实现的分布式锁存在以下问题:

        1)不可重入:同一个线程无法多次获取同一把锁。

        2)不可重试:获取锁只尝试一次就返回 false ,没有重试机制。

        当然这个机制是可以自己在判断完有无获取锁之后,再来根据业务的需求进行手动添加代码。比如说,当业务需求是:需要重复尝试获取锁。则可以在判断获取锁失败之后,等待一段时间,再去获取锁即可。

        3)超时释放:锁超时释放虽然可以避免死锁,但如果是业务执行耗时较长,也会导致锁释放,存在安全隐患。

        比如说,当业务阻塞时间较久,锁到了超时时间则会自动释放,那么其他线程就会有可能获取锁成功,这就出现了多个线程获取锁成功,从而导致线程安全问题。

        4)主从一致性:如果 Redis 提供了主从集群,主从同步延迟,当主机宕机时,如果未来得及同步到其他机器上,则就会出现多线程获取锁成功情况,从而导致线程安全问题。

        那么 Java 实现了解决以上问题的 Redisson 分布式服务类。

        2.0 Redisson 功能概述

        Redisson 是一个在 Redis 的基础上实现的 Java 驻内存数据网络。它不仅提供了一系列的分布式的 Java 常用对象,还提供了许多分布式服务,其中包含了各种分布式锁的实现。

        Redisson 解决了不可重入问题、不可重试问题、超时释放问题、主从一致性问题。

        比如说,分布式锁的可重入锁、公平锁、联锁、红锁等等。

        3.0 Redisson 具体使用

        1)引入依赖

        <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.13.6</version></dependency>

        2)配置 RedissonClient类

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RedissonConfig {@Beanpublic RedissonClient client(){//配置类Config config = new Config();//添加redis地址,这里添加了单点的地址,也可以使用config.useClusterServers()添加集群地址config.useSingleServer().setAddress("redis://8.152.162.159:6379").setPassword("****");//创建客户端return Redisson.create(config);}
}

        3)使用 RedissonClient类

    @AutowiredRedissonClient redissonClient;@Testvoid contextLoads() throws InterruptedException {//先获取锁对象,根据业务来锁定资源RLock lock = redissonClient.getLock("lock");//尝试获取锁//tryLock() 进行了重写,有无参、只有两个参数、有三个参数boolean b = lock.tryLock(1, TimeUnit.SECONDS);if (b){System.out.println("成功获取锁!");}else {System.out.println("获取锁失败!");}}

        先注入 RedissonClient 对象,根据 getLock("锁") 方法获取 RLock lock 锁对象,根据业务需要对资源进行锁定。  

        调用 lock 对象中的 tryLock() 方法来尝试获取锁,该方法进行了重写:

        1)boolean tryLock():当获取锁失败时,默认不等待,就是不重试获取锁,默认锁的超时时间为 30 秒。

        2)boolean tryLock(long time, TimeUnit unit):在 time 时间内会进行重试尝试获取锁,unit 为时间单位。默认锁的超时时间为 30 秒。

        3)boolean tryLock(long waitTime, long leaseTime, TimeUnit unit):在获取锁失败时,在 waitTime 时间内进行重试尝试获取锁,锁的超时时间为 leaseTime 秒,unit 为时间单位。

        最后,调用 lock 对象中的方法 unlock() 来释放锁。

具体代码:

    @AutowiredRedissonClient redissonClient;@Testvoid contextLoads() throws InterruptedException {//先获取锁对象,根据业务来锁定资源RLock lock = redissonClient.getLock("lock");//尝试获取锁//tryLock() 进行了重写,有无参、只有两个参数、有三个参数boolean b = lock.tryLock(1, TimeUnit.SECONDS);if (!b){System.out.println("获取锁失败!");}try {System.out.println("获取锁成功!");} catch (Exception e) {throw new RuntimeException(e);} finally {//释放锁lock.unlock();}}

        4.0 Redisson 可重入锁原理

        在之前的基于 setnx 实现的分布式锁是不支持可重入锁,举个例子:线程一来获取锁,使用 setnx 来设置,当设置成功,则获取锁成功了,线程一在获取锁成功之后,再想来获取相同的锁时,则再次执行 setnx 命令,那一定是不可能成功获取,因为 setxn 已经存在了,这就是基于 setnx 来实现分布式锁不可重入锁的核心原因。

        而对于 Redisson 可以实现可重入锁,这是如何实现的呢?

        其核心原因是基于 Redis 中的哈希结构实现的分布式锁,利用 key 来锁定资源,对于 field 来标识唯一成功获取锁的对象,而对于 value 来累计同一个线程成功获取相同的锁的次数。

        具体实现思路:

        1)尝试获取锁:

        先判断缓存中是否存在 key 字段,如果存在,则说明锁已经被成功获取,这时候需要继续判断成功获取锁的对象是否为当前线程,如果根据 key field 来判断是当前线程,则 value += 1 且还需要重置锁的超时时间;如果根据 key field 判断不是当前线程,则直接返回 null。如果缓存中不存在 key 字段,则说明锁还没有被其他线程获取,则获取锁成功。

        2)释放锁:

        当业务完成之后,在释放锁之前,先判断获取锁的对象是不是当前线程,如果不是当前线程,则说明可能由于超时,锁已经被自动释放了,这时候直接返回 null;如果是当前线程,则进行 value -= 1 ,最后再来判断 value 是否大于 0 ,当大于 0 时,则不能直接释放锁,需要重置锁的超时时间;当 value = 0 时,则可以真正的释放锁。

如图:

 

        又因为使用 Java 实现不能保证原子性,所以需要借助 Lua 脚本实现多条 Redis 命令来保证原则性。

尝试获取锁的 Lua 脚本:

释放锁的 Lua 脚本:

        5.0 Redisson 锁重试原理

        在之前基于 setnx 实现的分布式锁,获取锁只尝试一次就返回 false ,没有重试机制。

        而 Redisson 是如何实现锁重试的呢?

实现锁重试

        追踪源代码:

得到该类:

        首先,将等待时间转换为毫秒,接着获取当前时间和获取当前线程 ID ,再接着第一个尝试去获取锁,将参数 waitTime 最大等待时间,leaseTime 锁的超时时间,unit 时间单位,threadId 当前线程 ID 传进去 tryAcquire 方法中。

        紧接着来查看 tryAcquire 方法:

         再查看调用的 tryAcquireAsync 方法:

        当指定了 leaseTime 锁的超时时间,则会调用 tryLockInnerAsync 方法;当没有指定 leaseTime 锁的超时时间,则会调用 getLockWatchdogTimeout 方法,默认超时时间为 30 秒。

        接着查看 tryLockInnerAsync 方法:

         可以看到,这就是尝试获取是的 Lua 脚本执行多条 Redis 命令。

        细心可以发现,如果正常获取锁,则返回 null ;如果获取锁失败,则返回当前锁的 TTL ,锁的剩余时间。

        因此最后将当前锁的 TTL 返回赋值给 Long ttl 变量。

        再接着往下:

        当 ttl == null ,则说明当前线程成功获取锁,因此就不需要接着往下再次尝试去获取锁了。相反,当 ttl != null ,则需要接着往下走,重新尝试去获取锁。

        判断 time 等于当前时间减去在第一次获取锁之前的时间,time 也就是最大的等待时间还剩多少。判断 time 是否小于 0 ,若小于 0 则已经到了最大等待时间了,所以不需要再继续等下去了,直接返回 false 即可。

        若 time 还是大于 0 ,则接着往下走:

        调用 subscribe 方法,该方法可以理解成订阅锁,一旦锁被释放之后,该方法就会收到通知,然后再去尝试获取锁。

回顾在释放锁的时候,使用 Redis 命令中的 redis.call('publish', KEYS[2], ARGV[1]) 来发布消息,通知锁已经被释放,一旦锁被释放,那么就可以成功订阅。

        因此,在订阅锁的过程中,并不是一直死等下去,而是在 time 剩余最大等待时间之内,如果可以订阅锁成功,才会去尝试获取锁。如果在 time 时间内,订阅锁失败,则会取消订阅,再返回 false 。

        接着往下走,当在 time 时间内订阅锁成功,会更新 time 时间,也就是更新最大的等待时间,判断 time 小于 0 ,则返回 false ,如果 time 还是大于 0 ,则到了真正尝试第二次获取锁,调用 tryAcquire(waitTime, leaseTime, unit, threadId) 方法,将返回值再次赋值给变量 ttl ,判断 ttl == null ,则说明成功获取锁了,直接返回 true ;判断 ttl != null ,则第二次获取锁还是失败,由需要更新 time 了,因为在调用尝试获取锁的过程中,消耗时间还是挺大的,同理,判断更新完之后的 time 是否大于 0,如果 time 小于 0,则超过了剩余最大锁的超时时间,返回 false ;

        如果判断 time 仍旧大于 0 :

        那么先判断锁的过期时间 ttl 与 剩余时间 time ,如果 ttl < time ,则类似订阅方法一样的思路,选择等待 ttl 锁的过期时间,当 ttl 过期之后,就会订阅该锁;如果 time < ttl ,则 ttl 还没有释放,就不需要等 ttl 了,等到 time 结束还没有订阅到锁,则 time 也就小于 0 了,如果在 time 时间内获取到锁,再次尝试去获取锁,同样的,当在 ttl 时间内,成功订阅了,而且 time > 0 ,则会第三次去尝试获取锁。之后的步骤都是如此,这里使用了 do whlie 循环,判断循环成立为 time > 0,当 time < 0 ,则会退出循环。

        总结,在解决可重试锁过程中,并不是循环不断的调用 tryAcquire(waitTime, leaseTime, unit, threadId) 方法来获取锁,这样容易造成 CPU 的浪费,而是通过等待锁释放,再去获取锁的方式来实现的可重试锁,利用信号量(Semaphore)和发布/订阅(PubSub)模式实现等待、唤醒、获取锁失败的重试机制。

        6.0 Redisson WatchDog 机制

        在之前基于 setnx 实现的分布式锁,锁超时释放虽然可以避免死锁,但是如果是业务执行耗时较长,也会导致锁释放,存在安全隐患。

        6.1 Redisson 是如何解决超时释放问题的呢?

        解决超时释放的核心是:当 leaseTime == -1 时,为了保证当前业务执行完毕才能释放锁,而不是业务还没有执行完毕,锁就被自动释放了。

追踪源代码:

        当 leaseTime == -1 时,默认锁的最大超时时间为 30 秒,会执行以下代码。

        接着点进去:

        WatchDog 会在锁的过期时间到期之前,定期向 Redis 发送续约请求,更新锁的过期时间。这通常是通过设置一个较短的过期时间和一个续约间隔来实现的。

        如果持有锁的线程正常释放锁,WatchDog 会停止续约操作。如果持有锁的线程崩溃或失去响应,WatchDog 会在锁的过期时间到达后自动释放锁。

        简单概述一下 WatchDog 机制:在获取锁成功之后,就会调用 scheduleExpirationRenewal(threadId) 方法开启自动续约,具体是由在 map 中添加业务名称和任务定时器,这个定时器会在一定时间内执行,比如说 10 秒就会自动开启任务,而该定时器中的任务就是不断的重置锁的最大超时时间,使用递归,不断的调用重置锁的时间,这就保证了锁是永久被当前线程持有。 

        这样就可以保证执行业务之后,才会释放锁。释放锁之后,会取消定时任务。

        7.0 Redisson MultiLock 原理

        7.1 Redisson 分布式锁是如何解决主从一致性问题的呢?

        先搞清楚什么是主从一致性问题,在集群的 Redis 中会区分出主力机和一般机器,在写 Redis 命令会放到主力机中运行,而主力机和一般机器需要保证数据都是一样的,也就是主从同步数据,在主力机中执行写命令时,突然发生宕机,未来得及将数据同步到其他一般机器中,而且当主力机宕机之后,会选出一台一般机器充当主力机,这时候的主力机没有同步之前的数据,那么其他线程再来写命名的时候就会出现问题了,这出现了主从不一致性。

        那么 Redisson 是如何来解决该问题呢?

        在多主架构中,每台主机都可以接收写请求,这样即使某一台主机宕机,其他主机仍然可以继续处理写请求。

        当某一台主机宕机后,如果在它恢复之前有新的写操作发生,可能会导致数据不一致。通过比较不同主机的数据状态,可以很容易地发现这些不一致的问题。

        当宕机的主机恢复后,可以通过与其他主机的数据进行比较,找出差异并进行数据同步,确保所有主机的数据一致。

        简单来说,设置多台主力机,每一次写命令都是一式多份,当某一台主力机出现宕机了,主从未来得及同步时,再写命令,同样一式多份,这样充当主力机出现了跟其他主力机不同的结果时,就很容易的发现问题了。

        通过设置多台主力机并进行写操作的多份复制,可以有效提高系统的可靠性,并在出现问题时快速发现和解决数据不一致的问题。

具体使用:

相关文章:

Redis 篇-深入了解分布式锁 Redisson 原理(可重入原理、可重试原理、主从一致性原理、解决超时锁失效)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 本章目录 1.0 基于 Redis 实现的分布式锁存在的问题 2.0 Redisson 功能概述 3.0 Redisson 具体使用 4.0 Redisson 可重入锁原理 5.0 Redisson 锁重试原理 6.0 Redisson WatchDo…...

PostgreSQL中的多版本并发控制(MVCC)深入解析

引言 PostgreSQL作为一款强大的开源关系数据库管理系统&#xff0c;以其高性能、高可靠性和丰富的功能特性而广受欢迎。在并发控制方面&#xff0c;PostgreSQL采用了多版本并发控制&#xff08;MVCC&#xff09;机制&#xff0c;该机制为数据库提供了高效的数据访问和更新能力…...

SpringBoot项目-实现简单的CRUD功能和分页查询

背景 本博文主要是创建了一个新的SpringBoot项目&#xff0c;实现基本的增删改查&#xff0c;分页查询&#xff0c;带条件的分页查询功能。是方便初学者学习后端项目的一个比较清晰明了的实践代码&#xff0c;读者可根据博文&#xff0c;从自己动手创建一个新的SpringBoot项目…...

CCF编程能力等级认证GESP—C++2级—20240907

CCF编程能力等级认证GESP—C2级—20240907 单选题&#xff08;每题 2 分&#xff0c;共 30 分&#xff09;判断题&#xff08;每题 2 分&#xff0c;共 20 分&#xff09;编程题 (每题 25 分&#xff0c;共 50 分)数位之和小杨的矩阵 单选题&#xff08;每题 2 分&#xff0c;共…...

C语言手撕实战代码_二叉排序树(二叉搜索树)_构建_删除_插入操作详解

二叉排序树习题1.设计算法构建一棵二叉排序树(又称二叉搜索树BST)2.查找二叉排序树中结点为x的结点所在的层数3.删除二叉排序树T中值为x的结点4.查找二叉排序树中所有小于key的关键字5.编写算法&#xff0c;将一棵二叉树t分解成两棵二叉排序树t1和t2&#xff0c;使得t1中的所有…...

YC教父的创始人模式VS职业经理人模式:AI时代的独立开发者崛起

近年来&#xff0c;由风投资助的创始人模式因其相对较低的入门门槛而在创业圈内广受欢迎。然而&#xff0c;真正的挑战在于独立开发者&#xff08;一人商业&#xff09;模式。随着AI技术的飞速发展&#xff0c;一人商业模式有望成为未来的主流。本文将探讨独立开发者的工作范围…...

[SUCTF 2019]Pythonginx

给了源码 app.route(/getUrl, methods[GET, POST]) def getUrl():url request.args.get("url")host parse.urlparse(url).hostnameif host suctf.cc:return "我扌 your problem? 111"parts list(urlsplit(url))host parts[1]if host suctf.cc:retu…...

省市县相关校验sql随笔

1.层级校验 要判断一个给定的省、市、区&#xff08;县&#xff09;名字是否符合正确的层级关系,假设你的表结构如下&#xff1a; CREATE TABLE regions (id INT PRIMARY KEY,name VARCHAR(255),parent_id INT, -- 指向上一级区域的id&#xff0c;例如市的parent_id指向省的…...

uniapp ios sticky定位,内部 u-tabs(包含scroll-view)消失问题

uniapp中用sticky定位时&#xff0c;元素内部如果有scroll-view&#xff0c;ios在触发bounce机制时&#xff0c;scroll-view的元素会消失&#xff0c;解决方法是页面上包一层高度为100vh的scroll-view <scroll-view style"height: 100vh;" scroll-y scrolltolowe…...

web群集--nginx配置文件location匹配符的优先级顺序详解及验证

文章目录 前言优先级顺序优先级顺序(详解)1. 精确匹配&#xff08;Exact Match&#xff09;2. 正则表达式匹配&#xff08;Regex Match&#xff09;3. 前缀匹配&#xff08;Prefix Match&#xff09; 匹配规则的综合应用验证优先级 前言 location的作用 在 NGINX 中&#xff0…...

Vivado编译报错黑盒子问题

1 问题描述 “Black Box Instances: Cell **** of type ** has undefined contents and is considered a back box. The contents of this cell must be defined for opt_design to complete successfully.” 检查工程代码提示的模块&#xff0c;该模块为纯手写的Veril…...

【建造者模式】

建造者模式 Builder Pattern 属于创建型模式是将一个复杂对象的构建与它的标识分离&#xff0c;使得同样的构建过程可以创建不同的表示关键点&#xff1a;用户只需要指定需要建造的类型就可以获得对象&#xff0c;建造过程及细节不需要了解 实现 demo 需要构建的对象 Data pu…...

自动化表格处理的革命:智能文档系统技术解析

在当今数据驱动的商业环境中&#xff0c;表格数据的自动化处理成为了企业提高效率、降低成本的关键。企业智能文档系统在智能表格识别方面展现出卓越的性能&#xff0c;通过精准识别和处理各种通用表格&#xff0c;显著提升了企业文档管理的智能化水平。本文将深入探讨该系统在…...

【Hot100】LeetCode—394. 字符串解码

目录 1- 思路栈实现四种情况处理 2- 实现⭐394. 字符串解码——题解思路 3- ACM 实现 原题链接&#xff1a;394. 字符串解码 1- 思路 栈实现四种情况处理 ① 遇到数字&#xff0c;进行倍数相加 、②遇到左括号&#xff0c;压栈之前的元素、③遇到右括号弹出&#xff0c;栈进行…...

12. 如何在MyBatis中进行分页查询?常见的分页实现方式有哪些?

在MyBatis中&#xff0c;分页查询是一种常见的需求&#xff0c;尤其是在处理大数据量的情况下。MyBatis本身不直接提供分页功能&#xff0c;但可以通过以下几种常见的实现方式来实现分页查询。 1. 手动分页 这是最基本的分页方式&#xff0c;直接在SQL语句中添加分页参数。不同…...

@[TOC](力扣题目-滑动窗口-qsort排序-二分法查找)

通信 LCR 009. 乘积小于 K 的子数组268. 丢失的数字287. 寻找重复数 LCR 009. 乘积小于 K 的子数组 已解答 滑动窗口 给定一个正整数数组 nums和整数 k &#xff0c;请找出该数组内乘积小于 k 的连续的子数组的个数。 示例 1: 输入: nums [10,5,2,6], k 100 输出: 8 解释…...

Docker容器相关命令

Docker是一种容器化技术&#xff0c;可以帮助用户更轻松地创建、部署和管理容器。下面是一些常见的Docker容器管理任务&#xff1a; 创建容器&#xff1a;使用Docker镜像创建一个新的容器。 docker run image_name列出容器&#xff1a;查看当前运行的容器列表。 docker ps启动容…...

【老课推荐】基于LangChain和知识图谱的大模型医疗问答机器人项目

在当今数据驱动和人工智能主导的时代&#xff0c;大模型和知识图谱的结合是一个重要的研究和应用方向。大模型实战课程通过48课时&#xff0c;分为六个主要章节&#xff0c;涵盖了从基本概念到高级应用的多方面内容。学员将通过本课程学习如何使用LangChain和OpenAI进行开发&am…...

Adobe Sensei——自动化视频编辑、特效应用和素材增强,通过AI技术快速优化视频内容,自动修复视频质量、自动添加背景音乐或字幕

一、Adobe Sensei介绍 Adobe Sensei 是 Adobe 公司开发的一款基于人工智能和机器学习技术的平台&#xff0c;旨在增强其各种创意、文档和体验管理工具。Adobe Sensei 通过深度学习、计算机视觉、自然语言处理&#xff08;NLP&#xff09;等先进技术&#xff0c;帮助用户在 Ado…...

【AIGC数字人】EchoMimic:基于可编辑关键点条件的类人音频驱动肖像动画

GitHub&#xff1a;https://github.com/BadToBest/EchoMimic 论文&#xff1a; https://arxiv.org/pdf/2407.08136 comfyui&#xff1a; https://github.com/smthemex/ComfyUI_EchoMimic 相关工作 Wav2Lip Wav2Lip是一个开创性的工作 &#xff0c;但输出会出现面部模糊或扭…...

变量数据类型 Day3

1. 变量 1.1 变量的概念 变量是计算机内存中的一块存储单元&#xff0c;是存储数据的基本单元变量的组成包括&#xff1a;数据类型、变量名、值&#xff0c;后文会具体描述变量的本质作用就是去记录数据的&#xff0c;比如说记录一个人的身高、体重、年龄&#xff0c;就需要去…...

SpringBoot2:请求处理原理分析-RESTFUL风格接口

一、RESTFUL简介 Rest风格支持&#xff08;使用HTTP请求方式&#xff0c;动词来表示对资源的操作&#xff09; 以前&#xff1a;/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户 现在&#xff1a; /user GET-获取用户 DELETE-删除用户 PUT-修改…...

[Linux][配置]Linux修改history存储的最大记录数

Linux修改History最大记录为20000行 sed -i s/^HISTSIZE1000/HISTSIZE20000/ /etc/profile source /etc/profile 在 Linux 系统中&#xff0c;HISTSIZE 环境变量用于定义历史记录的大小&#xff0c;即在终端中可以回溯的命令数量。默认情况下&#xff0c;这个值通常是 1000&…...

代码随想录 刷题记录-28 图论 (5)最短路径

一、dijkstra&#xff08;朴素版&#xff09;精讲 47. 参加科学大会 思路 本题就是求最短路&#xff0c;最短路是图论中的经典问题即&#xff1a;给出一个有向图&#xff0c;一个起点&#xff0c;一个终点&#xff0c;问起点到终点的最短路径。 接下来讲解最短路算法中的 d…...

大数据-124 - Flink State 01篇 状态原理和原理剖析:状态类型 执行分析

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…...

C++复习day04

一、函数重载 1.什么是函数重载&#xff1f; 自然语言中&#xff0c;一个词可以有多重含义&#xff0c;人们可以通过上下文来判断该词真实的含义&#xff0c;即该词被重 载了。 比如&#xff1a;以前有一个笑话&#xff0c;国有两个体育项目大家根本不用看&#xff0c;也不用…...

[苍穹外卖]-04菜品管理接口开发

效果预览 新增菜品 需求分析 查看产品原型分析需求, 包括用到哪些接口, 业务的限制规则 业务规则 菜品名称必须是唯一的菜品必须属于某个分类下, 不能单独存在新增菜品时可以根据情况选择菜品的口味每个菜品必须对应一张图片 接口设计 根据类型查询分类接口 文件上传接口 …...

gitlab 启动/关闭/启用开机启动/禁用开机启动

文章目录 启动 gitlab关闭 gitlab查看 gitlab 运行状态启用 gitlab 开机启动禁用 gitlab 开机启动GitlabGit启动 gitlab hxstrive@localhost:~$ sudo gitlab-ctl start ok: run: alertmanager: (pid 65953) 0s ok: run: gitaly: (pid 65965) 0s ok: run: gitlab-exporter: (pi…...

中间件解析漏洞(附环境搭建教程)

⼀&#xff1a;IIS解析漏洞 环境资源&#xff1a; https://download.csdn.net/download/Nai_zui_jiang/89717504 环境安装 windows2003iis6 1.创建新的虚拟机 2.在下⼀步中选择我们的iso⽂件镜像 vm已主动识别到windows2003 3.产品密钥⽹上搜⼀个 密码自己设置一个简单的&…...

matlab实现kaiser窗+时域采样序列(不管原信号拉伸成什么样子)是一样的,变到频谱后再采样就是一样的频域序列。

下图窗2的频谱在周期化的时候应该是2&#xff08;w-k*pi/T&#xff09;我直接对2w减得写错了 可见这两个kaiser窗频谱不一样&#xff0c;采样间隔为2T的窗&#xff0c;频谱压缩2倍&#xff0c;且以原采样频率的一半周期化。 但是这两个不同的kaiser窗在频域采样点的值使完全一…...