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 多实例环境下安全地执行定时任务,确保幂等性和唯一性。本方案轻量、易理解,适用于大多数业务场景。

相关推荐
strayCat232552 小时前
2. Spring Boot 自动配置原理深度解析
java·spring boot·后端
SimonKing2 小时前
每月500 Credits+不限频对话,这款IDEA插件的免费版诚意拉满
java·后端·程序员
我叫黑大帅2 小时前
PHP mysqli 实用开发指南
后端·面试·php
polaris06302 小时前
Spring Boot 集成 MyBatis 全面讲解
spring boot·后端·mybatis
tumeng07112 小时前
Spring Boot与MyBatis
spring boot·后端·mybatis
鬼蛟3 小时前
Spring Boot整合全局异常处理器、junit、多环境、logback
java·spring boot·后端
tkevinjd3 小时前
Redis主从复制
数据库·redis·后端·缓存·面试
小江的记录本3 小时前
【Logback】Logback 日志框架 与 SLF4J绑定、三层模块、MDC链路追踪、异步日志、滚动策略
java·spring boot·后端·spring·log4j·maven·logback
随风,奔跑3 小时前
Spring Boot笔记
java·spring boot·笔记·后端