建站套餐推荐,wordpress文章详情展示不了,合肥网站建设网站,WordPress怎么用cdn方案概述
本方案基于 Redisson 实现分布式锁#xff0c;结合重试机制和双重检查模式#xff0c;确保在高并发场景下的数据一致性和系统稳定性。
核心特性
✅ 分布式锁#xff1a;防止多实例/多线程并发执行✅ 重试机制#xff1a;提高系统容错能力✅ 双重检查#xff1…方案概述本方案基于Redisson实现分布式锁结合重试机制和双重检查模式确保在高并发场景下的数据一致性和系统稳定性。核心特性✅分布式锁防止多实例/多线程并发执行✅重试机制提高系统容错能力✅双重检查减少不必要的锁竞争✅缓存降级锁获取失败时尝试从缓存读取✅异常处理完善的异常捕获和日志记录核心设计模式1. 双重检查锁定Double-Check Locking┌─────────────────────────────────────────┐ │ 1. 检查缓存无锁 │ │ ↓ 缓存未命中 │ │ 2. 获取分布式锁 │ │ ↓ 获取成功 │ │ 3. 再次检查缓存双重检查 │ │ ↓ 缓存仍未命中 │ │ 4. 执行业务逻辑 │ │ 5. 更新缓存 │ │ 6. 释放锁 │ └─────────────────────────────────────────┘优势减少锁竞争大部分请求在第一次检查时就能从缓存获取数据避免重复执行获取锁后再次检查确保不重复执行2. 重试机制Retry Pattern尝试次数: 1 ──失败── 等待 200ms ── 尝试次数: 2 ──失败── 等待 400ms ── 尝试次数: 3 ──失败── 抛出异常特点指数退避每次重试间隔递增200ms, 400ms, 600ms…最大重试次数防止无限重试异常记录记录最后一次异常信息Redisson 分布式锁详解lock.tryLock(waitTime, leaseTime, timeUnit)方法详解方法签名booleantryLock(longwaitTime,longleaseTime,TimeUnitunit)throwsInterruptedException参数说明参数类型说明示例值waitTimelong等待时间尝试获取锁的最大等待时间。如果在这段时间内无法获取锁方法返回false15leaseTimelong持有时间锁的自动释放时间。超过这个时间锁会自动释放即使业务逻辑未完成30timeUnitTimeUnit时间单位TimeUnit.SECONDS示例调用RLocklockredissonClient.getLock(lock:key);booleanlockedlock.tryLock(15,30,TimeUnit.SECONDS);含义最多等待15秒尝试获取锁如果获取成功锁将在30秒后自动释放时间单位秒关键问题解答1. 是否会自动续命看门狗机制答案❌ 不会自动续命当使用tryLock(waitTime, leaseTime, timeUnit)方法并指定了leaseTime参数时Redisson 的看门狗机制会被禁用。原因看门狗机制只在未指定leaseTime时生效指定leaseTime后Redisson 认为你希望锁在固定时间后自动释放这是为了避免死锁但可能导致业务逻辑未完成时锁被释放验证方法// 方式1指定 leaseTime无看门狗lock.tryLock(15,30,TimeUnit.SECONDS);// ❌ 无看门狗// 方式2不指定 leaseTime有看门狗lock.tryLock(15,-1,TimeUnit.SECONDS);// ✅ 有看门狗-1 表示不设置过期时间// 或者lock.lock();// ✅ 有看门狗默认30秒续命2. 如果 30 秒内业务没做完会出现什么情况场景分析时间线 T0s: 线程A获取锁开始执行业务逻辑预计需要45秒 T30s: 锁自动释放leaseTime到期 T31s: 线程B获取锁开始执行业务逻辑 T45s: 线程A的业务逻辑完成尝试释放锁可能失败或释放了线程B的锁可能的问题重复执行多个线程可能同时执行相同的业务逻辑数据不一致并发修改可能导致数据冲突资源浪费重复调用外部接口增加系统负载锁释放异常线程A可能释放了线程B的锁解决方案✅ 使用看门狗机制见下文✅ 合理设置leaseTime确保大于业务执行时间✅ 业务逻辑中增加幂等性检查✅ 使用lock.isHeldByCurrentThread()检查锁的持有者看门狗机制详解什么是看门狗Watchdog看门狗是 Redisson 提供的一种自动续命机制用于防止业务逻辑执行时间超过锁的持有时间。工作原理┌─────────────────────────────────────────────────────┐ │ 1. 获取锁不指定 leaseTime │ │ 2. Redisson 启动看门狗线程 │ │ 3. 每 10 秒检查一次锁是否仍被当前线程持有 │ │ 4. 如果持有自动续命 30 秒默认值 │ │ 5. 业务逻辑完成后释放锁看门狗停止 │ │ 6. 如果线程异常退出锁在 30 秒后自动释放 │ └─────────────────────────────────────────────────────┘如何启用看门狗方式1使用lock()方法推荐RLocklockredissonClient.getLock(lock:key);try{// 先尝试获取锁最多等待15秒if(lock.tryLock(15,-1,TimeUnit.SECONDS)){try{// 业务逻辑// 看门狗会自动续命}finally{if(lock.isHeldByCurrentThread()){lock.unlock();}}}}catch(InterruptedExceptione){Thread.currentThread().interrupt();thrownewRuntimeException(获取锁被中断,e);}注意leaseTime设置为-1表示不设置过期时间启用看门狗。方式2使用lock()无参方法RLocklockredissonClient.getLock(lock:key);try{// 阻塞等待获取锁启用看门狗lock.lock();try{// 业务逻辑// 看门狗会自动续命}finally{if(lock.isHeldByCurrentThread()){lock.unlock();}}}catch(Exceptione){// 异常处理}注意lock()方法会阻塞等待直到获取到锁。方式3使用lock(long leaseTime, TimeUnit unit)并手动续命RLocklockredissonClient.getLock(lock:key);try{if(lock.tryLock(15,30,TimeUnit.SECONDS)){try{// 业务逻辑// 如果预计执行时间超过30秒需要手动续命if(需要续命){lock.expire(30,TimeUnit.SECONDS);// 手动续命30秒}}finally{if(lock.isHeldByCurrentThread()){lock.unlock();}}}}catch(InterruptedExceptione){Thread.currentThread().interrupt();thrownewRuntimeException(获取锁被中断,e);}看门狗配置Redisson 默认配置续命间隔lockWatchdogTimeout 30秒默认值续命时长每次续命 30秒自定义配置ConfigconfignewConfig();// 设置看门狗超时时间为60秒config.setLockWatchdogTimeout(60000);// 单位毫秒RedissonClientredissonClientRedisson.create(config);看门狗 vs 固定过期时间特性看门狗机制固定过期时间适用场景业务执行时间不确定业务执行时间可预估自动续命✅ 是❌ 否死锁风险低异常退出时自动释放低固定时间后释放重复执行风险低高超时后可能重复执行性能开销略高需要后台线程低推荐使用✅ 业务时间不确定时✅ 业务时间确定且较短时重试机制设计设计原则指数退避重试间隔逐渐增加避免系统过载最大重试次数防止无限重试异常记录记录最后一次异常便于排查中断处理正确处理线程中断实现示例/** * 带重试机制的获取资源方法 * * param maxRetries 最大重试次数 * return 资源对象 */privateStringgetResourceWithRetry(intmaxRetries){intretryCount0;ExceptionlastExceptionnull;while(retryCountmaxRetries){try{returntryGetResourceWithLock();}catch(Exceptione){lastExceptione;retryCount;if(retryCountmaxRetries){// 指数退避200ms, 400ms, 600ms...longsleepMs200L*retryCount;log.warn(第{}次尝试失败{}ms后重试错误: {},retryCount,sleepMs,e.getMessage());try{Thread.sleep(sleepMs);}catch(InterruptedExceptionie){Thread.currentThread().interrupt();thrownewRuntimeException(获取资源被中断,ie);}}}}log.error(重试{}次后仍然失败,maxRetries);thrownewRuntimeException(获取资源失败: (lastException!null?lastException.getMessage():未知错误));}重试策略对比策略公式示例3次重试适用场景固定间隔sleepMs fixed200ms, 200ms, 200ms系统负载稳定线性递增sleepMs base * retryCount200ms, 400ms, 600ms通用场景推荐指数退避sleepMs base * 2^(retryCount-1)200ms, 400ms, 800ms高并发场景随机退避sleepMs random(base, max)150-250ms, 300-500ms避免惊群效应完整实现示例标准模板代码ComponentSlf4jpublicclassResourceManager{privatestaticfinalStringRESOURCE_CACHE_KEYresource:cache:key;privatestaticfinalStringRESOURCE_LOCK_KEYlock:resource:key;privatestaticfinalintMAX_RETRIES3;ResourceprivateRedissonClientredissonClient;/** * 获取资源带缓存和重试机制 * * param forceRefresh 是否强制刷新 * return 资源对象 */publicStringgetResource(booleanforceRefresh){// 1. 如果不是强制刷新先从缓存读取if(!forceRefresh){RBucketStringbucketredissonClient.getBucket(RESOURCE_CACHE_KEY);StringcachedResourcebucket.get();if(StringUtils.isNotBlank(cachedResource)){log.debug(资源从缓存获取成功);returncachedResource;}}// 2. 使用分布式锁获取资源增加重试机制returngetResourceWithRetry(forceRefresh,MAX_RETRIES);}/** * 带重试机制的获取资源 * * param forceRefresh 是否强制刷新 * param maxRetries 最大重试次数 * return 资源对象 */privateStringgetResourceWithRetry(booleanforceRefresh,intmaxRetries){intretryCount0;ExceptionlastExceptionnull;while(retryCountmaxRetries){try{returntryGetResourceWithLock(forceRefresh);}catch(Exceptione){lastExceptione;retryCount;if(retryCountmaxRetries){longsleepMs200L*retryCount;// 递增延迟log.warn(第{}次尝试失败{}ms后重试错误: {},retryCount,sleepMs,e.getMessage());try{Thread.sleep(sleepMs);}catch(InterruptedExceptionie){Thread.currentThread().interrupt();thrownewRuntimeException(获取资源被中断,ie);}}}}log.error(重试{}次后仍然失败,maxRetries);thrownewRuntimeException(获取资源失败: (lastException!null?lastException.getMessage():未知错误));}/** * 使用分布式锁尝试获取资源 * * param forceRefresh 是否强制刷新 * return 资源对象 */privateStringtryGetResourceWithLock(booleanforceRefresh){RLocklockredissonClient.getLock(RESOURCE_LOCK_KEY);try{// 方案1使用固定过期时间适合业务时间可预估的场景booleanlockedlock.tryLock(15,30,TimeUnit.SECONDS);// 方案2使用看门狗机制适合业务时间不确定的场景// boolean locked lock.tryLock(15, -1, TimeUnit.SECONDS);if(!locked){log.warn(获取分布式锁超时);// 锁获取失败时再次尝试从缓存读取if(!forceRefresh){RBucketStringbucketredissonClient.getBucket(RESOURCE_CACHE_KEY);StringcachedResourcebucket.get();if(StringUtils.isNotBlank(cachedResource)){log.info(锁超时后从缓存获取到资源);returncachedResource;}}thrownewRuntimeException(获取分布式锁超时可能系统繁忙);}try{// 3. 双重检查避免重复获取if(!forceRefresh){RBucketStringbucketredissonClient.getBucket(RESOURCE_CACHE_KEY);StringcachedResourcebucket.get();if(StringUtils.isNotBlank(cachedResource)){log.info(获取锁后从缓存获取到资源双重检查);returncachedResource;}}// 4. 执行业务逻辑获取资源StringresourcefetchResourceFromSource();// 5. 写入缓存if(StringUtils.isNotBlank(resource)){RBucketStringbucketredissonClient.getBucket(RESOURCE_CACHE_KEY);bucket.set(resource,3600,TimeUnit.SECONDS);// 缓存1小时log.info(资源已缓存);}returnresource;}finally{// 6. 释放锁确保只释放当前线程持有的锁if(lock.isHeldByCurrentThread()){lock.unlock();log.debug(锁已释放);}}}catch(InterruptedExceptione){log.error(获取锁时被中断,e);Thread.currentThread().interrupt();thrownewRuntimeException(获取资源被中断,e);}}/** * 从数据源获取资源业务逻辑 */privateStringfetchResourceFromSource(){// 实现具体的业务逻辑// 例如调用外部API、查询数据库等returnresource_data;}}最佳实践与参数推荐lock.tryLock()参数推荐场景1业务执行时间可预估 30秒// 推荐配置booleanlockedlock.tryLock(15,30,TimeUnit.SECONDS);参数说明waitTime 15秒等待时间适中避免长时间阻塞leaseTime 30秒根据业务最大执行时间设置建议设置为业务最大执行时间 * 1.5适用场景Token刷新、缓存预热、数据同步等场景2业务执行时间不确定// 推荐配置使用看门狗机制booleanlockedlock.tryLock(15,-1,TimeUnit.SECONDS);// 或者lock.lock();// 阻塞等待启用看门狗参数说明waitTime 15秒等待时间leaseTime -1不设置过期时间启用看门狗适用场景复杂计算、批量处理、长时间任务等场景3高并发场景// 推荐配置缩短等待时间避免线程堆积booleanlockedlock.tryLock(5,20,TimeUnit.SECONDS);参数说明waitTime 5秒快速失败避免线程堆积leaseTime 20秒根据实际业务时间设置适用场景秒杀、限流、高频接口等参数选择决策树业务执行时间是否可预估 ├─ 是 │ ├─ 执行时间 10秒 → waitTime10s, leaseTime15s │ ├─ 执行时间 10-30秒 → waitTime15s, leaseTime30s │ └─ 执行时间 30秒 → 考虑使用看门狗机制 │ └─ 否 └─ 使用看门狗机制 → waitTime15s, leaseTime-1通用推荐值业务类型waitTimeleaseTime是否看门狗说明Token刷新15s30s❌通常很快完成缓存预热10s20s❌数据加载较快数据同步30s60s❌中等耗时操作批量处理15s-1✅时间不确定复杂计算15s-1✅时间不确定外部API调用15s30s❌有超时控制重试机制参数推荐场景最大重试次数初始延迟退避策略说明高可用要求5200ms线性递增提高成功率快速失败2100ms固定间隔快速响应网络不稳定3300ms指数退避适应网络波动通用场景3200ms线性递增推荐配置常见问题与解决方案Q1: 锁获取失败后应该如何处理问题tryLock()返回false时业务逻辑无法执行。解决方案降级策略尝试从缓存读取如示例代码重试机制外层增加重试逻辑快速失败直接返回错误由调用方处理Q2: 如何避免死锁问题业务异常导致锁未释放。解决方案✅ 使用try-finally确保锁释放✅ 使用lock.isHeldByCurrentThread()检查✅ 设置合理的leaseTime或使用看门狗✅ 避免在锁内调用可能阻塞的方法Q3: 锁被其他线程释放怎么办问题线程A的锁被线程B释放。解决方案// ✅ 正确检查锁的持有者if(lock.isHeldByCurrentThread()){lock.unlock();}// ❌ 错误直接释放lock.unlock();// 可能释放其他线程的锁Q4: 如何监控锁的使用情况解决方案日志记录记录锁获取/释放时间指标监控统计锁等待时间、持有时间告警机制锁等待时间过长时告警StopWatchstopWatchStopWatch.createStarted();booleanlockedlock.tryLock(15,30,TimeUnit.SECONDS);longwaitTimestopWatch.getTime();if(waitTime5000){log.warn(锁等待时间过长: {}ms,waitTime);}Q5: 分布式锁的性能影响优化建议减少锁粒度只锁必要的资源缩短持有时间尽快释放锁使用本地锁单机场景优先使用synchronized缓存降级锁获取失败时使用缓存总结核心要点双重检查减少锁竞争提高性能重试机制提高系统容错能力合理参数根据业务场景选择waitTime和leaseTime看门狗机制业务时间不确定时使用异常处理确保锁正确释放选择建议✅业务时间可预估→ 使用固定leaseTime✅业务时间不确定→ 使用看门狗机制✅高并发场景→ 缩短waitTime快速失败✅高可用要求→ 增加重试次数使用降级策略参考资源Redisson 官方文档分布式锁最佳实践