分布式任务调度简化

首先,我也没有做过什么分布式的任务调度框架,我只是这几天做定时任务,为了解决高可用的特性,弄了一个简化版的分布式任务调度。其实分布式任务调度很简单,说白了在单机上做的话,就需要利用lock或者锁了。而多服务器了之后,就需要提供一个分布式锁即可,但是这样会在每个方法里面去显式地去写出一段代码,例如我使用redis来实现的话。

boolean haveGotLock = redisUtil.set(key,value,nx,ex,timeout);
if(!haveGotLock)
    return;

这种,其实是重复的工作,因此直接考虑aop来做。

先放一段我想达到的效果

@DistributedSchedule(lockKey="yourJobKey")
public void yourJob(Object args..){}

这样的形式就可以了。

1.首先得有注解才行

/**
 * 分布式 调度的注解
 *
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DistributedSchedule {
	/**
	 * key的过期时间 不写的话,默认20秒过期
	 * @return
	 */
	long timeout() default 20;
	/**
	 * key的名字必填
	 */
	String lockKey();
	/**
	 * value 非必填
	 */
	String lockValue() default "";
}

2.那么既然写了注解,就得有相应的aop才行

/**
 * 分布式任务调度 
 * @author limaojie
 *
 */
@Aspect
public class DistributedScheduleAspect {
    //依赖 org.slf4j.Logger
	private static final Logger logger = LoggerFactory.getLogger(DistributedScheduleAspect.class);
	/**切入点*/
	@Pointcut("@annotation(com.util.annotation.DistributedSchedule)")
	public void distributedScheduleAspectPointcut() {};
	/**这是必须要的,是分布式锁的定义超类*/
	@Autowired
	private ISchduleLock schduleLock;
	
	/**	
	 * 切入
	 * @throws Throwable 
	 */
	@Around("distributedScheduleAspectPointcut()")
	public Object methodsAnnotatedWithDistributedSchedule(ProceedingJoinPoint joinPoint) throws Throwable {
        beforeTryLock();
        if(null == schduleLock)
            throw new DistributedScheduleFailException("you need check your         schduleLock,because the schduleLock is null");
        boolean isLocked=false;
        try {
            //通过反射去获取到该方法注解的属性值
            DistributedSchedule schedule =((MethodSignature)joinPoint.getSignature()).getMethod().getAnnotation(DistributedSchedule.class);
            String lockKey = schedule.lockKey();
            long timeout = schedule.timeout();
            String lockValue = schedule.lockValue();
            //当value不设置的时候就随机生成一个uuid
            //其实value弄成uuid都有点浪费了,完全可以设成0
            if(null == lockValue || "".equals(lockValue))
            lockValue=UUID.randomUUID().toString();
            //当获取分布式锁成功的时候
            //isLocked就应该是true
            isLocked = schduleLock.tryLock(lockKey, timeout, lockValue);
            }catch (Exception e) {
                logger.error("your schduleLock had happened some error:{}",e);
                //throw e 我想的是,可以再做一个捕获异常
                //当依赖的分布式锁崩溃了,这个时候可以进行一些系统提醒,例如发日志等
                throw e;
                //这里为什么不绕过,是因为,许多定时任务并不允许在一个时间点多次发生
            }
            //成功就继续调用方法
            if(isLocked) {
                Object result = joinPoint.proceed();
                //后置方法
                afterJobDone();
                return result;
            } else
            return null;
    }

    protected void beforeTryLock() {
        //if you need, you can overload
    }
    
    protected void afterJobDone() {
        //if you need, you can overload
    }
}

3.注意一下ISchduleLock这个接口

/**
 * 分布式锁的定义类
 *
 */
public interface ISchduleLock {

	/**
	 * 锁住
	 */
	boolean tryLock(String key,long timeout,String value);
	/**
	 * 释放锁
	 */
	boolean releaseLock(String key);
}

其实上面,我只用到了分布式锁的方法,而没有使用到释放锁的操作

那么如果你想使用的话,就可以写一个类,不管是redis还是zookeeper等等都可以,然后实现我这个接口,重写这两个方法即可。然后注入给spring容器即可。

最后我在把我的configuration贴出来

@Configuration
//这个注解是为了考虑到用户继承了distributedScheduleAspect 实现了一些功能而放弃默认的
@ConditionalOnMissingBean(name= {"distributedScheduleAspect"})
public class DistributedScheduledConfig {
    
    //因为DistributedSecheduleAspect必须要依赖一个分布式锁的实现,因此写了一个ConditionalOnBean
    @Bean
    @ConditionalOnBean(name="schduleLock")
    public DistributedScheduleAspect distributedScheduleAspect() {
        return new DistributedScheduleAspect();
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值