代码锁工具类,重点是key生成策略

package com.lk.node.common.aspect;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 幂等性存储组件
 * 使用内存存储幂等性状态,支持分布式环境
 * 
 * @author CHENYB
 * @since 2025-09-22
 */
@Slf4j
@Component
public class IdempotentStorage {
    
    /**
     * 存储幂等性锁的Map
     * Key: 幂等性键
     * Value: 锁对象和过期时间
     */
    private final ConcurrentHashMap<String, LockInfo> lockMap = new ConcurrentHashMap<>();
    
    /**
     * 清理过期锁的定时任务间隔(毫秒)
     */
    @Value("${idempotent.storage.cleanup-interval:3600000}")
    private long cleanupInterval;
    
    /**
     * 锁过期时间(毫秒)
     */
    @Value("${idempotent.storage.lock-expire-time:600000}")
    private long lockExpireTime;
    
    /**
     * 上次清理时间
     */
    private volatile long lastCleanupTime = System.currentTimeMillis();
    
    /**
     * 尝试获取锁
     * 
     * @param key 幂等性键
     * @param expireTime 过期时间
     * @param timeUnit 时间单位
     * @return true-获取成功,false-已存在
     */
    public boolean tryLock(String key, long expireTime, TimeUnit timeUnit) {
        long expireTimeMillis = System.currentTimeMillis() + timeUnit.toMillis(expireTime);
        
        // 尝试创建新的锁信息
        LockInfo newLockInfo = new LockInfo(expireTimeMillis);
        LockInfo existingLockInfo = lockMap.putIfAbsent(key, newLockInfo);
        
        if (existingLockInfo == null) {
            // 成功创建新锁
            log.debug("成功获取幂等性锁 - 键: {}, 过期时间: {}", key, expireTimeMillis);
            return true;
        } else {
            // 锁已存在,检查是否过期
            if (System.currentTimeMillis() > existingLockInfo.getExpireTime()) {
                // 锁已过期,尝试替换
                if (lockMap.replace(key, existingLockInfo, newLockInfo)) {
                    log.debug("替换过期锁成功 - 键: {}, 过期时间: {}", key, expireTimeMillis);
                    return true;
                } else {
                    // 替换失败,说明有其他线程已经更新了锁
                    return false;
                }
            } else {
                // 锁未过期,获取失败
                log.debug("获取幂等性锁失败 - 键: {}, 剩余时间: {}ms", 
                        key, existingLockInfo.getExpireTime() - System.currentTimeMillis());
                return false;
            }
        }
    }
    
    /**
     * 释放锁
     * 
     * @param key 幂等性键
     */
    public void releaseLock(String key) {
        LockInfo removed = lockMap.remove(key);
        if (removed != null) {
            log.debug("释放幂等性锁 - 键: {}", key);
        }
    }
    
    /**
     * 检查锁是否存在且未过期
     * 
     * @param key 幂等性键
     * @return true-存在且未过期,false-不存在或已过期
     */
    public boolean isLocked(String key) {
        LockInfo lockInfo = lockMap.get(key);
        if (lockInfo == null) {
            return false;
        }
        
        if (System.currentTimeMillis() > lockInfo.getExpireTime()) {
            // 锁已过期,移除
            lockMap.remove(key, lockInfo);
            return false;
        }
        
        return true;
    }
    
    /**
     * 清理过期的锁
     */
    private void cleanupExpiredLocks() {
        long currentTime = System.currentTimeMillis();
        
        // 避免频繁清理,设置最小间隔
        if (currentTime - lastCleanupTime < cleanupInterval) {
            return;
        }
        
        lastCleanupTime = currentTime;
        
        int cleanedCount = 0;
        for (String key : lockMap.keySet()) {
            LockInfo lockInfo = lockMap.get(key);
            if (lockInfo != null && currentTime > lockInfo.getExpireTime()) {
                if (lockMap.remove(key, lockInfo)) {
                    cleanedCount++;
                }
            }
        }
        
        if (cleanedCount > 0) {
            log.debug("清理过期锁完成 - 清理数量: {}, 当前锁数量: {}", cleanedCount, lockMap.size());
        }
    }
    
    /**
     * 获取当前锁的数量
     */
    public int getLockCount() {
        return lockMap.size();
    }
    
    /**
     * 定时清理过期锁(使用配置的清理间隔)
     */
    @Scheduled(fixedRateString = "${idempotent.storage.cleanup-interval:3600000}")
    public void scheduledCleanup() {
        try {
            cleanupExpiredLocks();
            log.info("定时清理幂等性锁完成 - 当前锁数量: {}, 清理间隔: {}ms", lockMap.size(), cleanupInterval);
        } catch (Exception e) {
            log.error("定时清理幂等性锁异常: {}", e.getMessage(), e);
        }
    }
    
    /**
     * 清空所有锁(用于测试)
     */
    public void clearAll() {
        lockMap.clear();
        log.debug("清空所有幂等性锁");
    }
    
    /**
     * 锁信息内部类
     */
    private static class LockInfo {
        private final long expireTime;
        
        public LockInfo(long expireTime) {
            this.expireTime = expireTime;
        }
        
        public long getExpireTime() {
            return expireTime;
        }
    }
}

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