公司网站备案怎么做,怎么用网站做转换服务器,申请百度收录网址,青岛不错的网站公司目录 一、基于Lua看门狗实现
1.1 缓存实体
1.2 延迟队列存储实体
1.3 分布式锁RedisDistributedLockWithDog
1.4 看门狗线程续期
1.5 测试类
1.6 测试结果
1.7 总结
二、RedLock分布式锁
2.1 Redlock分布式锁简介
2.2 RedLock测试例子
2.3 RedLock 加锁核心源码分析…
目录 一、基于Lua看门狗实现
1.1 缓存实体
1.2 延迟队列存储实体
1.3 分布式锁RedisDistributedLockWithDog
1.4 看门狗线程续期
1.5 测试类
1.6 测试结果
1.7 总结
二、RedLock分布式锁
2.1 Redlock分布式锁简介
2.2 RedLock测试例子
2.3 RedLock 加锁核心源码分析
2.4 RedLock的问题与解决方案
2.4.1 问题
2.4.1.1 时钟偏移
2.4.1.2 单点故障
2.4.1.3 性能瓶颈
2.4.2 解决方案
2.4.2.1 引入重试机制
2.4.2.2 时钟校准
2.4.2.3 引入冗余节点
2.5 总结 一、基于Lua看门狗实现
1.1 缓存实体
package com.ningzhaosheng.distributelock.redis;/*** author ningzhaosheng* date 2024/4/18 15:37:16* description 类说明Redis的key-value结构*/
public class LockItem {private final String key;private final String value;public LockItem(String key, String value) {this.key key;this.value value;}public String getKey() {return key;}public String getValue() {return value;}
}
1.2 延迟队列存储实体
package com.ningzhaosheng.distributelock.redis;import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;/*** author ningzhaosheng* date 2024/4/18 15:35:43* description 说明存放到延迟队列的元素比标准的delay的实现要提前一点时间*/
public class ItemVoT implements Delayed {/*到期时刻 20:00:35,234*/private long activeTime;/*业务数据泛型*/private T data;/*传入的数值代表过期的时长单位毫秒需要乘1000转换为毫秒和到期时间* 同时提前100毫秒续期,具体的时间可以自己决定*/public ItemVo(long expirationTime, T data) {super();this.activeTime expirationTime System.currentTimeMillis() - 100;this.data data;}public long getActiveTime() {return activeTime;}public T getData() {return data;}/*** 返回元素到激活时刻的剩余时长*/public long getDelay(TimeUnit unit) {long d unit.convert(this.activeTime- System.currentTimeMillis(), unit);return d;}/*** 按剩余时长排序*/public int compareTo(Delayed o) {long d (getDelay(TimeUnit.MILLISECONDS)- o.getDelay(TimeUnit.MILLISECONDS));if (d 0) {return 0;} else {if (d 0) {return -1;} else {return 1;}}}
}
1.3 分布式锁RedisDistributedLockWithDog
package com.ningzhaosheng.distributelock.redis;import redis.clients.jedis.Jedis;
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;/*** author ningzhaosheng* date 2024/4/18 15:38:07* description Redis分布式锁附带看门狗线程的实现:加锁保持锁1秒*/
public class RedisDistributedLockWithDog implements Lock {private final static int LOCK_TIME 1 * 1000;private final static String RS_DISTLOCK_NS tdln2:;/*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;/*还有并发问题考虑ThreadLocal*/private ThreadLocalString lockerId new ThreadLocal();private Thread ownerThread;private String lockName lock;private Jedis jedis null;// 看门狗线程private Thread expireThread;private WatchDogThead watchDogThead;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;}public RedisDistributedLockWithDog(Jedis jedis) {this.jedis jedis;watchDogThead new WatchDogThead(jedis);closeExpireThread();}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 (ownerThread t) {return true;} else if (ownerThread ! null) {/*说明本进程中有别的线程正在持有分布式锁*/return false;}try {/*每一个锁的持有人都分配一个唯一的id也可采用snowflake算法*/String id UUID.randomUUID().toString();SetParams params new SetParams();params.px(LOCK_TIME); //加锁时间1sparams.nx();synchronized (this) {if ((ownerThread null) OK.equals(jedis.set(RS_DISTLOCK_NS lockName, id, params))) {lockerId.set(id);setOwnerThread(t);if (expireThread null) {//看门狗线程启动expireThread new Thread(watchDogThead, expireThread);expireThread.setDaemon(true);expireThread.start();}//往延迟阻塞队列中加入元素让看门口可以在过期之前一点点的时间去做锁的续期watchDogThead.addDelayDog(new ItemVo((int) LOCK_TIME, new LockItem(lockName, id)));System.out.println(Thread.currentThread().getName() 已获得锁----);return true;} else {System.out.println(Thread.currentThread().getName() 无法获得锁----);return false;}}} catch (Exception e) {throw new RuntimeException(分布式锁尝试加锁失败, e);}}Overridepublic boolean tryLock(long time, TimeUnit unit) throws InterruptedException {throw new UnsupportedOperationException(不支持等待尝试获取锁);}Overridepublic void unlock() {Thread t Thread.currentThread();if (ownerThread ! t) {throw new RuntimeException(试图释放无所有权的锁);}try {Long result (Long) jedis.eval(RELEASE_LOCK_LUA,Arrays.asList(RS_DISTLOCK_NS lockName),Arrays.asList(lockerId.get()));System.out.println(result);if (result.longValue() ! 0L) {System.out.println(t.getName() Redis上的锁已释放);} else {System.out.println(t.getName() Redis上的锁释放失败);}} catch (Exception e) {throw new RuntimeException(释放锁失败, e);} finally {lockerId.remove();setOwnerThread(null);// 关闭看门狗closeExpireThread();// 关闭jedis连接jedis.close();}}Overridepublic Condition newCondition() {throw new UnsupportedOperationException(不支持等待通知操作);}/*** 中断看门狗线程*/public void closeExpireThread() {if (null ! expireThread) {expireThread.interrupt();}}
}
1.4 看门狗线程续期
package com.ningzhaosheng.distributelock.redis;import redis.clients.jedis.Jedis;import java.util.Arrays;
import java.util.concurrent.DelayQueue;/*** author ningzhaosheng* date 2024/4/18 16:18:10* description 看门狗线程*/
public class WatchDogThead implements Runnable {private final static int LOCK_TIME 1 * 1000;private final static String LOCK_TIME_STR String.valueOf(LOCK_TIME);private final static String RS_DISTLOCK_NS tdln2:;/*看门狗线程*/private Thread expireThread;//通过delayDog 避免无谓的轮询减少看门狗线程的轮序次数 阻塞延迟队列 刷1 没有刷2private static DelayQueueItemVoLockItem delayDog new DelayQueue();//续锁逻辑判断是持有锁的线程才能续锁private final static String DELAY_LOCK_LUA if redis.call(get,KEYS[1])ARGV[1] then\n return redis.call(pexpire, KEYS[1],ARGV[2])\n else return 0 end;// 客户端Jedis jedis null;public WatchDogThead(Jedis jedis) {this.jedis jedis;}public void addDelayDog(ItemVoLockItem itemItemVo) {delayDog.add(itemItemVo);}Overridepublic void run() {System.out.println(看门狗线程已启动......);while (!Thread.currentThread().isInterrupted()) {try {LockItem lockItem delayDog.take().getData();//只有元素快到期了才能take到 0.9stry {Long result (Long) jedis.eval(DELAY_LOCK_LUA,Arrays.asList(RS_DISTLOCK_NS lockItem.getKey()),Arrays.asList(lockItem.getValue(), LOCK_TIME_STR));if (result.longValue() 0L) {System.out.println(Redis上的锁已释放无需续期);} else {delayDog.add(new ItemVo((int) LOCK_TIME,new LockItem(lockItem.getKey(), lockItem.getValue())));System.out.println(Redis上的锁已续期: LOCK_TIME);}} catch (Exception e) {throw new RuntimeException(锁续期失败, e);}} catch (InterruptedException e) {System.out.println(看门狗线程被中断);break;}}System.out.println(看门狗线程准备关闭......);}}
1.5 测试类
package com.ningzhaosheng.distributelock.redis;import com.ningzhaosheng.distributelock.zookeeper.OrderServiceHandle;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;/*** author ningzhaosheng* date 2024/4/19 8:07:38* description redis看门狗分布式锁测试类*/
public class TestRedisDistributedLockWithDog {private static final String REDIS_ADDRESS1 192.168.31.167:26379;private static final String REDIS_ADDRESS2 192.168.31.215:26379;private static final String REDIS_ADDRESS3 192.168.31.154:26379;public static void main(String[] args) {SetString sentinels new HashSetString();sentinels.add(REDIS_ADDRESS1);sentinels.add(REDIS_ADDRESS2);sentinels.add(REDIS_ADDRESS3);// Redis主服务器的名字String masterName mymaster;// Redis服务器的密码String password xiaoning;try {int NUM 10;CountDownLatch cdl new CountDownLatch(NUM);for (int i 1; i NUM; i) {// 创建JedisSentinelPoolJedisSentinelPool pool new JedisSentinelPool(masterName, sentinels, password);// 从池中获取Jedis实例Jedis jedis pool.getResource();// 按照线程数迭代实例化线程Lock lock new RedisDistributedLockWithDog(jedis);new Thread(new OrderServiceHandle(cdl, lock)).start();// 创建一个线程倒计数器减1cdl.countDown();pool.close();}} catch (Exception e) {e.printStackTrace();}}
}
1.6 测试结果 1.7 总结
以上基于看门狗机制实现的Redis锁在对高并发要求不高的场景下可以使用但是其实它还是有问题的在主从架构模式下可能会导致两个线程同时获取到锁得问题出现 线程A从主redis中请求一个分布式锁获取锁成功从redis准备从主redis同步锁相关信息时主redis突然发生宕机锁丢失了触发从redis升级为新的主redis线程B从继任主redis的从redis上申请一个分布式锁此时也能获取锁成功导致同一个分布式锁被两个客户端同时获取没有保证独占使用特性
当对高可用有要求的场景这种方式就不合适了那么在高并发、高可靠场景下我们该如何基于Redis 实现分布式锁呢得接着往下看
二、RedLock分布式锁
2.1 Redlock分布式锁简介
Redlock是由Redis的作者Salvatore Sanfilippo提出的一种分布式锁算法它利用Redis的特性来实现分布式锁。Redlock算法的核心思想是通过在多个Redis实例上创建相同的分布式锁以保证锁的可靠性。Redlock算法通过在Redis中设置一个唯一的键值对来表示锁的存在其他线程或进程需要通过竞争来获取这个锁。
官网wiki8. 分布式锁和同步器 · redisson/redisson Wiki · GitHub 2.2 RedLock测试例子
package com.ningzhaosheng.distributelock.redis.redisson;import org.redisson.Redisson;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;/*** author ningzhaosheng* date 2024/6/22 15:57:17* description*/
public class TestRedLockByRedisson {public static void main(String[] args) {String RS_DISTLOCK_NS tdln2:;String lockName lock;Config config new Config();config.useSentinelServers().addSentinelAddress(redis://192.168.31.215:26379,redis://192.168.31.167:26379,redis://192.168.31.154:26379).setMasterName(mymaster).setPassword(xiaoning);RedissonClient redisson Redisson.create(config);try {for (int i 1; i 10; i) {// 生成8位随机数Random random new Random();// 创建RedLockRLock rLock redisson.getLock(RS_DISTLOCK_NSlockName random.nextInt(99999999));RedissonRedLock rdLock new RedissonRedLock(rLock);boolean islock rdLock.tryLock(500,3000,TimeUnit.MILLISECONDS);if(islock){System.out.println(执行业务啦);}else{System.out.println(获取锁失败);}rdLock.unlock();}} catch (Exception e) {e.printStackTrace();}redisson.shutdown();}
}
测试结果 2.3 RedLock 加锁核心源码分析
这里我就以tryLock()为例分析下RedLock 的源码 public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {long newLeaseTime -1L;if (leaseTime ! -1L) {if (waitTime -1L) {newLeaseTime unit.toMillis(leaseTime);} else {newLeaseTime unit.toMillis(waitTime) * 2L;}}long time System.currentTimeMillis();long remainTime -1L;if (waitTime ! -1L) {remainTime unit.toMillis(waitTime);}long lockWaitTime this.calcLockWaitTime(remainTime);// 允许加锁失败的节点数量N-(N/21)int failedLocksLimit this.failedLocksLimit();ListRLock acquiredLocks new ArrayList(this.locks.size());ListIterator iterator this.locks.listIterator();// 遍历所有节点通过EVAL命令执行lua脚本加锁while(iterator.hasNext()) {// 迭代获取Redis实例RLock lock (RLock)iterator.next();boolean lockAcquired;// 尝试加锁调用lock.tryLock()方法try {if (waitTime -1L leaseTime -1L) {lockAcquired lock.tryLock();} else {long awaitTime Math.min(lockWaitTime, remainTime);// 尝试加锁lockAcquired lock.tryLock(awaitTime, newLeaseTime, TimeUnit.MILLISECONDS);}} catch (RedisResponseTimeoutException var21) {// 如果抛出异常为了防止加锁成功但是响应失败需要解锁所有节点同时返回加锁失败状态this.unlockInner(Arrays.asList(lock));lockAcquired false;} catch (Exception var22) {lockAcquired false;}if (lockAcquired) {// 如果加锁成功把锁加到以获得锁集合中acquiredLocks.add(lock);} else {//计算已经申请锁失败的节点是否已经到达允许加锁失败节点个数限制 (即N-(N/21))如果已经到达就认定最终申请锁失败则没有必要继续从后面的节点申请了因为 Redlock 算法要求至少N/21 个节点都加锁成功才算最终的锁申请成功if (this.locks.size() - acquiredLocks.size() this.failedLocksLimit()) {break;}if (failedLocksLimit 0) {this.unlockInner(acquiredLocks);if (waitTime -1L) {return false;}failedLocksLimit this.failedLocksLimit();acquiredLocks.clear();while(iterator.hasPrevious()) {iterator.previous();}} else {--failedLocksLimit;}}// 计算 目前从各个节点获取锁已经消耗的总时间如果已经等于最大等待时间则认定最终申请锁失败返回falseif (remainTime ! -1L) {remainTime - System.currentTimeMillis() - time;time System.currentTimeMillis();if (remainTime 0L) {this.unlockInner(acquiredLocks);return false;}}}// 重置锁过期时间if (leaseTime ! -1L) {acquiredLocks.stream().map((l) - {return (RedissonLock)l;}).map((l) - {return l.expireAsync(unit.toMillis(leaseTime), TimeUnit.MILLISECONDS);}).forEach((f) - {f.syncUninterruptibly();});}// 如果逻辑正常执行完则认为最终申请锁成功返回truereturn true;}2.4 RedLock的问题与解决方案
2.4.1 问题
2.4.1.1 时钟偏移
Redlock算法中需要使用到各个Redis实例的系统时钟来实现锁的过期时间控制。然而不同Redis实例之间的时钟可能存在偏移导致锁的过期时间计算错误从而导致锁的误释放或者死锁的发生。
2.4.1.2 单点故障
Redlock算法的可靠性依赖于多个Redis实例之间的协作。如果其中一个Redis实例发生故障可能会导致整个系统的分布式锁服务不可用。
2.4.1.3 性能瓶颈
RedLock需要访问多个实例网络延迟可能导致某些线程获取锁的时间较长会增加网络带宽的压力。此外每个实例都需要对锁进行检查和定时删除操作也会影响Redis的性能。
2.4.2 解决方案
针对上述问题我们可以采取以下解决方案来提高Redlock分布式锁在高并发环境下的性能和可靠性
2.4.2.1 引入重试机制
为了应对网络延迟带来的竞争问题我们可以在获取锁失败后进行重试。通过设定适当的重试次数和重试间隔可以减少因网络延迟导致的锁竞争失败的情况。但是该方案还是解决不了性能瓶颈问题性能瓶颈问题是架构方案决定的。
2.4.2.2 时钟校准
为了解决时钟偏移问题我们可以通过定期校准各个Redis实例的系统时钟来保证它们之间的一致性。可以使用NTP协议或者其他时间同步机制来实现时钟校准。
2.4.2.3 引入冗余节点
为了避免单点故障带来的问题我们可以引入冗余节点来提高系统的可用性。通过在系统中增加多个Redis实例作为备份节点可以在主节点故障时快速切换到备份节点保证分布式锁服务的可用性。
2.5 总结
基于Redis实现分布式锁的方案他的实现方式有两种一直是基于setnx指令lua脚本看门狗进程实现在Redis单实例下的分布式锁方案这种方式在主从模式下当主节点宕机主从切换的时候会有获取锁失败的情况或者会有多个客户端同时获取到锁的情况为了提高分布式锁的可用性则出现了改进后的RedLock锁RedLock锁是基于多实例加锁即N/21同时给N/21个实例加锁成功才算获取锁成功。但是他的问题也很明显具体如前文所述。所以在进行技术方案选型的时候你要结合你的场景明确选用分布式锁时需要用途
如果你只考虑提升加锁效率问题那么选用一台高性能的服务器部署单实例采用基于setnx指令lua脚本看门狗进程的方案就够了。如果你考虑高可用想避免单点问题并且你的部署架构是主从的或者集群模式的那么你可以选择使用RedLock但是你就不得不考虑因此带来的复杂性和各种问题。如果你要保证绝对的数据一致性那么不好意思Redis实现分布式锁方案可能不适合你因为从架构的角度来说Redis的架构属于AP架构它保证的是可用性这时你可以考虑使用Zookeeper实现分布式锁方案因为它的架构属于CP架构保证的是一致性但是Zookeeper也有问题就是它的性能没有Redis高。
那么在并发量大可靠性高的场景下我们最终应该怎么技术选型呢我的建议是避免使用分布式锁可以通过串行化来解决比如同步串行化异步串行化等方案。这里暂时不对串行化设计展开论述后续有时间我会在另外的主题文章进行分享。 好了本次内容就分享到这欢迎关注本博主。如果有帮助到大家欢迎大家点赞关注收藏有疑问也欢迎大家评论留言