`

Redis的事务机制实现定时类工具任务的高可用

阅读更多

        系统中目前有些工具包含了一些定时任务,但是为了保证工具不出现单点故障,在部署多台服务器时,会只在一台服务器上开启定时任务,其他服务器关闭,但是在版本迭代部署过程中,因为配置文件的差异导致容易出现问题,一不小心就导致多台服务器同时开启了定时任务,或者多台服务器都没执行异步任务,说到底,还是因为工具无法做到完全一致。

       说到底,还是因为没有一个机制保证一模一样的工具,无法实现定时类任务只在一台服务器运行。

       后来经过思考,可以借鉴Redis的事物和Watch机制来实现,逻辑很简单:

       1)所有服务器配置一样,每台服务器都可以定时启动任务

       2)但是在任务启动的开始,判断是否已经有当前执行时间点的信息存储至Redis的Set里面去

             比如,任务执行时间串为: 0 */1 * * *  (cron4j)

             那么到了整点的时候,先判断 set中是否存在 yyyyMMddHH

             如果存在,则表示其他实例已经抢先运行了

             否则,使用watch、multi命令来往redis的set中插入数据,

             哪个插入成功,则该实例可以执行具体的任务了,

             插入失败则不执行。

       看代码:

/**
	 * 设置信号量(用于决定当前工具实例是否有资格去执行任务)
	 * @param taskName
	 * @return
	 * @throws Exception 
	 */
	public boolean setRedisSemaphore(){
		try {
			//1、先判断信号量是否存在(信号量的值以  quartz:{taskName}  yyyyMMdd 或者 yyyyMMddHH (由任务对应的执行周期决定) 的形式存储)
			String key = "quartz:" + taskName;
			String format = getFormat();
			if(StringUtils.isBlank(format)){
				return true;
			}else{
				SimpleDateFormat sdf = new SimpleDateFormat(format);
				String value = sdf.format(new Date());
				if(redisService.sismember(RedisName_Biz, key, value)){
					return false;
				}else{
					boolean keyExists = redisService.exists(RedisName_Biz, key);
					//2、如果存在则返回,否则设置信号量,设置成功就继续处理,设置失败,本次任务结束
					Jedis jedis = redisService.getJedisByKey(RedisName_Biz);
					Pipeline p = jedis.pipelined();
					p.watch(key);
					p.multi();
					p.sadd(key, value);
					p.exec();
					List<Object> list = p.syncAndReturnAll();
					String result = list.get(list.size() - 1).toString();
						
					if(!keyExists){
						redisService.expire(RedisName_Biz, key, 7 * 24 * 60 * 60);
					}
						
					redisService.returnJedisByKey(RedisName_Biz, jedis);
						
					return result.equals("[1]");
				}
			}
		} catch (Exception e) {
			logger.info(String.format("任务【%s】设置信号量出现异常:", this.taskName) + e);
		}
		return false;
	}

        

       虽然我们开发了一个工具框架集成了这个机制,但是目前这个方式只能实现工具多实例同时运行的高可用机制,无法保证针对一批数据有多个实例同时去处理(队列性质的除外,因为队列性质的数据源不需要这个机制来保障)。

       之前项目组使用过淘宝的开源框架TBSchedule,但是上线后遇到了各种各样的问题,还导致过现网故障,而由于TbSchedule设计的太过完美,功能比较丰富、复杂,在解决问题的过程中也遇到了不少麻烦,后来干脆直接把TbSchedule给抛弃了。 不过它的思路是非常优秀的,使用分配任务项的方式来决定实例需要处理哪些处理,而且不会造成数据的重复处理,同时增加实例、减少实例,都会及时的进行数据重新分配。

后续会继续研究下它的思路来时间一个精简版的高可用工具框架。

       附:淘宝开源框架有兴趣的话见如下地址:

              http://code.taobao.org/p/tbschedule/wiki/index/

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics