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

在线视频网站a一级爰a做免费wordpress 关闭插件更新

在线视频网站a一级爰a做免费,wordpress 关闭插件更新,设计logo网站 生成器,图片短链接生成器分布式锁简介 1. 什么是分布式锁 分布式锁是一种在分布式系统环境下#xff0c;通过多个节点对共享资源进行访问控制的一种同步机制。它的主要目的是防止多个节点同时操作同一份数据#xff0c;从而避免数据的不一致性。 线程锁#xff1a; 也被称为互斥锁#xff08;Mu…分布式锁简介 1. 什么是分布式锁 分布式锁是一种在分布式系统环境下通过多个节点对共享资源进行访问控制的一种同步机制。它的主要目的是防止多个节点同时操作同一份数据从而避免数据的不一致性。 线程锁 也被称为互斥锁Mutex主要用于控制同一进程中的多个线程对共享资源的访问。进程锁 进程锁是用于控制同一台机器上的多个进程对共享资源的访问。进程锁可以是系统级的如文件锁也可以是用户级的如信号量Semaphore。分布式锁 分布式锁是用于控制分布式系统中的多个节点对共享资源的访问。由于分布式系统中的节点可能位于不同的机器甚至不同的地理位置因此分布式锁的实现比线程锁和进程锁要复杂得多。分布式锁需要在网络中的多个节点之间进行协调以保证锁的唯一性和一致性。 2. 分布式锁的特性 分布式锁主要有以下几个特性 互斥性在任何时刻只有一个节点可以持有锁。不会发生死锁如果一个节点崩溃锁可以被其他节点获取。公平性如果多个节点同时申请锁系统应该保证每个节点都有获取锁的机会。可重入性同一个节点可以多次获取同一个锁而不会被阻塞。高可用锁服务应该是高可用的不能因为锁服务的故障而影响整个系统的运行。 分布式锁的基本原理 1. 分布式锁的基本步骤 分布式锁的基本原理可以分为以下几个步骤 请求锁当一个实例需要访问共享资源时它会向分布式锁系统发送一个请求试图获取一个锁。锁定资源分布式锁系统会检查是否有其他实例已经持有这个锁。如果没有那么这个实例就会获得锁并且有权访问共享资源。如果有那么这个实例就必须等待直到锁被释放。访问资源一旦实例获取了锁它就可以安全地访问共享资源而不用担心其他实例会同时访问这个资源。释放锁当实例完成对共享资源的访问后它需要通知分布式锁系统释放锁。这样其他正在等待的实例就可以获取锁访问共享资源。 SETNX 命令SETNXSet if Not Exists命令用于在 key 不存在时设置值。这是实现分布式锁的关键命令因为它能确保在同一时间只有一个客户端能够获得锁。EXPIRE 命令EXPIRE 命令用于为 key 设置过期时间。这对于避免死锁非常重要因为即使某个客户端崩溃锁也会在一定时间后自动释放。DEL 命令DEL 命令用于删除 key。在释放锁时需要使用此命令删除对应的 key。Lua 脚本Redis 支持使用 Lua 脚本来执行一系列原子操作。这对于实现安全的分布式锁非常有用因为它可以确保在释放锁时检查锁的持有者。RedLock 算法Redis 官方推荐了一种名为 RedLock 的分布式锁算法。RedLock 是一种基于多个 Redis 实例的分布式锁算法旨在提供更高的安全性和容错能力。 Redis中分布式锁的实现 Redis分布式锁最简单的实现 SET my_key my_value NX EX 10 # 设置键值对, 超时时间为10s。 如果my_key存在则不进行任何操作 想要实现分布式锁必须要求 Redis 有「互斥」的能力我们可以使用 SETNX 命令这个命令表示SET if Not Exists即如果 key 不存在才会设置它的值否则什么也不做。 两个客户端进程可以执行这个命令达到互斥就可以实现一个分布式锁。 客户端 1 申请加锁加锁成功 客户端 2 申请加锁因为它后到达加锁失败 此时加锁成功的客户端就可以去操作「共享资源」例如修改 MySQL 的某一行数据或者调用一个 API 请求。 操作完成后还要及时释放锁给后来者让出操作共享资源的机会。如何释放锁呢 也很简单直接使用 DEL 命令删除这个 key 即可这个逻辑非常简单。 但是它存在一个很大的问题当客户端 1 拿到锁后如果发生下面的场景就会造成「死锁」 1、程序处理业务逻辑异常没及时释放锁 2、进程挂了没机会释放锁 这时这个客户端就会一直占用这个锁而其它客户端就「永远」拿不到这把锁了。怎么解决这个问题呢 如何避免死锁 我们很容易想到的方案是在申请锁时给这把锁设置一个「租期」。 在 Redis 中实现时就是给这个 key 设置一个「过期时间」。这里我们假设操作共享资源的时间不会超过 10s那么在加锁时给这个 key 设置 10s 过期即可 这样一来无论客户端是否异常这个锁都可以在 10s 后被「自动释放」其它客户端依旧可以拿到锁。 但现在还是有问题 现在的操作加锁、设置过期是 2 条命令有没有可能只执行了第一条第二条却「来不及」执行的情况发生呢例如 SETNX 执行成功执行EXPIRE 时由于网络问题执行失败 SETNX 执行成功Redis 异常宕机EXPIRE 没有机会执行 SETNX 执行成功客户端异常崩溃EXPIRE也没有机会执行 总之这两条命令不能保证是原子操作一起成功就有潜在的风险导致过期时间设置失败依旧发生「死锁」问题。 在 Redis 2.6.12 之后Redis 扩展了 SET 命令的参数用这一条命令就可以了 SET lock 1 EX 10 NX 锁被别人释放怎么办 上面的命令执行时每个客户端在释放锁时都是「无脑」操作并没有检查这把锁是否还「归自己持有」所以就会发生释放别人锁的风险这样的解锁流程很不「严谨」如何解决这个问题呢 解决办法是客户端在加锁时设置一个只有自己知道的「唯一标识」进去。 例如可以是自己的线程 ID也可以是一个 UUID随机且唯一这里我们以UUID 举例 SET lock $uuid EX 20 NX 之后在释放锁时要先判断这把锁是否还归自己持有伪代码可以这么写 if redis.get(lock) $uuid:     redis.del(lock) 这里释放锁使用的是 GET DEL 两条命令这时又会遇到我们前面讲的原子性问题了。这里可以使用lua脚本来解决。 安全释放锁的 Lua 脚本如下 if redis.call(GET,KEYS[1]) ARGV[1] then     return redis.call(DEL,KEYS[1]) else     return 0 end 好了这样一路优化整个的加锁、解锁的流程就更「严谨」了。 这里我们先小结一下基于 Redis 实现的分布式锁一个严谨的的流程如下 1、加锁 SET lock_key $unique_id EX $expire_time NX 2、操作共享资源 3、释放锁Lua 脚本先 GET 判断锁是否归属自己再DEL 释放锁 Java代码实现分布式锁 import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.params.SetParams;import java.util.Arrays; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock;/*** 分布式锁的实现*/ Component public class RedisDistLock implements Lock {private final static int LOCK_TIME 5*1000;private final static String RS_DISTLOCK_NS tdln:;/*if redis.call(get,KEYS[1])ARGV[1] thenreturn redis.call(del, KEYS[1])else return 0 end*/private final static String RELEASE_LOCK_LUA if redis.call(get,KEYS[1])ARGV[1] then\n return redis.call(del, KEYS[1])\n else return 0 end;/*保存每个线程的独有的ID值*/private ThreadLocalString lockerId new ThreadLocal();/*解决锁的重入*/private Thread ownerThread;private String lockName lock;Autowiredprivate JedisPool jedisPool;public String getLockName() {return lockName;}public void setLockName(String lockName) {this.lockName lockName;}public Thread getOwnerThread() {return ownerThread;}public void setOwnerThread(Thread ownerThread) {this.ownerThread ownerThread;}Overridepublic void lock() {while(!tryLock()){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}Overridepublic void lockInterruptibly() throws InterruptedException {throw new UnsupportedOperationException(不支持可中断获取锁);}Overridepublic boolean tryLock() {Thread t Thread.currentThread();if(ownerThreadt){/*说明本线程持有锁*/return true;}else if(ownerThread!null){/*本进程里有其他线程持有分布式锁*/return false;}Jedis jedis null;try {String id UUID.randomUUID().toString();SetParams params new SetParams();params.px(LOCK_TIME);params.nx();synchronized (this){/*线程们本地抢锁*/if((ownerThreadnull)OK.equals(jedis.set(RS_DISTLOCK_NSlockName,id,params))){lockerId.set(id);setOwnerThread(t);return true;}else{return false;}}} catch (Exception e) {throw new RuntimeException(分布式锁尝试加锁失败);} finally {jedis.close();}}Overridepublic boolean tryLock(long time, TimeUnit unit) throws InterruptedException {throw new UnsupportedOperationException(不支持等待尝试获取锁);}Overridepublic void unlock() {if(ownerThread!Thread.currentThread()) {throw new RuntimeException(试图释放无所有权的锁);}Jedis jedis null;try {jedis jedisPool.getResource();Long result (Long)jedis.eval(RELEASE_LOCK_LUA,Arrays.asList(RS_DISTLOCK_NSlockName),Arrays.asList(lockerId.get()));if(result.longValue()!0L){System.out.println(Redis上的锁已释放);}else{System.out.println(Redis上的锁释放失败);}} catch (Exception e) {throw new RuntimeException(释放锁失败,e);} finally {if(jedis!null) jedis.close();lockerId.remove();setOwnerThread(null);System.out.println(本地锁所有权已释放);}}Overridepublic Condition newCondition() {throw new UnsupportedOperationException(不支持等待通知操作);}}锁过期时间不好评估怎么办 看上面这张图假入key的失效时间是10s但是客户端C在拿到分布式锁之后然后业务逻辑执行超过10s那么问题来了在客户端C释放锁之前其实这把锁已经失效了那么客户端A和客户端B都可以去拿锁这样就已经失去了分布式锁的功能了 比较简单的妥协方案是尽量「冗余」过期时间降低锁提前过期的概率但是这个并不能完美解决问题那怎么办呢 分布式锁加入看门狗 加锁时先设置一个过期时间然后我们开启一个「守护线程」定时去检测这个锁的失效时间如果锁快要过期了操作共享资源还未完成那么就自动对锁进行「续期」重新设置过期时间。 这个守护线程我们一般也把它叫做「看门狗」线程。 为什么要使用守护线程 Redisson中的分布式锁 Redisson把这些工作都封装好了 dependencygroupIdorg.redisson/groupIdartifactIdredisson/artifactIdversion3.12.3/version/dependencyimport 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 MyRedissonConfig {/*** 所有对Redisson的使用都是通过RedissonClient*/Bean(destroyMethodshutdown)public RedissonClient redisson(){//1、创建配置Config config new Config();config.useSingleServer().setAddress(redis://127.0.0.1:6379);//2、根据Config创建出RedissonClient实例RedissonClient redisson Redisson.create(config);return redisson;} }import com.msb.redis.lock.rdl.RedisDistLockWithDog; import org.junit.jupiter.api.Test; import org.redisson.Redisson; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit;SpringBootTest public class TestRedissionLock {private int count 0;Autowiredprivate RedissonClient redisson;Testpublic void testLockWithDog() throws InterruptedException {int clientCount 3;RLock lock redisson.getLock(RD-lock);CountDownLatch countDownLatch new CountDownLatch(clientCount);ExecutorService executorService Executors.newFixedThreadPool(clientCount);for (int i 0;iclientCount;i){executorService.execute(() - {try {lock.lock(10, TimeUnit.SECONDS);System.out.println(Thread.currentThread().getName()准备进行累加。);Thread.sleep(2000);count;} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}countDownLatch.countDown();});}countDownLatch.await();System.out.println(count);} } GitHub - redisson/redisson: Redisson - Easy Redis Java client and Real-Time Data Platform. Valkey compatible. Sync/Async/RxJava/Reactive API. Over 50 Redis based Java objects and services: Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Spring Cache, Tomcat, Scheduler, JCache API, Hibernate, RPC, local cache ... Redisson: Easy Redis Java client and Real-Time Data Platform 锁过期时间不好评估怎么办 集群下的锁还安全么 基于 Redis 的实现分布式锁前面遇到的问题以及对应的解决方案 1、死锁设置过期时间 2、过期时间评估不好锁提前过期守护线程自动续期 3、锁被别人释放锁写入唯一标识释放锁先检查标识再释放 之前分析的场景都是锁在「单个」Redis实例中可能产生的问题并没有涉及到 Redis 的部署架构细节。 而我们在使用 Redis 时一般会采用主从集群 哨兵的模式部署这样做的好处在于当主库异常宕机时哨兵可以实现「故障自动切换」把从库提升为主库继续提供服务以此保证可用性。 但是因为主从复制是异步的那么就不可避免会发生的锁数据丢失问题加了锁却没来得及同步过来。从库被哨兵提升为新主库这个锁在新的主库上丢失了 Redlock真的安全吗 Redis 作者提出的 Redlock方案是如何解决主从切换后锁失效问题的。 Redlock 的方案基于一个前提 不再需要部署从库和哨兵实例只部署主库但主库要部署多个官方推荐至少 5 个实例。 注意不是部署 Redis Cluster就是部署 5 个简单的 Redis 实例。它们之间没有任何关系都是一个个孤立的实例。 做完之后我们看官网代码怎么去用的 8. 分布式锁和同步器 · redisson/redisson Wiki · GitHub 8.4. 红锁RedLock 基于Redis的Redisson红锁 RedissonRedLock对象实现了Redlock介绍的加锁算法。该对象也可以用来将多个 RLock对象关联为一个红锁每个 RLock对象实例可以来自于不同的Redisson实例。 RLock lock1 redissonInstance1.getLock(lock1); RLock lock2 redissonInstance2.getLock(lock2); RLock lock3 redissonInstance3.getLock(lock3);RedissonRedLock lock new RedissonRedLock(lock1, lock2, lock3); // 同时加锁lock1 lock2 lock3 // 红锁在大部分节点上加锁成功就算成功。 lock.lock(); ... lock.unlock(); 大家都知道如果负责储存某些分布式锁的某些Redis节点宕机以后而且这些锁正好处于锁住的状态时这些锁会出现锁死的状态。为了避免这种情况的发生Redisson内部提供了一个监控锁的看门狗它的作用是在Redisson实例被关闭前不断的延长锁的有效期。默认情况下看门狗的检查锁的超时时间是30秒钟也可以通过修改Config.lockWatchdogTimeout来另行指定。 另外Redisson还通过加锁的方法提供了 leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了。 RedissonRedLock lock new RedissonRedLock(lock1, lock2, lock3); // 给lock1lock2lock3加锁如果没有手动解开的话10秒钟后将会自动解开 lock.lock(10, TimeUnit.SECONDS);// 为加锁等待100秒时间并在加锁成功10秒钟后自动解开 boolean res lock.tryLock(100, 10, TimeUnit.SECONDS); ... lock.unlock(); Redlock实现整体流程 1、客户端先获取「当前时间戳T1」 2、客户端依次向这 5 个 Redis 实例发起加锁请求 3、如果客户端从 3 个大多数以上Redis 实例加锁成功则再次获取「当前时间戳T2」如果 T2 - T1 锁的过期时间此时认为客户端加锁成功否则认为加锁失败。 4、加锁成功去操作共享资源 5、加锁失败/释放锁向「全部节点」发起释放锁请求。 所以总的来说客户端在多个 Redis 实例上申请加锁必须保证大多数节点加锁成功大多数节点加锁的总耗时要小于锁设置的过期时间释放锁要向全部节点发起释放锁请求。 我们来看 Redlock 为什么要这么做 为什么要在多个实例上加锁 本质上是为了「容错」部分实例异常宕机剩余的实例加锁成功整个锁服务依旧可用。 为什么大多数加锁成功才算成功 多个 Redis 实例一起来用其实就组成了一个「分布式系统」。在分布式系统中总会出现「异常节点」所以在谈论分布式系统问题时需要考虑异常节点达到多少个也依旧不会影响整个系统的「正确性」。 这是一个分布式系统「容错」问题这个问题的结论是如果只存在「故障」节点只要大多数节点正常那么整个系统依旧是可以提供正确服务的。 为什么步骤 3 加锁成功后还要计算加锁的累计耗时 因为操作的是多个节点所以耗时肯定会比操作单个实例耗时更久而且因为是网络请求网络情况是复杂的有可能存在延迟、丢包、超时等情况发生网络请求越多异常发生的概率就越大。 所以即使大多数节点加锁成功但如果加锁的累计耗时已经「超过」了锁的过期时间那此时有些实例上的锁可能已经失效了这个锁就没有意义了。 为什么释放锁要操作所有节点 在某一个 Redis 节点加锁时可能因为「网络原因」导致加锁失败。 例如客户端在一个 Redis 实例上加锁成功但在读取响应结果时网络问题导致读取失败那这把锁其实已经在 Redis 上加锁成功了。 所以释放锁时不管之前有没有加锁成功需要释放「所有节点」的锁以保证清理节点上「残留」的锁。 好了明白了 Redlock 的流程和相关问题看似Redlock 确实解决了 Redis 节点异常宕机锁失效的问题保证了锁的「安全性」。 但事实真的如此吗 RedLock的是是非非 一个分布式系统更像一个复杂的「野兽」存在着你想不到的各种异常情况。 这些异常场景主要包括三大块这也是分布式系统会遇到的三座大山NPC。 NNetwork Delay网络延迟 PProcess Pause进程暂停GC CClock Drift时钟漂移 比如一个进程暂停GC的例子 1客户端 1 请求锁定节点 A、B、C、D、E 2客户端 1 的拿到锁后进入 GC时间比较久 3所有 Redis 节点上的锁都过期了 4客户端 2 获取到了 A、B、C、D、E 上的锁 5客户端 1 GC 结束认为成功获取锁 6客户端 2 也认为获取到了锁发生「冲突」 GC 和网络延迟问题这两点可以在红锁实现流程的第3步来解决这个问题。 但是最核心的还是时钟漂移因为时钟漂移就有可能导致第3步的判断本身就是一个BUG所以当多个 Redis 节点「时钟」发生问题时也会导致 Redlock 锁失效。 RedLock总结 Redlock 只有建立在「时钟正确」的前提下才能正常工作如果你可以保证这个前提那么可以拿来使用。 但是时钟偏移在现实中是存在的 第一从硬件角度来说时钟发生偏移是时有发生无法避免。例如CPU 温度、机器负载、芯片材料都是有可能导致时钟发生偏移的。 第二人为错误也是很难完全避免的。 所以Redlock尽量不用它而且它的性能不如单机版 Redis部署成本也高优先考虑使用主从 哨兵的模式 实现分布式锁只会有很小的记录发生主从切换时的锁丢失问题。 分布式锁的常见问题和解决方案 1. 死锁问题 问题 当一个客户端获取了锁但由于某些原因如程序崩溃、异常等无法释放锁时会导致其他客户端永远无法获取锁。解决方案 设置锁的过期时间。当锁的持有者未能在过期时间内执行完毕并释放锁时锁将自动过期从而允许其他客户端获取锁。 2. 锁续命问题 问题 如果一个操作需要的时间可能超过锁的过期时间那么在操作执行过程中锁过期会导致其他客户端获取到锁从而产生并发问题。 解决方案 使用锁续命机制。在锁持有者执行操作期间可以定期检查锁是否即将过期并在适当的时候对锁进行续命即重新设置锁的过期时间。 3. 锁释放问题 问题 为确保数据的一致性只有锁的持有者才能释放锁。但在实际应用中可能会出现误解锁的情况。解决方案 在设置锁时为锁关联一个唯一的值如UUID。在释放锁时先检查锁的值是否与当前客户端的值匹配如果匹配则释放锁否则不做任何操作。注意锁持有人的判断和锁的释放应该在一个原子操作内完成。 4. 锁的公平性问题 问题 在高并发环境中如果多个节点同时请求获取锁可能会出现“饥饿”现象即某些节点长时间无法获取到锁。解决方案 引入队列将请求锁的节点按照顺序排队。例如在Zookeeper中可以使用顺序节点来实现公平锁。 5. 锁的可重入性问题 问题 在某些场景中一个节点可能需要多次获取同一个锁如果锁不支持重入可能会导致死锁。解决方案 为锁添加一个拥有者的概念只有锁的拥有者才能再次获取到锁。例如在Redis中可以将锁的值设置为节点的唯一标识获取锁时检查锁的值是否为自己的标识。 6. 锁的安全性问题 问题 如果分布式锁的存储系统如Redis、Zookeeper等出现故障可能会导致锁无法正常工作。解决方案 使用高可用的存储系统如使用Redis集群或Zookeeper集群。另外可以使用心跳机制来检测存储系统的状态如果检测到故障可以及时进行切换。
http://www.ho-use.cn/article/10812049.html

