使用redis分布式锁+lua脚本实现分布式定时任务控制demo

耗尽温柔 提交于 2019-12-06 23:52:09

分布式系统经常要遇到定时任务执行的问题,不能重复执行,但很多时候又不能统一到一个微服务里面,因为这样就失去了微服务的意义。由于我的系统只有寥寥几个定时任务,而且都是按天执行的,我就弄了这么个小东西来控制分布式定时任务。

我使用的redis分布式锁来控制分布式定时任务的方式,实际上适用于定时任务较少的情况,而且不适用于瞬时反复执行的定时任务。这种情况下,如果加上分布式定时任务框架,如Elastic-Job这种,显然就很重了,所以这是一个极轻量级的方式。这种方式显然很难支持作业分片、失效转移、重新触发执行以及执行过程中服务挂掉之后的重新执行。所以使用这种方式要慎重,如果系统存在后续定时任务大规模扩展、定时任务需要分片或者有瞬时反复执行的定时任务等情况,则这种简单的方式就不适用了。

原理其实很简单,各个微服务系统同时向redis申请加锁,由于redis是单线程的,所以只能有一个加锁成功,然后后面的全部得不到锁。得到锁的执行定时任务,得不到的,就放弃执行定时任务。

为啥要用lua脚本呢?因为好用啊,亲,操作的原子性能得到保证。

小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();
   }
}

这个例子就大概表达了整个思路的意思。其实相当简单。

里面的

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;
}

OVER

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!