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

分布式锁—6.Redisson的同步器组件

大纲

1.Redisson的分布式锁简单总结

2.Redisson的Semaphore简介

3.Redisson的Semaphore源码剖析

4.Redisson的CountDownLatch简介

5.Redisson的CountDownLatch源码剖析

1.Redisson的分布式锁简单总结

(1)可重入锁RedissonLock

(2)公平锁RedissonFairLock

(3)联锁MultiLock

(4)红锁RedLock

(5)读写锁之读锁RedissonReadLock和写锁RedissonWriteLock

Redisson分布式锁包括:可重入锁、公平锁、联锁、红锁、读写锁。

(1)可重入锁RedissonLock

非公平锁,最基础的分布式锁,最常用的锁。

(2)公平锁RedissonFairLock

各个客户端尝试获取锁时会排队,按照队列的顺序先后获取锁。

(3)联锁MultiLock

可以一次性加多把锁,从而实现一次性锁多个资源。

(4)红锁RedLock

RedLock相当于一把锁。虽然利用了MultiLock包裹了多个小锁,但这些小锁并不对应多个资源,而是每个小锁的key对应一个Redis实例。只要大多数的Redis实例加锁成功,就可以认为RedLock加锁成功。RedLock的健壮性要比其他普通锁要好。

但是RedLock也有一些场景无法保证正确性,当然RedLock只要求部署主库。比如客户端A尝试向5个Master实例加锁,但仅仅在3个Maste中加锁成功。不幸的是此时3个Master中有1个Master突然宕机了,而且锁key还没同步到该宕机Master的Slave上,此时Salve切换为Master。于是在这5个Master中,由于其中有一个是新切换过来的Master,所以只有2个Master是有客户端A加锁的数据,另外3个Master是没有锁的。但继续不幸的是,此时客户端B来加锁,那么客户端B就很有可能成功在没有锁数据的3个Master上加到锁,从而满足了过半数加锁的要求,最后也完成了加锁,依然发生重复加锁。

(5)读写锁之读锁RedissonReadLock和写锁RedissonWriteLock

不同客户端线程的四种加锁情况:

情况一:先加读锁再加读锁,不互斥

情况二:先加读锁再加写锁,互斥

情况三:先加写锁再加读锁,互斥

情况四:先加写锁再加写锁,互斥

同一个客户端线程的四种加锁情况:

情况一:先加读锁再加读锁,不互斥

情况二:先加读锁再加写锁,互斥

情况三:先加写锁再加读锁,不互斥

情况四:先加写锁再加写锁,不互斥

2.Redisson的Semaphore简介

(1)Redisson的Semaphore原理图

Semaphore也是Redisson支持的一种同步组件。Semaphore作为一个锁机制,可以允许多个线程同时获取一把锁。任何一个线程释放锁之后,其他等待的线程就可以尝试继续获取锁。

图片

(2)Redisson的Semaphore使用演示

