使用redis分布式锁解决spring schedule集群部署重复执行问题

产品需求:

对接腾讯云实时音视频,实时拉取腾讯云最新的视频并使用Ffmpeg视频合成。

遇到问题:

由于服务都是集群部署,定时任务会在各个服务器上重复执行,轻则浪费服务器资源,重则造成数据紊乱。

解决方案:

quartz定时任务提供了集群方式,但还需为其创建数据库表。spring schedule可以实现简单轻量级的定时任务,但无法保证只执行一次。
通过和架构师探讨得知,可以使用redis的分布式锁保证spring schedule集群只执行一次。
redis分布式锁是通过setnx命令实现的。该命令的作用是,当往redis中存入一个值时,会先判断该值对应的key是否存在,如果存在则返回0,如果不存在,则将该值存入redis并返回1。根据这个特性,我们在程序中,每次都调用setIfAbsent(该方法是setnx命令的实现)方法,来模拟是否获取到锁,如果返回true,则说明该key值不存在,表示获取到锁;如果返回false,则说明该key值存在,已经有程序在使用这个key值了,从而实现了类似加锁的功能。

@Component
@Configuration
@EnableScheduling
public class AutoConvertTask {
    private static final Logger logger = LoggerFactory.getLogger(AutoConvertTask.class);

    @Autowired
    private RedisTemplate redisTemplate;

    private static final String LOCK = "task-job-lock";

    private static final String KEY = "tasklock";

    @Scheduled(cron = "0 0 0 * * ? ")
    public void autoConvertJob() {
        boolean lock = false;
        try {
            lock = redisTemplate.opsForValue().setIfAbsent(KEY, LOCK);
            logger.info("是否获取到锁:" + lock);
            if (lock) {
                List<GameHistory> historyList = historyService.findTenDaysAgoUntreated();
                for (GameHistory history : historyList) {
                    update(history);
                }
            } else {
                logger.info("没有获取到锁,不执行任务!");
                return;
            }
        } finally {
            if (lock) {
                redisTemplate.delete(KEY);
                logger.info("任务结束,释放锁!");
            } else {
                logger.info("没有获取到锁,无需释放锁!");
            }
        }

    }

}

这就是使用Redis的解决方案,Github上面还有好多使用redis封装成框架的解决方案哦。

上一篇:Python编码/文件读取/多线程


下一篇:使用python发生此类错误 ValueError: endog must be in the unit interval.