問題描述: 有一個定時任務是每周一給客戶發送郵件的功能, 后台部署了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個定時任務中,只有一個能獲取到鎖,可以去操作業務,其他同樣的定時任務失敗了 。
總結:后面會具體總結一下分布式鎖相關的內容