public class RedissonDemo {public static void main(String[] args) throws Exception {//连接3主3从的Redis CLusterConfig config = new Config();...//SemaphoreRedissonClient redisson = Redisson.create(config);final RSemaphore semaphore = redisson.getSemaphore("semaphore");semaphore.trySetPermits(3);for (int i = 0; i < 10; i++) {new Thread(new Runnable() {public void run() {try {System.out.println(new Date() + ":线程[" + Thread.currentThread().getName() + "]尝试获取Semaphore锁");semaphore.acquire();System.out.println(new Date() + ":线程[" + Thread.currentThread().getName() + "]成功获取到了Semaphore锁,开始工作");Thread.sleep(3000);semaphore.release();System.out.println(new Date() + ":线程[" + Thread.currentThread().getName() + "]释放Semaphore锁");} catch (Exception e) {e.printStackTrace();}}}).start();}}
}

3.Redisson的Semaphore源码剖析

(1)Semaphore的初始化

(2)Semaphore设置允许获取的锁数量

(3)客户端尝试获取Semaphore的锁

(4)客户端释放Semaphore的锁

(1)Semaphore的初始化

public class Redisson implements RedissonClient {//Redis的连接管理器,封装了一个Config实例protected final ConnectionManager connectionManager;//Redis的命令执行器,封装了一个ConnectionManager实例protected final CommandAsyncExecutor commandExecutor;...protected Redisson(Config config) {this.config = config;Config configCopy = new Config(config);//初始化Redis的连接管理器connectionManager = ConfigSupport.createConnectionManager(configCopy);...  //初始化Redis的命令执行器commandExecutor = new CommandSyncService(connectionManager, objectBuilder);...}@Overridepublic RSemaphore getSemaphore(String name) {return new RedissonSemaphore(commandExecutor, name);}...
}public class RedissonSemaphore extends RedissonExpirable implements RSemaphore {private final SemaphorePubSub semaphorePubSub;final CommandAsyncExecutor commandExecutor;public RedissonSemaphore(CommandAsyncExecutor commandExecutor, String name) {super(commandExecutor, name);this.commandExecutor = commandExecutor;this.semaphorePubSub = commandExecutor.getConnectionManager().getSubscribeService().getSemaphorePubSub();}...
}

(2)Semaphore设置允许获取的锁数量

public class RedissonSemaphore extends RedissonExpirable implements RSemaphore {...@Overridepublic boolean trySetPermits(int permits) {return get(trySetPermitsAsync(permits));}@Overridepublic RFuture<Boolean> trySetPermitsAsync(int permits) {RFuture<Boolean> future = commandExecutor.evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,//执行命令"get semaphore",获取到当前的数值"local value = redis.call('get', KEYS[1]); " +"if (value == false) then " +//然后执行命令"set semaphore 3"//设置这个信号量允许客户端同时获取锁的总数量为3"redis.call('set', KEYS[1], ARGV[1]); " +"redis.call('publish', KEYS[2], ARGV[1]); " +"return 1;" +"end;" +"return 0;",Arrays.asList(getRawName(), getChannelName()),permits);if (log.isDebugEnabled()) {future.onComplete((r, e) -> {if (r) {log.debug("permits set, permits: {}, name: {}", permits, getName());} else {log.debug("unable to set permits, permits: {}, name: {}", permits, getName());}});}return future;}...
}

首先执行命令"get semaphore",获取到当前的数值。然后执行命令"set semaphore 3",也就是设置这个信号量允许客户端同时获取锁的总数量为3。

(3)客户端尝试获取Semaphore的锁

public class RedissonSemaphore extends RedissonExpirable implements RSemaphore {...private final SemaphorePubSub semaphorePubSub;final CommandAsyncExecutor commandExecutor;public RedissonSemaphore(CommandAsyncExecutor commandExecutor, String name) {super(commandExecutor, name);this.commandExecutor = commandExecutor;this.semaphorePubSub = commandExecutor.getConnectionManager().getSubscribeService().getSemaphorePubSub();}@Overridepublic void acquire() throws InterruptedException {acquire(1);}@Overridepublic void acquire(int permits) throws InterruptedException {if (tryAcquire(permits)) {return;}CompletableFuture<RedissonLockEntry> future = subscribe();commandExecutor.syncSubscriptionInterrupted(future);try {while (true) {if (tryAcquire(permits)) {return;}//获取Redisson的Semaphore失败,于是便调用本地JDK的Semaphore的acquire()方法,此时当前线程会被阻塞//之后如果Redisson的Semaphore释放了锁,那么当前客户端便会通过监听订阅事件释放本地JDK的Semaphore,唤醒被阻塞的线程,继续执行while循环//注意:getLatch()返回的是JDK的Semaphore = "new Semaphore(0)" ==> (state - permits)//首先调用CommandAsyncService.getNow()方法//然后调用RedissonLockEntry.getLatch()方法//接着调用JDK的Semaphore的acquire()方法commandExecutor.getNow(future).getLatch().acquire();}} finally {unsubscribe(commandExecutor.getNow(future));}}@Overridepublic boolean tryAcquire(int permits) {//异步转同步return get(tryAcquireAsync(permits));}@Overridepublic RFuture<Boolean> tryAcquireAsync(int permits) {if (permits < 0) {throw new IllegalArgumentException("Permits amount can't be negative");}if (permits == 0) {return RedissonPromise.newSucceededFuture(true);}return commandExecutor.evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,//执行命令"get semaphore",获取到当前值"local value = redis.call('get', KEYS[1]); "+//如果semaphore的当前值不是false,且大于客户端线程申请获取锁的数量"if (value ~= false and tonumber(value) >= tonumber(ARGV[1])) then " +//执行"decrby semaphore 1",将信号量允许获取锁的总数量递减1"local val = redis.call('decrby', KEYS[1], ARGV[1]); " +"return 1; " +"end; " +//如果semaphore的值变为0,那么客户端就无法获取锁了,此时返回false"return 0;",Collections.<Object>singletonList(getRawName()),permits//ARGV[1]默认是1);}...
}public class CommandAsyncService implements CommandAsyncExecutor {...@Overridepublic <V> V getNow(CompletableFuture<V> future) {try {return future.getNow(null);} catch (Exception e) {return null;}}...
}public class RedissonLockEntry implements PubSubEntry<RedissonLockEntry> {private final Semaphore latch;...public RedissonLockEntry(CompletableFuture<RedissonLockEntry> promise) {super();this.latch = new Semaphore(0);this.promise = promise;}public Semaphore getLatch() {return latch;}...
}

执行命令"get semaphore",获取到semaphore的当前值。如果semaphore的当前值不是false,且大于客户端线程申请获取锁的数量。那么就执行"decrby semaphore 1",将信号量允许获取锁的总数量递减1。

如果semaphore的值变为0,那么客户端就无法获取锁了,此时tryAcquire()方法返回false。表示获取semaphore的锁失败了,于是当前客户端线程便会通过本地JDK的Semaphore进行阻塞。

当客户端后续收到一个订阅事件把本地JDK的Semaphore进行释放后,便会唤醒阻塞线程继续while循环。在while循环中,会不断尝试获取这个semaphore的锁,如此循环往复,直到成功获取。

(4)客户端释放Semaphore的锁

public class RedissonSemaphore extends RedissonExpirable implements RSemaphore {...@Overridepublic void release() {release(1);}@Overridepublic void release(int permits) {get(releaseAsync(permits));}@Overridepublic RFuture<Void> releaseAsync(int permits) {if (permits < 0) {throw new IllegalArgumentException("Permits amount can't be negative");}if (permits == 0) {return RedissonPromise.newSucceededFuture(null);}RFuture<Void> future = commandExecutor.evalWriteAsync(getRawName(), StringCodec.INSTANCE, RedisCommands.EVAL_VOID,//执行命令"incrby semaphore 1""local value = redis.call('incrby', KEYS[1], ARGV[1]); " +"redis.call('publish', KEYS[2], value); ",Arrays.asList(getRawName(), getChannelName()),permits);if (log.isDebugEnabled()) {future.onComplete((o, e) -> {if (e == null) {log.debug("released, permits: {}, name: {}", permits, getName());}});}return future;}...
}//订阅semaphore不为0的事件,semaphore不为0时会触发执行这里的监听回调
public class SemaphorePubSub extends PublishSubscribe<RedissonLockEntry> {public SemaphorePubSub(PublishSubscribeService service) {super(service);}@Overrideprotected RedissonLockEntry createEntry(CompletableFuture<RedissonLockEntry> newPromise) {return new RedissonLockEntry(newPromise);}@Overrideprotected void onMessage(RedissonLockEntry value, Long message) {Runnable runnableToExecute = value.getListeners().poll();if (runnableToExecute != null) {runnableToExecute.run();}//将客户端本地JDK的Semaphore进行释放value.getLatch().release(Math.min(value.acquired(), message.intValue()));}
}//订阅锁被释放的事件,锁被释放为0时会触发执行这里的监听回调
public class LockPubSub extends PublishSubscribe<RedissonLockEntry> {public static final Long UNLOCK_MESSAGE = 0L;public static final Long READ_UNLOCK_MESSAGE = 1L;public LockPubSub(PublishSubscribeService service) {super(service);}  @Overrideprotected RedissonLockEntry createEntry(CompletableFuture<RedissonLockEntry> newPromise) {return new RedissonLockEntry(newPromise);}@Overrideprotected void onMessage(RedissonLockEntry value, Long message) {if (message.equals(UNLOCK_MESSAGE)) {Runnable runnableToExecute = value.getListeners().poll();if (runnableToExecute != null) {runnableToExecute.run();}value.getLatch().release();} else if (message.equals(READ_UNLOCK_MESSAGE)) {while (true) {Runnable runnableToExecute = value.getListeners().poll();if (runnableToExecute == null) {break;}runnableToExecute.run();}//将客户端本地JDK的Semaphore进行释放value.getLatch().release(value.getLatch().getQueueLength());}}
}

客户端释放Semaphore的锁时,会执行命令"incrby semaphore 1"。每当客户端释放掉permits个锁,就会将信号量的值累加permits,这样Semaphore信号量的值就不再是0了。然后通过publish命令发布一个事件,之后订阅了该事件的其他客户端都会对getLatch()返回的本地JDK的Semaphore进行加1。于是其他客户端正在被本地JDK的Semaphore进行阻塞的线程,就会被唤醒继续执行。此时其他客户端就可以尝试获取到这个信号量的锁,然后再次将这个Semaphore的值递减1。

4.Redisson的CountDownLatch简介

(1)Redisson的CountDownLatch原理图解

(2)Redisson的CountDownLatch使用演示

(1)Redisson的CountDownLatch原理图解

CountDownLatch的基本原理:要求必须有n个线程来进行countDown,才能让执行await的线程继续执行。如果没有达到指定数量的线程来countDown,会导致执行await的线程阻塞。

图片

(2)Redisson的CountDownLatch使用演示

public class RedissonDemo {public static void main(String[] args) throws Exception {//连接3主3从的Redis CLusterConfig config = new Config();...//CountDownLatchfinal RedissonClient redisson = Redisson.create(config);RCountDownLatch latch = redisson.getCountDownLatch("myCountDownLatch");//1.设置可以countDown的数量为3latch.trySetCount(3);System.out.println(new Date() + ":线程[" + Thread.currentThread().getName() + "]设置了必须有3个线程执行countDown,进入等待中。。。");for (int i = 0; i < 3; i++) {new Thread(new Runnable() {public void run() {try {System.out.println(new Date() + ":线程[" + Thread.currentThread().getName() + "]在做一些操作,请耐心等待。。。。。。");Thread.sleep(3000);RCountDownLatch localLatch = redisson.getCountDownLatch("myCountDownLatch");localLatch.countDown();System.out.println(new Date() + ":线程[" + Thread.currentThread().getName() + "]执行countDown操作");} catch (Exception e) {e.printStackTrace();}}}).start();}latch.await();System.out.println(new Date() + ":线程[" + Thread.currentThread().getName() + "]收到通知,有3个线程都执行了countDown操作,可以继续往下执行");}
}

5.Redisson的CountDownLatch源码剖析

(1)CountDownLatch的初始化

(2)trySetCount()方法设置countDown的数量

(3)awati()方法进行阻塞等待

(4)countDown()方法对countDown的数量递减

(1)CountDownLatch的初始化

public class Redisson implements RedissonClient {//Redis的连接管理器,封装了一个Config实例protected final ConnectionManager connectionManager;//Redis的命令执行器,封装了一个ConnectionManager实例protected final CommandAsyncExecutor commandExecutor;...protected Redisson(Config config) {this.config = config;Config configCopy = new Config(config);//初始化Redis的连接管理器connectionManager = ConfigSupport.createConnectionManager(configCopy);...  //初始化Redis的命令执行器commandExecutor = new CommandSyncService(connectionManager, objectBuilder);...}@Overridepublic RCountDownLatch getCountDownLatch(String name) {return new RedissonCountDownLatch(commandExecutor, name);}...
}public class RedissonCountDownLatch extends RedissonObject implements RCountDownLatch {...private final CountDownLatchPubSub pubSub;private final String id;protected RedissonCountDownLatch(CommandAsyncExecutor commandExecutor, String name) {super(commandExecutor, name);this.id = commandExecutor.getConnectionManager().getId();this.pubSub = commandExecutor.getConnectionManager().getSubscribeService().getCountDownLatchPubSub();}...
}

(2)trySetCount()方法设置countDown的数量

trySetCount()方法的工作就是执行命令"set myCountDownLatch 3"。

public class RedissonCountDownLatch extends RedissonObject implements RCountDownLatch {...@Overridepublic boolean trySetCount(long count) {return get(trySetCountAsync(count));}@Overridepublic RFuture<Boolean> trySetCountAsync(long count) {return commandExecutor.evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,"if redis.call('exists', KEYS[1]) == 0 then " +"redis.call('set', KEYS[1], ARGV[2]); " +"redis.call('publish', KEYS[2], ARGV[1]); " +"return 1 " +"else " +"return 0 " +"end",Arrays.asList(getRawName(), getChannelName()),CountDownLatchPubSub.NEW_COUNT_MESSAGE,count);}...
}

(3)awati()方法进行阻塞等待

public class RedissonCountDownLatch extends RedissonObject implements RCountDownLatch {...@Overridepublic void await() throws InterruptedException {if (getCount() == 0) {return;}CompletableFuture<RedissonCountDownLatchEntry> future = subscribe();try {commandExecutor.syncSubscriptionInterrupted(future);while (getCount() > 0) {// waiting for open state//获取countDown的数量还大于0,就先阻塞线程,然后再等待唤醒,执行while循环//其中getLatch()返回的是JDK的semaphore = "new Semaphore(0)" ==> (state - permits)commandExecutor.getNow(future).getLatch().await();}} finally {unsubscribe(commandExecutor.getNow(future));}}@Overridepublic long getCount() {return get(getCountAsync());}@Overridepublic RFuture<Long> getCountAsync() {//执行命令"get myCountDownLatch"return commandExecutor.writeAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.GET_LONG, getRawName());}...
}

在while循环中,首先会执行命令"get myCountDownLatch"去获取countDown值。如果该值不大于0,就退出循环不阻塞线程。如果该值大于0,则说明还没有指定数量的线程去执行countDown操作,于是就会先阻塞线程,然后再等待唤醒来继续循环。

(4)countDown()方法对countDown的数量递减

public class RedissonCountDownLatch extends RedissonObject implements RCountDownLatch {...@Overridepublic void countDown() {get(countDownAsync());}@Overridepublic RFuture<Void> countDownAsync() {return commandExecutor.evalWriteNoRetryAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,"local v = redis.call('decr', KEYS[1]);" +"if v <= 0 then redis.call('del', KEYS[1]) end;" +"if v == 0 then redis.call('publish', KEYS[2], ARGV[1]) end;",Arrays.<Object>asList(getRawName(), getChannelName()),CountDownLatchPubSub.ZERO_COUNT_MESSAGE);}...
}

countDownAsync()方法会执行decr命令,将countDown的数量进行递减1。如果这个值已经小于等于0,就执行del命令删除掉该CoutDownLatch。如果是这个值为0,还会发布一条消息:

publish redisson_countdownlatch__channel__{anyCountDownLatch} 0

相关文章:

分布式锁—6.Redisson的同步器组件

大纲 1.Redisson的分布式锁简单总结 2.Redisson的Semaphore简介 3.Redisson的Semaphore源码剖析 4.Redisson的CountDownLatch简介 5.Redisson的CountDownLatch源码剖析 1.Redisson的分布式锁简单总结 (1)可重入锁RedissonLock (2)公平锁RedissonFairLock (3)联锁MultiL…...

文献分享: ConstBERT固定数目向量编码文档

&#x1f602;图放这了&#xff0c;大道至简的 idea \text{idea} idea不愧是 ECIR \text{ECIR} ECIR &#x1f449;原论文 1. ConstBERT \textbf{1. ConstBERT} 1. ConstBERT的原理 1️⃣模型的改进点&#xff1a;相较于 ColBERT \text{ColBERT} ColBERT为每个 Token \text{Tok…...

如何使用SSH命令安全连接并转发端口到远程服务器

ssh -p 22546 rootconnect.westc.gpuhub.com d6IS/mQKq/iG ssh -CNgv -L 6006:127.0.0.1:6006 rootconnect.westc.gpuhub.com -p 22546 第一条命令&#xff1a;用于登录远程服务器&#xff0c;进行交互式操作。第二条命令&#xff1a;用于建立 SSH 隧道&#xff0c;进行端口转…...

SolidWorks 转 PDF3D 技术详解

在现代工程设计与制造流程中&#xff0c;不同软件间的数据交互与格式转换至关重要。将 SolidWorks 模型转换为 PDF3D 格式&#xff0c;能有效解决模型展示、数据共享以及跨平台协作等问题。本文将深入探讨 SolidWorks 转 PDF3D 的技术原理、操作流程及相关注意事项&#xff0c;…...

9.2 EvictionManager源码解读

本节重点总结 : evictionManager初始化了两个相同的manager对象 evictionManager做本机驱逐pod的判定和厨房evictionAdmitHandler用来kubelet创建Pod前进依据本机的资源压力进行准入检查 evictionManager判断内存驱逐阈值有两种方法 第一种使用内核的memcg的通知机制&#xff…...

考研数一非数竞赛复习之Stolz定理求解数列极限

在非数类大学生数学竞赛中&#xff0c;Stolz定理作为一种强大的工具&#xff0c;经常被用来解决和式数列极限的问题&#xff0c;也被誉为离散版的’洛必达’方法&#xff0c;它提供了一种简洁而有效的方法&#xff0c;使得原本复杂繁琐的极限计算过程变得直观明了。本文&#x…...

整理一下高级设施农业栽培学这门课程的所有知识点

整理一下高级设施农业栽培学这门课程的所有知识点 以下是高级设施农业栽培学这门课程从入门到精通需要学习的知识点&#xff1a; 一、设施农业概述 设施农业的概念与发展历程 了解设施农业的定义、特点及作用&#xff0c;掌握其发展历程、现状与未来趋势。熟悉国内外设施农业…...

2025最新软件测试面试八股文(含答案+文档)

1、请试着比较一下黑盒测试、白盒测试、单元测试、集成测试、系统测试、验收测试的区别与联系。 参考答案&#xff1a; 黑盒测试&#xff1a;已知产品的功能设计规格&#xff0c;可以进行测试证明每个实现了的功能是否符合要求。 白盒测试&#xff1a;已知产品的内部工作过程…...

系统架构设计师—系统架构设计篇—基于体系结构的软件开发方法

文章目录 概述基于体系结构的开发模型-ABSDM体系结构需求体系结构设计体系结构文档化体系结构复审体系结构实现体系结构演化 概述 基于体系结构&#xff08;架构&#xff09;的软件设计&#xff08;Architecture-Based Software Design&#xff0c;ABSD&#xff09;方法。 AB…...

求最大公约数【C/C++】

大家好啊&#xff0c;欢迎来到本博客( •̀ ω •́ )✧&#xff0c;我将带领大家详细的了解最大公约数的思想与解法。 一、什么是公约数 公约数&#xff0c;也称为公因数&#xff0c;是指两个或多个整数共有的因数。具体来说&#xff0c;如果一个整数能被两个或多个整数整除&…...

Transformer 代码剖析16 - BLEU分数(pytorch实现)

一、BLEU算法全景图 #mermaid-svg-uwjb5mQ2KAC6Rqbp {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-uwjb5mQ2KAC6Rqbp .error-icon{fill:#552222;}#mermaid-svg-uwjb5mQ2KAC6Rqbp .error-text{fill:#552222;stroke:…...

手机屏幕摔不显示了,如何用其他屏幕临时显示,用来导出资料或者清理手机

首先准备一个拓展坞 然后 插入一个外接的U盘 插入鼠标 插入有数字小键盘区的键盘 然后准备一根高清线&#xff0c;一端链接电脑显示器,一端插入拓展坞 把拓展坞的连接线&#xff0c;插入手机充电口&#xff08;可能会需要转接头&#xff09; 然后确保手机开机 按下键盘…...

labelimg标注的xml标签转换为yolo格式标签

本文不生产技术&#xff0c;只做技术的搬运工&#xff01;&#xff01;&#xff01; 前言 在yolo训练时&#xff0c;我们需要对图像进行标注&#xff0c;而使用labelimg标注时如果直接选择输出yolo格式的数据集&#xff0c;则原始数据的很多信息无法被保存&#xff0c;因此一版…...

Linux云计算SRE-第十七周

1. 做三个节点的redis集群。 1、编辑redis节点node0(10.0.0.100)、node1(10.0.0.110)、node2(10.0.0.120)的安装脚本 [rootnode0 ~]# vim install_redis.sh#!/bin/bash # 指定脚本解释器为bashREDIS_VERSIONredis-7.2.7 # 定义Redis的版本号PASSWORD123456 # 设置Redis的访问…...

K8S学习之基础十八:k8s的灰度发布和金丝雀部署

灰度发布 逐步扩大新版本的发布范围&#xff0c;从少量用户逐步扩展到全体用户。 特点是分阶段发布、持续监控、逐步扩展 适合需要逐步验证和降低风险的更新 金丝雀部署 将新版本先部署到一小部分用户或服务器&#xff0c;观察其表现&#xff0c;再决定是否全面推广。 特点&…...

WSL with NVIDIA Container Toolkit

一、wsl 下安装 docker 会提示安装 docekr 桌面版&#xff0c;所以直接安装 docker 桌面版本即可 二、安装 NVIDIA Container Toolkit NVIDIA Container Toolkit仓库 https://github.com/NVIDIA/nvidia-container-toolkit​github.com/NVIDIA/nvidia-container-toolkit 安装…...

PAT线上考试 真题/注意细节(甲/乙级)

闲谈 从此以后&#xff01;参加竞赛&#xff01; 都要为自己留够足够的时间练习&#xff01; 都要为自己留够足够的时间练习&#xff01; 都要为自己留够足够的时间练习&#xff01; 重要的事情说三遍&#xff0c;毕竟这只是我参加各种竞赛的开始&#xff01; \(&#xff…...

springcloud sentinel教程

‌QPS&#xff08;Queries Per Second&#xff09;即每秒查询率 TPS&#xff0c;每秒处理的事务数目 PV&#xff08;page view&#xff09;即页面浏览量 UV 访问数&#xff08;Unique Visitor&#xff09;指独立访客访问数 一、初识Sentinel 什么是雪崩问题? 微服务之间相…...

摄相机标定的基本原理

【相机标定的基本原理与经验分享】https://www.bilibili.com/video/BV1eE411c7kr?vd_source7c2b5de7032bf3907543a7675013ce3a 相机模型&#xff1a; 定义&#xff1a; 内参&#xff1a;就像相机的“眼睛”。它描述了相机内部的特性&#xff0c;比如焦距&#xff08;镜头的放…...

HJ C++11 Day2

Initializer Lists 对于一个类P class P{P(int a, int b){cout << "P(int, int), a" << a << ", b " << b << endl;}P(initializer_list<int> initlist){cout << "P(initializer_list<int>), val…...

在 ASP.NET Core 中启用 Brotli 和 Gzip 响应压缩

在本文中&#xff0c;我们将探讨如何在 ASP.NET Core 应用程序中启用响应压缩&#xff0c;重点介绍 Brotli 和 Gzip 算法以及如何验证压缩是否有效。 什么是响应压缩&#xff1f; 响应压缩通过使用Brotli 或 Gzip等算法来最小化 HTTP 响应的大小。这些算法在传输文本资产&#…...

leetcode69.x 的平方根

题目&#xff1a; 给你一个非负整数 x &#xff0c;计算并返回 x 的 算术平方根 。 由于返回类型是整数&#xff0c;结果只保留 整数部分 &#xff0c;小数部分将被 舍去 。 注意&#xff1a;不允许使用任何内置指数函数和算符&#xff0c;例如 pow(x, 0.5) 或者 x ** 0.5 。…...

第11章 web应用程序安全(网络安全防御实战--蓝军武器库)

网络安全防御实战--蓝军武器库是2020年出版的&#xff0c;已经过去3年时间了&#xff0c;最近利用闲暇时间&#xff0c;抓紧吸收&#xff0c;总的来说&#xff0c;第11章开始学习利用web应用程序安全&#xff0c;主要讲信息收集、dns以及burpsuite&#xff0c;现在的资产测绘也…...

flac、kgg、kgma格式音频转换MP3

1. 选择需要转换的音频文件 2. 下载闪电音频格式转换器 闪电音频格式转换器-全面覆盖常见音乐格式_音频合并分割_音频压缩 3. 买会员有点贵&#xff0c;也没必要&#xff0c;偶尔转换一次的&#xff0c;就去闲鱼买&#xff0c;两天会员9块钱。 4. 闲鱼卖家给兑换码&#xff0c…...

macos 程序 运行

sudo xattr -r -d com.apple.quarantine [/Applications/Name]使用stow 管理配置文件...

基于YOLO11深度学习的电瓶车进电梯检测与语音提示系统【python源码+Pyqt5界面+数据集+训练代码】

《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…...

HTML5 表单属性

HTML5 表单属性 引言 HTML5作为新一代的网页标准,带来了许多新的特性和改进。在表单处理方面,HTML5引入了一系列新的表单属性,这些属性使得表单的创建和使用更加灵活和强大。本文将详细介绍HTML5表单属性,包括其功能、使用方法和注意事项。 一、HTML5表单属性概述 HTML…...

从0开始,手搓Tomcat

一、什么是Tomcat Tomcat 是一款开源的、轻量级的 Web 服务器&#xff0c;它不仅能够提供 HTTP 服务&#xff0c;还能够运行 Java Servlet 和 JavaServer Pages&#xff08;JSP&#xff09;。对于许多开发者来说&#xff0c;理解 Tomcat 的目录结构以及如何在该结构中组织应用…...

数列分块入门2

题目描述 给出一个长为 n n n 的数列&#xff0c;以及 n n n 个操作&#xff0c;操作涉及区间加法&#xff0c;询问区间内小于某个值 x x x 的元素个数。 输入格式 第一行输入一个数字 n n n。 第二行输入 n n n 个数字&#xff0c;第 i i i 个数字为 a i a_i ai​&a…...

【ThreeJS Basics 06】Camera

文章目录 Camera 相机PerspectiveCamera 透视相机正交相机用鼠标控制相机大幅度转动&#xff08;可以看到后面&#xff09; 控制组件FlyControls 飞行组件控制FirstPersonControls 第一人称控制PointerLockControls 指针锁定控制OrbitControls 轨道控制TrackballControls 轨迹球…...

postman接口请求中的 Raw是什么

前言 在现代的网络开发中&#xff0c;API 的使用已经成为数据交换的核心方式之一。然而&#xff0c;在与 API 打交道时&#xff0c;关于如何发送请求体&#xff08;body&#xff09;内容类型的问题常常困扰着开发者们&#xff0c;尤其是“raw”和“json”这两个术语之间的区别…...

docker1

前言 技术架构 单机架构 应用数据分离架构 应用服务集群架构 读写分离/主从分离架构 写入主的时候&#xff0c;要同步Mysql从的数据才可以 冷热分离架构 写的时候要写入主和缓存数据库 读的时候先去缓存看有没有&#xff0c;没有的话就去从数据库读数据 主要就是看这个数据是…...

RocketMQ延迟消息深度解析:原理、实践与性能调优

RocketMQ延迟消息深度解析&#xff1a;原理、实践与性能调优 编程相关书籍分享&#xff1a;https://blog.csdn.net/weixin_47763579/article/details/145855793 DeepSeek使用技巧pdf资料分享&#xff1a;https://blog.csdn.net/weixin_47763579/article/details/145884039 一、…...

RabbitMQ 高级特性解析:RabbitMQ 消息可靠性保障 (上)

RabbitMQ 核心功能 RabbitMQ 高级特性解析&#xff1a;RabbitMQ 消息可靠性保障 &#xff08;上&#xff09;-CSDN博客 RabbitMQ 高级特性&#xff1a;从 TTL 到消息分发的全面解析 &#xff08;下&#xff09;-CSDN博客 前言 最近再看 RabbitMQ&#xff0c;看了看自己之前写…...

大白话JavaScript实现一个函数,将数组中的元素进行去重

大白话JavaScript实现一个函数&#xff0c;将数组中的元素进行去重 答题思路 要实现数组元素去重的函数&#xff0c;核心思路是遍历数组&#xff0c;然后判断每个元素是否已经在新数组里存在&#xff0c;如果不存在就添加进去&#xff0c;存在则跳过。下面会介绍几种不同的实…...

PQL查询和监控各类中间件

1 prometheus的PQL查询 1.1 Metrics数据介绍 prometheus监控中采集过来的数据统一称为Metrics数据&#xff0c;其并不是代表具体的数据格式&#xff0c;而是一种统计度量计算单位当需要为某个系统或者某个服务做监控时&#xff0c;就需要使用到 metrics prometheus支持的met…...

uni_app实现下拉刷新

1. 在页面配置中启用下拉刷新 首先&#xff0c;你需要在页面的 pages.json 文件中启用下拉刷新功能。 {"pages": [{"path": "pages/index/index","style": {"navigationBarTitleText": "首页","enablePull…...

C#类型转换基本概念

一、基本定义‌ C# 类型转换是将数据从一种类型转换为另一种类型的过程&#xff0c;分为 ‌隐式转换‌ 和 ‌显式转换‌ 两类‌。 强类型语言特性‌&#xff1a;C# 要求变量类型在编译时确定&#xff0c;类型转换需满足兼容性或显式规则‌。目的‌&#xff1a;处理不同数据类…...

【学习笔记】【DeepSeek AI 医生】2-2 AI家庭医生课程内容介绍

【DeepSeek AI 医生】2-4 项目详细分析及DeepSeek适用场景 一、Ollama部署二、可视化UI三、构建项目环境四、搭建项目架构五、Spring Al六、SSE服务端推送事件七、数据持久化八、线上部署 一、Ollama部署 Mac部署windows 部署ollama脚本、常用命令DeepSeek 提示词、角色、适用…...

DeepSeek使用教程--让DeepSeek生成精准题库

想让DeepSeek出好题&#xff0c;关键在于提示词的设计。总结了一个基本模板&#xff1a; 请帮我生成一套关于[学科/知识点]的题目&#xff0c;包括[题型]&#xff0c;难度为[简单/中等/困难]&#xff0c;适合[年级/学习阶段]的学生&#xff0c;总共[数量]道题。每道题请提供详细…...

数学之约数个数定理-阶乘约数

题目&#xff1a; 定义阶乘 n!123⋅⋅⋅n。 请问 100!&#xff08;100的阶乘&#xff09;有多少个正约数。 们需要计算 100! 的正约数的个数。阶乘 100! 的定义是&#xff1a; 100!123⋯100 直接计算 100!的值是不现实的&#xff0c;因为它是一个非常大的数。因此&#xf…...

C语言学习笔记-进阶(7)字符串函数3

1. strstr的使用和模拟实现 char * strstr ( const char * str1, const char * str2); Returns a pointer to the first occurrence of str2 in str1, or a null pointer if str2 is not part of str1. &#xff08;函数返回字符串str2在字符串str1中第⼀次出现的位置&#x…...

快乐数 力扣202

一、题目 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1&#xff0c;也可能是 无限循环 但始终变不到 1。如果这个过程 结果为 1&…...

Cpu100%问题(包括-线上docker服务以及Arthas方式进行处理)

&#x1f353; 简介&#xff1a;java系列技术分享(&#x1f449;持续更新中…&#x1f525;) &#x1f353; 初衷:一起学习、一起进步、坚持不懈 &#x1f353; 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正&#x1f64f; &#x1f353; 希望这篇文章对你有所帮助,欢…...

近三年图像超分辨率研究进展综述(轻量化方向)

一、图像超分辨率技术的近三年核心进展 1. 轻量化网络设计突破 轻量化模型是端侧部署的关键,近三年研究主要围绕参数压缩与计算效率提升展开: 11卷积与通道优化:SCNet提出全11卷积架构,通过逐点卷积替代传统33卷积,在保持重建质量的同时将模型参数减少60%以上。该设计通…...

成都亚恒云知教育咨询公司:绘画的风格如何学习与确定?

宝子们&#xff0c;好久不见&#xff01; 最近收到好多同学发给我们成都亚恒云知教育咨询有限公司创作的作品&#xff0c;同时也收到了有很多乖乖的询问&#xff1a;关于绘画&#xff0c;老师我没有属于自己的风格&#xff0c;怎么才能画出属于自己的风格的作品啊&#xff0c;…...

数据结构篇——串(String)

一、引入 在计算机中的处理的数据内容大致可分为以整形、浮点型等的数值处理和字符、字符串等的非数值处理。 今天我们主要学习的就是字符串数据。本章主要围绕“串的定义、串的类型、串的结构及其运算”来进行串介绍与学习。 二、串的定义 2.1、串的基本定义 串&#xff08;s…...

Qwen架构与Llama架构的核心区别

我们在讨论Deepseek不同版本之间的区别时了解到,DeepSeek-R1的蒸馏模型分为Qwen和Llama两个系列,包括Qwen系列的0.5B、1.5B、3B、7B、14B、32B、72B和Llama系列的8B、70B。Qwen系列以阿里通义千问(Qwen)为基础模型架构(具体是Qwen-2.5),Llama系列以Meta的Llama为基础模型…...

uniapp或者vue 使用serialport

参考https://blog.csdn.net/ykee126/article/details/90440499 版本是第一位&#xff1a;否则容易编译失败 node 版本 18.14.0 npm 版本 9.3.1 electron 版本 30.0.8 electron-rebuild 版本 3.2.9 serialport 版本 10.0.0 需要python环境 main.js // Modules to control app…...

Linux和gcc/g++常用命令总结

目录 Linux命令总结 文件操作相关命令 ls cd pwd cp mv rm cat mkdir rmdir touch 文本处理操作命令 grep awk sed 进程管理操作相关命令 ps top htop kill pkill killall chmod chown 网络操作相关命令 ping ifconfig netstat ss lsof curl …...