ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Redisson 如何用 RFencedLock 获取围栏令牌保护外部资源

Redisson 如何用 RFencedLock 获取围栏令牌保护外部资源 Redisson 如何用 RFencedLock 获取围栏令牌保护外部资源【免费下载链接】redissonRedisson: Valkey Redis Java Client and Real-Time Data Platform. Sync/Async/RxJava/Reactive API. Over 50 Valkey and Redis based Java objects and services: Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Spring, Tomcat, Scheduler, JCache API, Hibernate, RPC, local cache..项目地址: https://gitcode.com/GitHub_Trending/re/redisson当你用分布式锁保护一个外部资源数据库、对象存储、下游系统的写入时会面临一个普通RLock无法覆盖的问题持锁的客户端可能因长时间 GC 暂停或网络故障被挂起期间锁过期、其他客户端拿到锁并写入等第一个客户端恢复后它仍然以为自己持有着锁继续写入就会造成脏写。Redisson 的RFencedLock就是为这个场景设计的它在每次成功获取锁时返回一个单调递增的 fencing token被保护的资源端记录自己接受过的最高令牌凡是携带更低令牌的操作一律拒绝。这样即使持锁客户端失忆复活它的写入也会被资源端挡下fenced out。适用前提Java 项目已通过 Redisson 客户端连接到 Valkey 或 Redis被锁保护的写入方是能够接收并校验 token 的外部系统。如果锁只是用于进程间/节点间的协调不涉及外部系统强制校验文档给出的建议是直接用普通 RLock 即可。为什么普通锁不够令牌机制解决的场景Redisson 的分布式锁是协作式advisory的安全性依赖部署拓扑。文档用这样一个序列说明故障窗口客户端 A 在主节点上成功获取锁后主节点在锁写入尚未同步到副本时宕机副本被提升为新主且没有该锁的记录客户端 B 随即成功获取同一把锁两个客户端都以为自己持锁。Redisson 默认通过副本同步检查checkLockSyncedSlaves默认开启等待超时slavesSyncTimeout默认 1000 毫秒超时后释放锁并让本次获取失败来收窄这个窗口。而 fencing token 解决的是剩余风险把我认为我持锁变成资源端可以核验的事实。RFencedLock每次获取都返回一个新令牌资源端只接受不低于已见最高值的令牌。文档同时指出已弃用的RedLock由RLock配合副本同步检查和RFencedLock需要围栏令牌时取代。准备创建客户端并获取 RFencedLock先在项目中添加 Redisson 依赖源文档中版本号以xVERSIONx占位替换为你实际使用的版本dependency groupIdorg.redisson/groupId artifactIdredisson/artifactId versionxVERSIONx/version /dependency然后按 configuration.md 的 Single mode 一节创建客户端以单节点为例地址替换为你自己的 Valkey/Redis 地址Config config new Config(); // use redis:// for Redis connection // use valkey:// for Valkey connection // use rediss:// for Redis SSL connection config.useSingleServer().setAddress(redis://myredisserver:6379); RedissonClient redisson Redisson.create(config);RedissonClient是线程安全的创建一次后在应用内复用即可应用退出时调用redisson.shutdown()。获取围栏锁对象RFencedLock lock redisson.getFencedLock(myLock);getFencedLock(name)在 RedissonClient 中的说明为实现的是 non-fair 锁不保证线程间的获取顺序。主路径获取锁、拿到令牌、交给资源端校验同步 API 的完整流程如下取自 locks-and-synchronizers.md 的 Fenced Lock 专节RFencedLock lock redisson.getFencedLock(myLock); // 阻塞获取锁成功时返回递增后的 fencing token Long token lock.lockAndGetToken(); try { // 将 token 传给被保护的资源资源端必须拒绝任何 // token 低于其已接受最高值的写入 storage.write(data, token); } finally { lock.unlock(); }要点逐步说明lockAndGetToken()在锁被占用时阻塞等待直到获取成功返回Long类型的令牌无需判空。被保护资源的校验规则由文档明确给出资源端记录已见最高令牌拒绝携带更低令牌的操作即校验token 已接受的最大值不满足则拒绝本次操作。文档示例中这一逻辑以注释形式给出check if token old token下面是一个按该规则实现的最小示例具体存储操作由你自己的系统完成// 示例资源端对令牌做单调性校验规则来自文档存储操作自行替换 long lastAcceptedToken 0; void write(Object data, long token) { if (token lastAcceptedToken) { // 令牌低于已接受最大值持锁方已过期拒绝操作 throw new IllegalStateException(stale fencing token: token); } lastAcceptedToken token; // ... 这里执行真实的写存储操作 }unlock()放在finally中确保异常路径也能释放锁。注意只有获取锁的线程可以解锁否则抛出IllegalMonitorStateException。判断本次获取是否成功lockAndGetToken()返回非空令牌即成功改用tryLockAndGetToken(...)时返回null表示在等待窗口内没有拿到锁此时不能执行受保护操作也无需调用unlock()。验证令牌行为单调递增仓库自带测试 RedissonFencedLockTest 展示了可核对的验证方式对同一把锁连续获取令牌依次为 1、2、3、4、5同时验证了普通lock()之后调用getToken()能拿到当前值该测试中为 3、4tryLockAndGetToken()成功时返回令牌 5。据此可以做两项检查递增检查同一把锁连续获取两次第二次lockAndGetToken()返回的令牌应大于第一次getToken()在不获取锁的情况下返回当前令牌。围栏检查用一个低于资源端已接受最高值的令牌调用写入资源端应拒绝该操作——这正是被挂起后复活的过期持锁方会被挡下的效果。可选的获取变体文档为同一把锁提供了多个获取入口按场景选用带租约时间到期自动释放对应leaseTime参数// 获取锁并在 10 秒后自动解锁 Long token lock.lockAndGetToken(10, TimeUnit.SECONDS);限时尝试获取拿不到就放弃// 最多等待 100 秒持有 10 秒后自动释放未获取到时返回 null Long token lock.tryLockAndGetToken(100, 10, TimeUnit.SECONDS); if (token ! null) { try { // 执行带令牌校验的资源写入 ... } finally { lock.unlock(); } }异步 API 下返回RFutureLong方法名带Async后缀文档示例中unlockAsync(threadId)需要显式传入线程 idRFencedLock lock redisson.getFencedLock(myLock); RFutureLong lockFuture lock.tryLockAndGetTokenAsync(100, 10, TimeUnit.SECONDS); long threadId Thread.currentThread().getId(); lockFuture.whenComplete((token, exception) - { if (token ! null) { try { // 执行带令牌校验的资源写入 ... } finally { lock.unlockAsync(threadId); } } });Reactive 与 RxJava3 的对应方法为lockAndGetToken()/lockAndGetToken(leaseTime, unit)/tryLockAndGetToken(waitTime, leaseTime, unit)返回MonoLong/SingleLong。文档对锁类对象有一段通用提示在异步、Reactive、RxJava3 API 中一次逻辑操作可能跨多个线程执行而锁的归属绑定线程 id因此要捕获Thread.currentThread().getId()并把同一个threadId传给lock、tryLock、unlock调用。行为细节与限制可重入、非公平RFencedLock是可重入锁接口 RFencedLock 的说明但不保证跨线程的获取顺序。解锁归属只有所属线程能解锁否则抛IllegalMonitorStateException。看门狗持锁的 Redisson 实例崩溃时锁可能一直处于已获取状态看门狗在持有方存活期间续期锁的过期时间默认 30 秒可通过Config.lockWatchdogTimeout修改。副本同步检查checkLockSyncedSlaves默认开启与slavesSyncTimeout默认 1000 毫秒作用于RLock、RSemaphore、RPermitExpirableSemaphoreRFencedLock继承RLock共享同一套锁机制。开启检查后若必需副本未在规定时间内确认同步Redisson 会释放锁并让本次获取失败客户端不会保留一把可能因故障转移丢失的锁。适用边界文档明确 fenced lock 用于锁保护的是能够强制校验 token 的外部系统写入纯进程内协调用普通RLock。令牌本身只是数值Redisson 不会替你调用资源端做校验拒绝低令牌的逻辑必须由被保护方实现。下一步令牌获取与校验的完整方法签名见 RFencedLock.java含lockAndGetToken、tryLockAndGetToken、getToken各重载锁类型选型对比表见 locks-and-synchronizers.md 开头的 Choosing a lock客户端创建与连接模式见 getting-started.md。【免费下载链接】redissonRedisson: Valkey Redis Java Client and Real-Time Data Platform. Sync/Async/RxJava/Reactive API. Over 50 Valkey and Redis based Java objects and services: Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Spring, Tomcat, Scheduler, JCache API, Hibernate, RPC, local cache..项目地址: https://gitcode.com/GitHub_Trending/re/redisson创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表