Apache Ignite:如何用IgniteCache避免集群节点请求与缓存重载冲突?
Ignite缓存重载与请求处理的互斥控制方案优化
需求回顾
- 缓存重载操作执行期间,必须拒绝所有外部请求处理
- 外部请求处理过程中,必须禁止触发缓存重载
原方案的核心问题
你提出的计数器方案存在原子性缺失的问题:请求计数的递增操作(counter++后put)没有加锁保护,缓存重载前的计数器检查也未与计数操作互斥。这就导致了你描述的并发漏洞:当首个请求处理完成、重载操作尚未启动的间隙,新请求可以无阻碍地递增计数器并开始处理,最终出现缓存重载与请求处理并行的冲突场景。
优化后的解决方案
利用Ignite的分布式锁来保护两个操作的临界区,确保请求计数的增减、缓存重载的检查与执行都在同一个锁的控制下,从根本上避免并发冲突。
1. 请求处理方法(ProcessMethod)优化
private void processMethod() { IgniteCache<Integer, Integer> counterCache = cacheService.getCache("myCounterCache"); String lockKey = "cache-mutex-lock"; // 全局互斥锁的key // 尝试获取分布式锁,获取失败则直接拒绝请求(或排队等待) Lock lock = counterCache.lock(lockKey); try { // 先检查缓存是否正在重载中 IgniteCache<String, Boolean> refDataCache = cacheService.getCache("myRefDataCache"); Boolean isReloading = refDataCache.get("cacheLoadedKey"); if (Boolean.FALSE.equals(isReloading)) { // 缓存重载中,拒绝请求 throw new ServiceUnavailableException("缓存正在重载,暂时无法处理请求"); } // 计数器递增(原子操作) Integer counter = counterCache.getAndIncrement("counterKey"); if (counter == null) { counterCache.put("counterKey", 1); } } finally { lock.unlock(); } try { // 执行业务处理逻辑 ... } finally { // 处理完成后递减计数器,同样需要锁保护 Lock decrementLock = counterCache.lock(lockKey); try { Integer counter = counterCache.getAndDecrement("counterKey"); if (counter != null && counter == 1) { // 计数器归0时可以清理,可选 counterCache.remove("counterKey"); } } finally { decrementLock.unlock(); } } }
2. 缓存重载方法(ReloadCache)优化
public void reloadCache() { IgniteCache<Integer, Integer> counterCache = cacheService.getCache("myCounterCache"); IgniteCache<String, Boolean> refDataCache = cacheService.getCache("myRefDataCache"); String lockKey = "cache-mutex-lock"; // 与请求处理共用同一个锁key Lock lock = counterCache.lock(lockKey); try { // 检查计数器是否为0,且缓存未在重载中 Integer counter = counterCache.get("counterKey"); Boolean isReloading = refDataCache.get("cacheLoadedKey"); if ((counter == null || counter == 0) && !Boolean.FALSE.equals(isReloading)) { // 标记缓存开始重载 refDataCache.put("cacheLoadedKey", false); try { // 执行缓存重载逻辑 startRefDataCacheReload(); ... // 加载数据到RefCache } finally { // 标记缓存重载完成 refDataCache.put("cacheLoadedKey", true); } } else { // 有请求正在处理或缓存已在重载,直接返回或重试 return; } } finally { lock.unlock(); } }
关键改进点
- 全局分布式锁:使用同一个锁key控制请求处理和缓存重载的所有临界操作,确保同一时间只有一类操作可以执行
- 原子计数操作:使用Ignite的
getAndIncrement/getAndDecrement方法替代手动的get+put,避免计数操作的中间状态不一致 - 状态标记检查:在请求处理前先检查缓存重载状态,直接拒绝重载期间的请求,避免无效处理
- finally块保护:所有锁的释放和状态恢复都放在finally块中,避免异常导致的锁泄漏或状态不一致
内容的提问来源于stack exchange,提问作者vs777
相关产品推荐
相关产品推荐