相关文章:

  • 揭阳手机网站建设龙华龙岗网站建设公司
  • 建工教育网校官方网站手机网站 优化
  • 赣州网站制作较好的公司郑州市住房和城乡建设厅网站
  • 店铺空间设计案例嘉兴关键词优化报价
  • 网站建设比较好的多少钱自助网站
  • phpcms资讯类网站模板建设一个网站的流程图
  • 网站设计如何收费标准qq互联网站备案号
  • 优秀网站设计欣赏国内做网站需要先申请域名
  • 贵州建设厅网站怎样查询电工证嘉兴做网站的公司
  • 做本地网站卖如何找做网站的客户
  • 网站开发进程报告品牌网站建设四川
  • 找合伙人的网站做淘宝怎么找到php网站的首页面html
  • 网站文章模板广告设计公司vi设计
  • 哪个公司做外贸网站好网页做的很美的网站
  • 合肥市城乡建设局网站首页魔客吧wordpress主题安装
  • 网站数据流程西安中风险地区
  • 适合新手做的网站集团网站建设管理制度
  • 单页面的网站模板免费下载wordpress路径爆出
  • 公司网站维护与更新网站建设人员配置
  • 怎样设置网站访问权限网站建网站建设网站
  • 网站建设怎样中英文外贸网站营销推广
  • 黑龙江省建设集团网站可视化开发
  • 电子商务网站建设的特点html模板素材
  • 正规的源码交易平台seo排名赚能赚钱吗
  • 做网站需要的带宽上行还是下行国内最大的app开发公司
  • 原网站开发新功能所有的网站建设教程
  • 做市场的逛的网站深圳分销网站设计费用
  • 做海外网站推广南京cms建站
  • 站外推广免费网站西安手机商城网站建设
  • 网站制作完成后应进入什么阶段网站做百度竞价利于百度优化