问题描述: 有一个定时任务是每周一给客户发送邮件的功能, 后台部署了2台服务器,所以客户 收到了2封重复邮件。
解决思路:
分布式锁一般有三种实现方式:1. 数据库乐观锁;2. 基于Redis的分布式锁;3. 基于ZooKeeper的分布式锁。
这里使用一台Redis服务器来解决上面的问题。
代码部分比较简单:
加锁 :主要是给多个定时任务给redis加锁(key),如果存在key,则加锁失败,如果不存在,则尝试去加锁,返回加锁结果。
解锁: 设置一下过期时间为20秒(可根据任务执行长短调整),过期后自动释放掉。这里就不去代码里面释放锁了。
private static final String LOCK_SUCCESS = "OK"; private static final String SET_IF_NOT_EXIST = "NX"; private static final String SET_WITH_EXPIRE_TIME = "PX"; private static final Long RELEASE_SUCCESS = 1L; /** * 尝试获取分布式锁 * * @param jedis Redis客户端 * @param lockKey 锁 * @param requestId 请求标识 * @param expireTime 超期时间 * @return 是否获取成功 */ public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
//这里的jedis自己去实现一下 String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime); if (LOCK_SUCCESS.equals(result)) { return true; } return false; }
测试 (同时启动4个服务,每个服务上面启动三个同样的定时任务)
//每分钟执行一次 static final String cron1 = "0 */1 * * * ?"; @Scheduled(cron = cron1) public void testRedis1() { String msg = doRedis(); System.out.println("msg1 :" + msg); } @Scheduled(cron = cron1) public void testRedis2() { String msg = doRedis(); System.out.println("msg 2:" + msg); } @Scheduled(cron = cron1) public void testRedis3() { String msg = doRedis(); System.out.println("msg 3:" + msg); } //执行redis private String doRedis() { boolean flag = RedisTool.tryGetDistributedLock("sendMail_20200101", UUID.randomUUID().toString(), 1000 * 20); String msg = null; if (flag) { msg = "你获取到锁了可以去操作业务!"; } else { msg = "别人获取到锁了,你就不用操作了!!!!"; } return msg; }
测试结果
观察到每一分钟, 3X4个定时任务中,只有一个能获取到锁,可以去操作业务,其他同样的定时任务失败了 。
总结:后面会具体总结一下分布式锁相关的内容