基于 Redis 分布式锁

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class KafkaConsumer {

    @Autowired
    private AsyncKafKaTask asyncKafKaTask;

    @Autowired
    private StringRedisTemplate redisTemplate; // 注入 Redis 模板

    // 锁的 Key 前缀
    private static final String LOCK_KEY_PREFIX = "scada:task:lock:";

    @KafkaListener(topics = {"scada"}, groupId = "${random.id}")
    public void onMessage(List<ConsumerRecord<String, String>> records) {
        // 1. 获取当前时间
        LocalDateTime now = LocalDateTime.now();
        int hour = now.getHour();
        
        // 2. 构造 Redis Key,按小时隔离,例如:scada:task:lock:2023102710
        // 这样每个小时的锁都是独立的,互不干扰
        String lockKey = LOCK_KEY_PREFIX + now.format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMddHH"));

        // 3. 尝试获取锁 (setIfAbsent 即 SETNX 操作)
        // 设置过期时间为 70 分钟,防止极端情况下锁无法释放(虽然按小时逻辑不太可能发生)
        Boolean isLocked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 70, TimeUnit.MINUTES);

        if (Boolean.TRUE.equals(isLocked)) {
            // --- 拿到锁,说明我是这一小时内第一个来处理消息的 ---
            log.info("捕获到整点任务窗口,开始执行每小时任务...");
            
            try {
                // 执行业务逻辑
                asyncKafKaTask.consumeTask(records);
                log.info("每小时任务执行成功");
            } catch (Exception e) {
                log.error("每小时任务执行失败", e);
                // 注意:这里可以选择是否删除锁。
                // 如果希望下次重试,可以删除锁;如果希望这一小时内不再重试,则保留锁直到过期。
                // redisTemplate.delete(lockKey); 
            }
        } else {
            // --- 没拿到锁,说明这一小时内已经有人执行过了 ---
            log.debug("本小时内任务已执行,跳过当前消息。");
            // 直接返回,不抛出异常,让 Kafka 正常提交偏移量,避免死循环重试
            return;
        }
    }
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值