纯黑子 2024-04-13 18:25 采纳率: 67.6%
浏览 7

生产环境定时任务轮询执行问题

生产环境有多台服务器,注册中心使用eureka,springboot项目中使用@Scheduled注解实现定时,现在每一台都会跑一次定时,如何让多台服务器上的定时任务轮询执行

  • 写回答

7条回答 默认 最新

  • 专家-赤兔[在线] 全栈领域优质创作者 2024-04-13 18:25
    关注

    引自免费微信小程序:皆我百晓生

    要解决在分布式环境中确保只有一个实例执行定时任务的问题,可以采用 leader election(领导选举)策略。在这种情况下,我们可以使用一个轻量级的分布式协调服务,例如 ZooKeeper 或者 Redis 的 String 事务操作。这里我将提供一个基于 Redis 的解决方案。

    首先,你需要在应用启动时,通过 Redis 进行 leader election,确保只有一个节点成为 leader 并执行定时任务。以下是步骤:

    1. 引入 Redis 相关依赖:
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
    1. 配置 Redis 连接: 在 application.properties 文件中添加 Redis 配置:
    spring.redis.host=your_redis_host
    spring.redis.port=your_redis_port
    
    1. 创建一个用于 leader election 的工具类 LeaderElectionService
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.connection.RedisConnection;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Service;
    
    @Service
    public class LeaderElectionService {
    
        private static final String LEADER_KEY = "distributed-task-leader";
    
        @Autowired
        private RedisTemplate<String, String> redisTemplate;
    
        public boolean electLeader(String serverId) {
            try (RedisConnection connection = redisTemplate.getConnectionFactory().getConnection()) {
                // 使用 Redis 的 SETNX 命令设置键值对,只有当 key 不存在时,才会设置成功,并返回 true。
                // 如果多个实例同时尝试获取锁,只会有一个返回 true,其他返回 false。
                return connection.setnx(LEADER_KEY, serverId).booleanValue();
            }
        }
    
        public void releaseLeadership() {
            redisTemplate.delete(LEADER_KEY);
        }
    }
    
    1. 在需要执行定时任务的服务类中注入 LeaderElectionService,并在方法上添加 @PostConstruct 注解确保在定时任务执行之前进行 leader election:
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Component;
    
    @Component
    public class DistributedTaskService {
    
        private static final long EXECUTION_INTERVAL_SECONDS = 60; // 定期执行间隔时间,单位:秒
    
        @Autowired
        private LeaderElectionService leaderElectionService;
    
        @Scheduled(fixedRate = EXECUTION_INTERVAL_SECONDS * 1000)
        @PostConstruct
        public void executeDistributedTask() {
            String serverId = "server-" + UUID.randomUUID(); // 生成唯一标识符,可以用 IP 地址或其他方式替代
            if (leaderElectionService.electLeader(serverId)) {
                System.out.println("Server " + serverId + " is the leader, executing task...");
    
                // 执行你的定时任务逻辑
                // ...
    
                leaderElectionService.releaseLeadership();
            } else {
                System.out.println("Server " + serverId + " is not the leader, skipping task execution.");
            }
        }
    }
    

    这样,在多台服务器上运行此应用时,只有被选为 leader 的服务器会执行定时任务。每次服务器重启时,都会重新竞争 leader 身份,从而确保任务始终由一台服务器执行。注意,这个方案依赖于 Redis 的可用性,如果 Redis 出现故障,可能会影响 leader election 的结果。

    评论 编辑记录

报告相同问题?

问题事件

  • 修改了问题 4月15日
  • 创建了问题 4月13日

悬赏问题

  • ¥30 不会,学习,有偿解答
  • ¥15 SQL查询语句报错(检查)
  • ¥15 此表中公式应该怎么写
  • ¥15 求HI-TECH PICC 9.50 PL3安装包
  • ¥15 下载ctorch报错,求解
  • ¥15 如何入门学习c语言,单片机
  • ¥15 idea 编辑语言的选择
  • ¥15 Windows下部署Asmjit
  • ¥15 请问双层规划模型的上下层目标函数不一致,是如何保证迭代收敛性的
  • ¥30 微信小程序 前端页面内容搜索