// ... 原有package和import...
import com.google.common.cache.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
@Service
public class ActivityArmory implements IActivityArmory, IActivityDispatch {
// 新增补偿队列(线程安全)
private final BlockingQueue<StockCompensation> compensationQueue = new LinkedBlockingQueue<>(10000);
// 本地缓存配置(5秒过期,最大缓存10000个SKU)
private Cache<Long, AtomicInteger> localStockCache = CacheBuilder.newBuilder()
.expireAfterWrite(5, TimeUnit.SECONDS)
.maximumSize(10000)
.removalListener(notification -> {
if (notification.getKey() != null) {
int finalStock = notification.getValue().get();
compensationQueue.offer(new StockCompensation(
(Long) notification.getKey(),
finalStock
));
}
})
.build();
// 初始化补偿任务
@PostConstruct
private void initCompensationTask() {
Executors.newSingleThreadScheduledExecutor()
.scheduleAtFixedRate(this::processCompensation, 1, 1, TimeUnit.SECONDS);
}
// 库存扣减方法改造
@Override
public boolean subtractionActivitySkuStock(Long sku, Date endDateTime) {
String cacheKey = Constants.RedisKey.ACTIVITY_SKU_STOCK_COUNT_KEY + sku;
try {
// 优先尝试Redis扣减
boolean redisResult = activityRepository.subtractionActivitySkuStock(sku, cacheKey, endDateTime);
if (redisResult) return true;
} catch (Exception e) {
log.warn("Redis操作异常,降级本地处理", e);
}
// 本地安全扣减
return safeLocalDecrement(sku, cacheKey);
}
// 带补偿的安全扣减
private boolean safeLocalDecrement(Long sku, String cacheKey) {
try {
AtomicInteger stock = localStockCache.get(sku, () ->
new AtomicInteger(getRedisStock(cacheKey)));
int current = stock.decrementAndGet();
if (current >= 0) {
compensationQueue.offer(new StockCompensation(sku, -1));
return true;
}
stock.incrementAndGet(); // 回滚超扣库存
return false;
} catch (Exception e) {
log.error("本地降级模式异常", e);
return false;
}
}
// 补偿处理逻辑
private void processCompensation() {
while (!compensationQueue.isEmpty()) {
StockCompensation comp = compensationQueue.poll();
try {
String cacheKey = Constants.RedisKey.ACTIVITY_SKU_STOCK_COUNT_KEY + comp.sku;
if (comp.delta < 0) {
// 扣减补偿(原子操作)
redisTemplate.opsForValue().decrement(cacheKey, -comp.delta);
} else {
// 最终值补偿(覆盖更新)
redisTemplate.opsForValue().set(cacheKey, comp.delta);
}
} catch (Exception e) {
log.error("库存补偿失败,重新入队", e);
compensationQueue.offer(comp);
}
}
}
// 补偿数据载体
private static class StockCompensation {
final Long sku;
final Integer delta;
// 构造方法省略...
}
// ... 其他原有方法保持不变 ...
}
实现方案说明 :
1. 双轨库存机制:Redis主存储 + 本地缓存降级
2. 最终一致性保障:通过补偿队列实现异步同步
3. 异常处理:Redis不可用时自动降级,恢复后自动补偿
4. 线程安全:采用线程安全队列和原子操作类
5. 性能优化:本地缓存5秒过期,补偿任务1秒执行