分佈式系統常常要遇到定時任務執行的問題,不能重複執行,但不少時候又不能統一到一個微服務裏面,由於這樣就失去了微服務的意義。因爲個人系統只有寥寥幾個定時任務,並且都是按天執行的,我就弄了這麼個小東西來控制分佈式定時任務。redis
我使用的redis分佈式鎖來控制分佈式定時任務的方式,實際上適用於定時任務較少的狀況,並且不適用於瞬時反覆執行的定時任務。這種狀況下,若是加上分佈式定時任務框架,如Elastic-Job這種,顯然就很重了,因此這是一個極輕量級的方式。這種方式顯然很難支持做業分片、失效轉移、從新觸發執行以及執行過程當中服務掛掉以後的從新執行。因此使用這種方式要慎重,若是系統存在後續定時任務大規模擴展、定時任務須要分片或者有瞬時反覆執行的定時任務等狀況,則這種簡單的方式就不適用了。框架
原理其實很簡單,各個微服務系統同時向redis申請加鎖,因爲redis是單線程的,因此只能有一個加鎖成功,而後後面的所有得不到鎖。獲得鎖的執行定時任務,得不到的,就放棄執行定時任務。分佈式
爲啥要用lua腳本呢?由於好用啊,親,操做的原子性能獲得保證。ide
小demo:微服務
@Test public void testgg(){ try{ jedisCluster.del("schedule:lock:sss"); String dateStr = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); for (int i=0;i<30;i++){ Thread thread = new Thread(new Runnable() { @Override public void run() { //lua腳本 String luaScript = "local vals = redis.call('setnx', KEYS[1],'lock') if vals > 0 then redis.call('expire',KEYS[1], 1) end if vals > 0 then return 1 end return 0"; //執行lua腳本 Object lockVal = jedisOperationUtils.executeLuaScript(luaScript,1, "schedule:lock:sss"); System.out.println(lockVal.toString()+" "+Thread.currentThread().getName()); if(lockVal!=null && Integer.valueOf(lockVal.toString())>0){ //獲得鎖,執行定時任務的內容 } } }); thread.start(); Thread.currentThread().sleep(100); } try{ Thread.currentThread().sleep(10000); }catch (Exception e){ System.out.println("ooooooooooooooooooooooooooooo"); e.printStackTrace(); } }catch (Exception e){ System.out.println("IIIIIIIIIIIIIII"); e.printStackTrace(); } }
這個例子就大概表達了整個思路的意思。其實至關簡單。性能
裏面的lua
jedisOperationUtils.executeLuaScript(......)
方法是封裝的,我不須要ARGV[],因此就沒封裝進去。線程
/** * lua腳本執行 * @param luaScript * @param keyCount * @param keys * @return */ public Object executeLuaScript(String luaScript,int keyCount,String ... keys){ Object object = null; try{ object = jedisCluster.eval(luaScript,keyCount,keys); if(object == null){ return null; } }catch(Exception e){ log.error("執行redislua腳本失敗",e); return null; } return object; }
OVERorm