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

释放双眼,带上耳机,听听看~!

产品需求:要求定时扫描超过10天,对抓中奖品进行处理的游戏记录,系统自动转换成娃娃币,同时在消费明细和消息中心提醒用户。
由于服务都是集群部署,定时任务会在各个服务器上重复执行,轻则浪费服务器资源,重则造成数据紊乱。
quartz定时任务提供了集群方式,但还需为其创建数据库表。spring schedule可以实现简单轻量级的定时任务,但无法保证只执行一次。
通过和架构师探讨得知,可以使用redis的分布式锁保证spring schedule集群只执行一次。
redis分布式锁是通过setnx命令实现的。该命令的作用是,当往redis中存入一个值时,会先判断该值对应的key是否存在,如果存在则返回0,如果不存在,则将该值存入redis并返回1。根据这个特性,我们在程序中,每次都调用setIfAbsent(该方法是setnx命令的实现)方法,来模拟是否获取到锁,如果返回true,则说明该key值不存在,表示获取到锁;如果返回false,则说明该key值存在,已经有程序在使用这个key值了,从而实现了类似加锁的功能。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
1@Component
2@Configuration
3@EnableScheduling
4public class AutoConvertTask {
5    private static final Logger logger = LoggerFactory.getLogger(AutoConvertTask.class);
6
7    @Autowired
8    private RedisTemplate redisTemplate;
9
10    private static final String LOCK = "task-job-lock";
11
12    private static final String KEY = "tasklock";
13
14    @Scheduled(cron = "0 0 0 * * ? ")
15    public void autoConvertJob() {
16        boolean lock = false;
17        try {
18            lock = redisTemplate.opsForValue().setIfAbsent(KEY, LOCK);
19            logger.info("是否获取到锁:" + lock);
20            if (lock) {
21                List<GameHistory> historyList = historyService.findTenDaysAgoUntreated();
22                for (GameHistory history : historyList) {
23                    update(history);
24                }
25            } else {
26                logger.info("没有获取到锁,不执行任务!");
27                return;
28            }
29        } finally {
30            if (lock) {
31                redisTemplate.delete(KEY);
32                logger.info("任务结束,释放锁!");
33            } else {
34                logger.info("没有获取到锁,无需释放锁!");
35            }
36        }
37
38    }
39
40}
41
42

给TA打赏
共{{data.count}}人
人已打赏
安全网络

CDN安全市场到2022年价值76.3亿美元

2018-2-1 18:02:50

安全漏洞

GitLab 不受 Rails 安全漏洞 CVE-2014-3483 影响

2014-7-5 11:12:22

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索