使用redis分佈式鎖+lua腳本實現分佈式定時任務控制demo

分佈式系統常常要遇到定時任務執行的問題,不能重複執行,但不少時候又不能統一到一個微服務裏面,由於這樣就失去了微服務的意義。因爲個人系統只有寥寥幾個定時任務,並且都是按天執行的,我就弄了這麼個小東西來控制分佈式定時任務。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

相關文章
相關標籤/搜索