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