Spring定时任务设计

在 Spring Cloud 微服务架构中,多个实例部署同一个微服务时,若需要执行定时任务(如每天凌晨清理日志、同步数据等),必须确保同一时间只有一个实例执行该任务,否则会造成重复执行、数据不一致等问题。为实现这一点,可以借助 Redis 分布式锁 来协调多个实例。 下面将详细介绍如何在 Spring Cloud 微服务中使用 Redis 实现分布式定时任务的方案。

一、整体思路

  • 使用 @Scheduled 注解定义定时任务。

  • 在定时任务执行前尝试获取 Redis 分布式锁。

  • 若成功获取锁,则执行业务逻辑;否则跳过本次执行。

  • 任务执行完成后释放锁。

  • 锁需设置合理的过期时间(防止死锁)。

  • 使用 Redis 的 SET key value NX PX 命令原子性地加锁。

二、核心组件设计

1. Redis 分布式锁工具类

查看项目,我的在 common 包中已经存在了可以使用的 redis 组件

建议去git仓库找开源的代码或者直接让ai写个工具类

其中存在 set() 方法可以很方便设置 redis 分布式事务锁

java 复制代码
/**
 * 普通缓存放入并设置时间
 *
 * @param key   键
 * @param value 值
 * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
 * @return true成功 false 失败
 * @author c02822
 */
public boolean set(String key, Object value, long time) {
    try {
        if (time > 0) {
            redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
        } else {
            set(key, value);
        }
        return true;
    } catch (Exception e) {
        log.error("except.", e);
        return false;
    }
}

2. 定时任务服务类

需要的定时任务:每天在固定的某一个时间将所有产品,所有环境的所有产品的工作负载状态存储到一个数据库表中。

以上面这个需求为例,首先需要明确的是用于存储数据的数据表。为此你新建两个数据库,建表语句如下:

sql 复制代码
-- 1. 工作负载每日快照表
CREATE TABLE workload_daily_snapshot (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    snapshot_date DATE NOT NULL, -- 快照日期
    namespace VARCHAR(100) NOT NULL,
    workload_name VARCHAR(255) NOT NULL,
    workload_type VARCHAR(50) NOT NULL, -- 'Deployment'/'StatefulSet'
    product VARCHAR(100), -- 产品线
    environment VARCHAR(50), -- 环境
    desired_replicas INT NOT NULL DEFAULT 0,
    available_replicas INT NOT NULL DEFAULT 0,
    image VARCHAR(500),
    cpu_request_m DECIMAL(10,2), -- CPU请求(毫核)
    memory_request_mi INT, -- 内存请求(MiB)
    cpu_limit_m DECIMAL(10,2), -- CPU限制(毫核)
    memory_limit_mi INT, -- 内存限制(MiB)
    created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_date_namespace (snapshot_date, namespace),
    INDEX idx_product_env (product, environment),
    UNIQUE KEY uk_daily_workload (snapshot_date, namespace, workload_name, workload_type)
);

-- 2. Pod每日状态表
CREATE TABLE pod_daily_status (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    snapshot_date DATE NOT NULL,
    namespace VARCHAR(100) NOT NULL,
    pod_name VARCHAR(255) NOT NULL,
    workload_name VARCHAR(255), -- 所属工作负载
    workload_type VARCHAR(50), -- 所属工作负载类型
    status VARCHAR(50) NOT NULL, -- Running/Pending/Failed等
    node_name VARCHAR(255),
    restart_count INT DEFAULT 0,
    product VARCHAR(100),
    environment VARCHAR(50),
    created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_date_status (snapshot_date, status),
    INDEX idx_workload (namespace, workload_name),
    INDEX idx_node (node_name)
);

此外我写了个定时任务的模板

java 复制代码
// ScheduledTaskService.java
@Slf4j

@Service

public class ScheduledTaskService {


    /**
    * redis工具包
    */
    @Autowired
    @Qualifier("redisUtil")
    private RedisUtil redisUtil;


    // 每天凌晨 2 点执行

    @Scheduled(cron = "0 0 2 * * ?")

    public void dailyCleanupTask() {

        String lockKey = "lock:daily_cleanup_task";

        long expireTime = 60 * 10; // 10 分钟,根据任务最大执行时间设定

        if (!redisUtil.hasKey(lockKey)) {
           redisUtil.set(lockKey, 1 , expireTime);
           // 业务逻辑
           。。。
        }
}

3. 配置类(启用定时任务 & Redis)

java 复制代码
// AppConfig.java

package com.example.config;



import org.springframework.context.annotation.Configuration;

import org.springframework.scheduling.annotation.EnableScheduling;



@Configuration

@EnableScheduling

public class AppConfig {

    // 启用 @Scheduled

}

三、注意事项

锁的过期时间:必须大于任务最大可能执行时间,避免任务未完成锁就过期,导致其他节点抢占锁造成并发执行。

锁的 key 命名规范:如 lock:service_name:task_name,便于管理和监控。

异常处理:务必在 finally 块中释放锁,防止因异常导致锁未释放。

避免主从切换下的锁失效:Redis 主从异步复制可能导致锁在主节点加锁后,主挂掉,从节点未同步锁信息就被提升为主,此时另一个客户端可能再次加锁。若对一致性要求极高,应使用 RedLock 算法或改用 ZooKeeper / etcd。但在大多数业务场景中,单 Redis 实例 + 合理过期时间已足够。

四、总结

通过 Redis 分布式锁,我们可以在 Spring Cloud 多实例环境下安全地执行定时任务,确保幂等性和唯一性。本方案轻量、易理解,适用于大多数业务场景。

相关推荐
2401_8955213421 分钟前
【Spring Security系列】Spring Security 过滤器详解与基于JDBC的认证实现
java·后端·spring
小码哥_常1 小时前
大文件上传不再卡顿:Spring Boot 分片上传、断点续传与进度条实现全解析
后端
_Evan_Yao1 小时前
RAG中的“Chunk”艺术:我试过10种切分策略后总结的结论
java·人工智能·后端·python·软件工程
今天你TLE了吗1 小时前
LLM到Agent&RAG——AI概念概述 第二章:提示词
人工智能·笔记·后端·学习
IT_陈寒2 小时前
Vue的响应式把我坑惨了,原来问题出在这
前端·人工智能·后端
shark22222223 小时前
能懂!基于Springboot的用户增删查改(三层设计模式)
spring boot·后端·设计模式
IGAn CTOU3 小时前
王炸级更新!Spring Boot 3.4 正式发布,新特性真香!
java·spring boot·后端
柯西劝我别收敛3 小时前
Koordinator-Scheduler 调度器源码解析
后端·云原生
tycooncool4 小时前
Spring中的IOC详解
java·后端·spring
303784 小时前
消息推送削峰落地方案
后端