商丘市网站建设设计制作费用计入什么会计科目
- 作者: 五速梦信息网
- 时间: 2026年04月20日 09:25
当前位置: 首页 > news >正文
商丘市网站建设,设计制作费用计入什么会计科目,织梦网站管理后台系统上面的织梦链接怎么样去掉,适合一个人开的实体店前言
在分布式系统中#xff0c;当不同进程或线程一起访问共享资源时#xff0c;会造成资源争抢#xff0c;如果不加以控制的话#xff0c;就会引发程序错乱。而分布式锁它采用了一种互斥机制来防止线程或进程间相互干扰#xff0c;从而保证了数据的一致性。
常见的分…前言
在分布式系统中当不同进程或线程一起访问共享资源时会造成资源争抢如果不加以控制的话就会引发程序错乱。而分布式锁它采用了一种互斥机制来防止线程或进程间相互干扰从而保证了数据的一致性。
常见的分布式锁实现方案
基于 Redis 实现分布式锁基于 Zookeeper 实现分布式锁
介绍
Redission是一个基于Redis实现的Java分布式对象存储和缓存框架。它提供了丰富的分布式数据结构和服务。例如分布式锁、分布式队列、分布式Rate Limiter等。
分布式锁的特点
互斥性是分布式锁的重要特点在任意时刻只有一个线程能够持有锁锁的超时时间。一个线程在持锁期间挂掉了而没主动释放锁此时通过超时时间来保证该线程在超时后可以释放锁这样其他线程才可以继续获取锁加锁和解锁必须是由同一个线程来设置Redis 是缓存型数据库拥有很高的性能因此加锁和释放锁开销较小并且能够很轻易地实现分布式锁。
特性
高性能
Redission是基于Redis的因此它继承了Redis的高性能和低延迟的特性。同时它采用了Netty的NIO框架能够并发地处理大量的请求使得应用程序的响应速度得到了极大的提升。
易用性
Redission提供了丰富的API和方法同时还提供了文档和示例让开发者易于上手和使用。此外它支持自动配置和灵活的配置方式使得开发者可以根据自己的需求进行配置和调整。
可扩展性
Redission的分布式架构使得它支持水平扩展可以将数据和请求分散到更多的节点上进行处理。这也使得它具备了更好的容错能力和可靠性。
常用场景
缓存
Redission支持不同的数据存储类型例如String、List、Set、Map、BloomFilter、HyperLogLog等它可以将这些数据存储在Redis中以实现数据缓存的功能从而提高应用程序的性能和响应速度。
分布式锁
Redission提供了可重入锁、公平锁等常用的分布式锁还支持异步执行、锁的自动续期、锁的等待等特性。
分布式队列
Redission提供了分布式队列的实现我们可以在不同的节点之间快速、可靠地实现任务的传递和处理。
Redis分布式锁命令
常用命令 Redis 分布式锁常用命令如下所示 SETNX key val仅当key不存在时设置一个 key 为 value 的字符串返回1若 key 存在设置失败返回 0Expire key timeout为 key 设置一个超时时间以 second 秒为单位超过这个时间锁会自动释放避免死锁DEL key删除 key。 上述 SETNX 命令相当于抢占锁操作EXPIRE 是为避免出现意外用来设置锁的过期时间也就是说到了指定的过期时间该客户端必须让出锁让其他客户端去持有。 但还有一种情况如果在 SETNX 和 EXPIRE 之间服务器进程突然挂掉也就是还未设置过期时间这样就会导致 EXPIRE 执行不了因此还是会造成“死锁”的问题。为了避免这个问题Redis 作者在 2.6.12 版本后对 SET 命令参数做了扩展使它可以同时执行 SETNX 和 EXPIRE 命令从而解决了死锁的问题。 直接使用 SET 命令实现语法格式如下 SET key value [expiration EX seconds|PX milliseconds] [NX|XX] EX second设置键的过期时间为 second 秒。 SET key value EX second 效果等同于 SETEX key second value 。PX millisecond设置键的过期时间为毫秒。SET key value PX millisecond 效果等同于 PSETEX key millisecondvalue 。NX只在键不存在时才对键进行设置操作。 SET key value NX 效果等同于 SETNX key value 。XX只在键已经存在时才对键进行设置操作。 原理
使用
使用redisson实现分布式锁的操作步骤三部曲
第一步 获取锁 RLock redissonLock redisson.getLock(lockKey);第二步 加锁实现锁续命功能 redissonLock.lock();第三步 释放锁 redissonLock.unlock(); Redis 实现分布式锁主要步骤
指定一个 key 作为锁标记存入 Redis 中指定一个 唯一的用户标识 作为 value。当 key 不存在时才能设置值确保同一时间只有一个客户端进程获得锁满足 互斥性 特性。设置一个过期时间防止因系统异常导致没能删除这个 key满足 防死锁 特性。当处理完业务之后需要清除这个 key 来释放锁清除 key 时需要校验 value 值需要满足 只有加锁的人才能释放锁 。 注意 以上实现步骤考虑到了使用分布式锁需要考虑的互斥性、防死锁、加锁和解锁必须为同一个进程等问题但是锁的续期无法实现。所以通常情况都是采用 Redisson 实现 Redis 的分布式锁借助 Redisson 的 WatchDog 机制 能够很好的解决锁续期的问题。 Watch Dog 机制其实就是一个后台定时任务线程获取锁成功之后会将持有锁的线程放入到一个 RedissonLock.EXPIRATION_RENEWAL_MAP里面然后每隔 10 秒 internalLockLeaseTime / 3 检查一下如果客户端 1 还持有锁 key判断客户端是否还持有 key其实就是遍历 EXPIRATION_RENEWAL_MAP 里面线程 id 然后根据线程 id 去 Redis 中查如果存在就会延长 key 的时间那么就会不断的延长锁 key 的生存时间。 Redisson的锁机制
1加锁机制
加锁其实是通过一段 lua 脚本实现的。这里 KEYS[1] 代表的是你加锁的 key比如你自己设置了加锁的那个锁 key 就是 “myLock”。
if (redis.call(exists, KEYS[1]) 0) then redis.call(hincrby, KEYS[1], ARGV[2], 1); redis.call(pexpire, KEYS[1], ARGV[1]); return nil; end;
if (redis.call(hexists, KEYS[1], ARGV[2]) 1) then redis.call(hincrby, KEYS[1], ARGV[2], 1); redis.call(pexpire, KEYS[1], ARGV[1]); return nil; end;
return redis.call(pttl, KEYS[1]); ARGV[1] 代表的是锁 key 的默认生存时间默认 30 秒。ARGV[2] 代表的是加锁的客户端的 ID类似于下面这样285475da-9152-4c83-822a-67ee2f116a79:52。至于最后面的一个 1 是为了后面可重入做的计数统计.
第一段 if 判断语句就是用 exists myLock 命令判断一下如果你要加锁的那个锁 key 不存在的话你就进行加锁。如何加锁呢使用 hincrby 命令设置一个 hash 结构。接着会执行 pexpire myLock 30000 命令设置 myLock 这个锁 key 的生存时间是 30 秒。到此为止加锁完成。
2锁互斥机制
第二个 if 判断判断一下myLock 锁 key 的 hash 数据结构中是否包含客户端 2 的 ID这里明显不是因为那里包含的是客户端 1 的 ID。所以客户端 2 会执行
return redis.call(pttl, KEYS[1]);返回的一个数字这个数字代表了 myLock 这个锁 key 的剩余生存时间。
3Watch dog 机制
主要用于锁续费服务。只要客户端 1 一旦加锁成功就会启动一个 Watch Dog。也就是说leaseTime 必须是 -1 才会开启 Watch Dog 机制也就是如果你想开启 Watch Dog 机制必须使用默认的加锁时间为 30s。
4可重入加锁机制
当锁key已经存在第二个 if 判断会成立因为 myLock 的 hash 数据结构中包含的那个 ID 即客户端 1 的 ID此时就会执行可重入加锁的逻辑
5锁释放机制
删除锁。广播释放锁的消息通知阻塞等待的进程向通道名为 redisson_lock__channel publish 一条 UNLOCK_MESSAGE 信息。取消 Watch Dog 机制即将 RedissonLock.EXPIRATION_RENEWAL_MAP 里面的线程 id 删除并且 cancel 掉 Netty 的那个定时任务线程。
源码分析
重点主要是依赖lua脚本的原子性实现加锁和释放锁的功能。
实例化RedissonLock
super(commandExecutor, name); 父类name赋值后续通过getName()获取commandExecutor 执行lua脚本的executorid 是个UUID 后面被用来当做和threadId组成 value值用作判断加锁和释放锁是否是同一个线程的校验。internalLockLeaseTime : 取自 Config#lockWatchdogTimeout默认30秒这个参数还有另外一个作用锁续命的执行周期 internalLockLeaseTime/3 10秒
加锁和锁的续命redissonLock.lock() Thread.currentThread().interrupt()。当发生异常则通知线程进行中断并释放锁。 lockInterruptibly(-1, null); 此环节会先获取当前线程的线程ID之后会尝试获取锁的剩余时间。 如果当前锁的剩余时间为null,说明没有线程持有该锁直接返回让当前线程加锁成功。如果当前线程的剩余时间不为null,就会一直尝试获取锁。 Redisson 提供了一个续期机制 只要客户端 1 一旦加锁成功就会启动一个 Watch Dog。也就是说leaseTime 必须是 -1 才会开启 Watch Dog 机制也就是如果你想开启 Watch Dog 机制必须使用默认的加锁时间为 30s。如果你自己自定义时间超过这个时间锁就会自定释放并不会延长。 tryAcquireAsync(leaseTime, unit, threadId)自旋获取锁的方法。其内部专门创建异步任务用于尝试获取锁。其任务内部会注册监听事件当剩余时间为null就会再次去获取锁并给锁延长过期时间。 tryLockInnerAsync 其内部实现是lua脚本。 scheduleExpirationRenewal(final long threadId)。又是lua脚本 判断是否存在存在就调用pexpire
释放锁redissonLock.unlock()
unlock()。先获取锁状态如果锁状态为null则抛出异常。否则就释放锁并删除key。unlockInnerAsync(long threadId)。unlock的内部实现也是lua脚本用于获取当前线程的锁状态。
Redissson tryLock源码
Overridepublic boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {long time unit.toMillis(waitTime);long current System.currentTimeMillis();long threadId Thread.currentThread().getId();// 1.尝试获取锁Long ttl tryAcquire(leaseTime, unit, threadId);// lock acquiredif (ttl null) {return true;}// 申请锁的耗时如果大于等于最大等待时间则申请锁失败.time - System.currentTimeMillis() - current;if (time 0) {acquireFailed(threadId);return false;}current System.currentTimeMillis();/*** 2.订阅锁释放事件并通过 await 方法阻塞等待锁释放有效的解决了无效的锁申请浪费资源的问题* 基于信息量当锁被其它资源占用时当前线程通过 Redis 的 channel 订阅锁的释放事件一旦锁释放会发消息通知待等待的线程进行竞争.** 当 this.await 返回 false说明等待时间已经超出获取锁最大等待时间取消订阅并返回获取锁失败.* 当 this.await 返回 true进入循环尝试获取锁./RFutureRedissonLockEntry subscribeFuture subscribe(threadId);// await 方法内部是用 CountDownLatch 来实现阻塞获取 subscribe 异步执行的结果应用了 Netty 的 Futureif (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) {if (!subscribeFuture.cancel(false)) {subscribeFuture.onComplete((res, e) - {if (e null) {unsubscribe(subscribeFuture, threadId);}});}acquireFailed(threadId);return false;}try {// 计算获取锁的总耗时如果大于等于最大等待时间则获取锁失败.time - System.currentTimeMillis() - current;if (time 0) {acquireFailed(threadId);return false;}/** 3.收到锁释放的信号后在最大等待时间之内循环一次接着一次的尝试获取锁* 获取锁成功则立马返回 true* 若在最大等待时间之内还没获取到锁则认为获取锁失败返回 false 结束循环/while (true) {long currentTime System.currentTimeMillis();// 再次尝试获取锁ttl tryAcquire(leaseTime, unit, threadId);// lock acquiredif (ttl null) {return true;}// 超过最大等待时间则返回 false 结束循环获取锁失败time - System.currentTimeMillis() - currentTime;if (time 0) {acquireFailed(threadId);return false;}/** 6.阻塞等待锁通过信号量(共享锁)阻塞,等待解锁消息*/currentTime System.currentTimeMillis();if (ttl 0 ttl time) {//如果剩余时间(ttl)小于wait time ,就在 ttl 时间内从Entry的信号量获取一个许可(除非被中断或者一直没有可用的许可)。getEntry(threadId).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);} else {//则就在wait time 时间范围内等待可以通过信号量getEntry(threadId).getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);}// 更新剩余的等待时间(最大等待时间-已经消耗的阻塞时间)time - System.currentTimeMillis() - currentTime;if (time 0) {acquireFailed(threadId);return false;}}} finally {// 7.无论是否获得锁,都要取消订阅解锁消息unsubscribe(subscribeFuture, threadId);}
// return get(tryLockAsync(waitTime, leaseTime, unit));}流程分析
尝试获取锁返回 null 则说明加锁成功返回一个数值则说明已经存在该锁ttl 为锁的剩余存活时间。如果此时客户端 2 进程获取锁失败那么使用客户端 2 的线程 id其实本质上就是进程 id通过 Redis 的 channel 订阅锁释放的事件。如果等待的过程中一直未等到锁的释放事件通知当超过最大等待时间则获取锁失败返回 false也就是第 39 行代码。如果等到了锁的释放事件的通知则开始进入一个不断重试获取锁的循环。循环中每次都先试着获取锁并得到已存在的锁的剩余存活时间。如果在重试中拿到了锁则直接返回。如果锁当前还是被占用的那么等待释放锁的消息具体实现使用了 JDK 的信号量 Semaphore 来阻塞线程当锁释放并发布释放锁的消息后信号量的 release() 方法会被调用此时被信号量阻塞的等待队列中的一个线程就可以继续尝试获取锁了。 特别注意 以上过程存在一个细节这里有必要说明一下也是分布式锁的一个关键点当锁正在被占用时等待获取锁的进程并不是通过一个 while(true) 死循环去获取锁而是利用了 Redis 的发布订阅机制,通过 await 方法阻塞等待锁的进程有效的解决了无效的锁申请浪费资源的问题。 优缺点
方案优点
Redisson 通过 Watch Dog 机制很好的解决了锁的续期问题。和 Zookeeper 相比较Redisson 基于 Redis 性能更高适合对性能要求高的场景。通过 Redisson 实现分布式可重入锁比原生的 SET mylock userId NX PX milliseconds lua 实现的效果更好些虽然基本原理都一样但是它帮我们屏蔽了内部的执行细节。在等待申请锁资源的进程等待申请锁的实现上也做了一些优化减少了无效的锁申请提升了资源的利用率。
方案缺点
使用 Redisson 实现分布式锁方案最大的问题就是如果你对某个 Redis Master 实例完成了加锁此时 Master 会异步复制给其对应的 slave 实例。但是这个过程中一旦 Master 宕机主备切换slave 变为了 Master。接着就会导致客户端 2 来尝试加锁的时候在新的 Master 上完成了加锁而客户端 1 也以为自己成功加了锁此时就会导致多个客户端对一个分布式锁完成了加锁这时系统在业务语义上一定会出现问题导致各种脏数据的产生。
所以这个就是 Redis Cluster 或者说是 Redis Master-Slave 架构的主从异步复制导致的 Redis 分布式锁的最大缺陷在 Redis Master 实例宕机的时候可能导致多个客户端同时完成加锁。
- 上一篇: 商丘加盟小吃网站免费咨询服务合同模板下载
- 下一篇: 商丘市做网站的公司龙岩网红
相关文章
-
商丘加盟小吃网站免费咨询服务合同模板下载
商丘加盟小吃网站免费咨询服务合同模板下载
- 技术栈
- 2026年04月20日
-
商丘河南网站建设网店怎么开啊流程是怎样
商丘河南网站建设网店怎么开啊流程是怎样
- 技术栈
- 2026年04月20日
-
商铺免费做的网站品牌营销成功案例
商铺免费做的网站品牌营销成功案例
- 技术栈
- 2026年04月20日
-
商丘市做网站的公司龙岩网红
商丘市做网站的公司龙岩网红
- 技术栈
- 2026年04月20日
-
商丘市做网站的公司烟台元和网络科技有限公司
商丘市做网站的公司烟台元和网络科技有限公司
- 技术栈
- 2026年04月20日
-
商丘手机网站建设给客户做一个网站ppt怎么做
商丘手机网站建设给客户做一个网站ppt怎么做
- 技术栈
- 2026年04月20日
