Spring Scheduled + Redis 实现分布式定时器(一)
来源:互联网 发布:淘宝水晶店哪家是真的 编辑:程序博客网 时间:2024/06/06 14:14
1、需要了解的技术点:
1.1、Redis的命令:SETNX,EXPIRE;
1.2、Spring的Scheduled定时器注解,触发器,任务,调度器;
1.3、Spring的applicationContext上下文对象,自定义注解,java反射机制;
2、思路:
2.1、创建一个自定义注解,参数:cron(时间格式);
2.2、创建一个@Component组件,用来实现自定义注解的功能,
2.2.1、实现ApplicationContextAware接口,用来获取spring的ApplicationContext上下文对象;
2.2.2、实现BeanPostProcessor接口,用来获取自定义注解所对应的方法;
2.2.3、实现SchedulingConfigurer接口,用来创建定时器任务;
2.2.4、创建一个实现Runabel接口的类,用来反射自定义注解所对应的方法和抢占redis的锁;
2.2.5、创建一个实现Trigger接口的触发器对象,用来获取下一次执行任务的时间,以便给redis设置锁的生存时间;
2.3、程序执行流程:
2.3.1、给需要加定时器的方法加上自定义注解
2.3.2、程序启动,获取spring上下文对象;
2.3.3、扫描自定义注解所对应的方法;
2.3.4、根据每个自定义注解的信息创建对应触发器和任务;
2.3.5、调度器触发任务时,先去抢占锁,再根据情况判断本实例是否要执行任务;
3、代码分解:
3.1、创建自定义注解;
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic @interface KyScheduled { /** * A cron-like expression, extending the usual UN*X definition to include * triggers on the second as well as minute, hour, day of month, month * and day of week. e.g. {@code "0 * * * * MON-FRI"} means once per minute on * weekdays (at the top of the minute - the 0th second). * @return an expression that can be parsed to a cron schedule */ String cron() default "";}3.2、创建KyTask类,用来记录自定义注解的信息和注解对应方法的信息;
public class KyScheduledExecution {public class KyTask { private KyScheduled kyScheduled; private Method kyMethod; public KyScheduled getKyScheduled() { return kyScheduled; } public void setKyScheduled(KyScheduled kyScheduled) { this.kyScheduled = kyScheduled; } public Method getKyMethod() { return kyMethod; } public void setKyMethod(Method kyMethod) { this.kyMethod = kyMethod; } }}
3.3、实现ApplicationContextAware接口,获取spring上下文对象;
原因:如果单纯使用java的反射机制,当定时器任务使用@Autowired注解时,会获取不到bean实例,所以要实现ApplicationContextAware接口;
@Componentpublic class KyScheduledExecution implements ApplicationContextAware { private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext context) throws BeansException { this.applicationContext = context; } private Object getBean(Class classname) { try { return this.applicationContext.getBean(classname); } catch (Exception e) { log.error(e); return ""; } }}
3.4、实现BeanPostProcessor接口,获取自定义注解信息;
@Componentpublic class KyScheduledExecution implements BeanPostProcessor { private Log log = LogFactory.getLog(getClass()); //记录任务集合 private List<KyTask> kyTaskList = new ArrayList<>(); @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } /** * 获取所有自定义注解,并记录注解和方法的信息 * @param bean bean * @param beanName beanName * @return Object * @throws BeansException BeansException */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { Method[] methods = ReflectionUtils.getAllDeclaredMethods(bean.getClass()); if (methods != null) { for (Method method : methods) { KyScheduled annotation = AnnotationUtils.findAnnotation(method, KyScheduled.class); if (annotation != null && !"".equals(annotation.cron())) { KyTask at = new KyTask(); at.setKyScheduled(annotation); at.setKyMethod(method); kyTaskList.add(at); } } } return bean; }}3.5、在KyScheduledExecution 在类中引入redis客户端,并实现获取redis锁的方法, 需要在3.3的setApplicationContext方法中执行createRedisClient();
public class KyScheduledExecution{ private Log log = LogFactory.getLog(getClass()); @Value("${spring.redis.host}") private String redisHost; @Value("${spring.redis.port}") private int redisPort; private Jedis jedis; //记录任务集合 private List<KyTask> kyTaskList = new ArrayList<>(); private ApplicationContext applicationContext; /** * 创建redis客户端 */ private void createRedisClient() { if (jedis == null) { jedis = new Jedis(redisHost, redisPort); } } /** * 获取分布式锁 * * @param lockName 锁名称 * @param second 加锁时间(秒) * @return 如果获取到锁,则返回lockId值,否则为null */ private String setnxLock(String lockName, int second) { synchronized (this) { //生成随机的Value值 String lockId = UUID.randomUUID().toString(); //抢占锁 Long lock = this.jedis.setnx(lockName, lockId); if (lock == 1) { //拿到Lock,设置超时时间 this.jedis.expire(lockName, second - 1); return lockId; } } return null; }}3.5、创建自定义的触发器对象,实现Trigger接口nextExecutionTime方法;
public class KyTrigger implements Trigger, Serializable { private String cron; private boolean syncLock; public KyTrigger(KyScheduled kyScheduled){ if(kyScheduled.cron() != null && !"".equals(kyScheduled.cron())) { this.cron = kyScheduled.cron(); } this.syncLock = kyScheduled.synclock(); } public boolean getSyncLock(){ return this.syncLock; } public String getCron() { return cron; } public void setCron(String cron) { if(cron != null && !"".equals(cron)) { this.cron = cron; } } @Override public Date nextExecutionTime(TriggerContext triggerContext) { CronTrigger cronTrigger = new CronTrigger(this.cron); return cronTrigger.nextExecutionTime(triggerContext); }}3.6、在KyScheduledExecution类中创建一个实现Runnable接口的自定义的JOB内部类,需要接收Method和自定义的trigger对象;
public class KyScheduledExecution{ private Log log = LogFactory.getLog(getClass()); /** * 任务对象 */ public class Job implements Runnable { private Method method; private String lockName; private Object invokeMethod; private Trigger trigger; public String getLockName() { return lockName; } Job(Method m, Trigger t) { this.trigger = t; this.invokeMethod = getBean(m.getDeclaringClass());//获取bean实例 this.lockName = m.getDeclaringClass().getName() + "." + m.getName();//构造LockName this.method = m; } @Override public void run() { //获取下次执行时间(秒) long nextTime = (this.trigger.nextExecutionTime(new SimpleTriggerContext()).getTime() - new Date().getTime()) / 1000; //抢占分布式锁 String result = setnxLock(this.lockName, (int) nextTime); if (result != null && !"".equals(result)) { try { //执行自定义注解的方法 this.method.invoke(this.invokeMethod); } catch (IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); log.error(e); } } } }}
3.7、在KyScheduledExecution类中实现SchedulingConfigurer接口;
@Componentpublic class KyScheduledExecution implements SchedulingConfigurer{ private Log log = LogFactory.getLog(getClass()); /** * 配置定时器 * * @param taskRegistrar ScheduledTaskRegistrar */ @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { if (taskRegistrar != null) { for (KyTask kt : kyTaskList) { Method method = kt.getKyMethod(); //创建触发器 KyTrigger trigger = new KyTrigger(kt.getKyScheduled()); //创建任务 Job job = new Job(method, trigger); //将任务加入调度器中 taskRegistrar.addTriggerTask(job, trigger); } } }}
代码分解完毕,以上为所有代码!!!
阅读全文
0 0
- Spring Scheduled + Redis 实现分布式定时器(一)
- Spring Scheduled + Redis 实现分布式定时器(二)
- spring定时器(@Scheduled)
- Spring @Scheduled 注解实现定时器调度配置
- 组件-------(一)redis系列--安装部署redis+实现redis分布式缓存 java+Spring+redis
- Spring注解定时器@Scheduled
- spring @Scheduled 定时器
- 利用redis(spring-data-redis)锁的功能来实现定时器的分布式
- spring mvc 任务定时器 @Scheduled
- Spring定时器技术终结者——采用Scheduled注释的方式实现Spring定时器
- spring自带的定时器@Scheduled
- spring自带定时器@Scheduled注解配置
- Spring任务调度<task:scheduled-tasks>(定时器)
- spring 基于注解的@Scheduled和quartz定时器两种实现
- spring scheduled annotation 实例一
- 安装部署redis+实现redis分布式缓存 java+Spring+redis
- Spring Scheduled实现定时任务
- 搭建redis集群实现分布式缓存(一)
- 读写文件时00 0A与80 0A的bug
- MyBatis + maven org.apache.ibatis.binding.BindingException: Invalid bound statement (not found):
- CSS 的优先级机制[总结]
- 判断三点顺序(顺时针或者逆时针)(模板)
- poj3461——Oulipo(kmp)
- Spring Scheduled + Redis 实现分布式定时器(一)
- 自定义View系列目录索引
- Redis应用场景
- [拓扑序] Educational Codeforces Round 25 825E. Minimal Labels
- Java中serialVersionUID的作用
- 统计学习方法-朴素贝叶斯法(基于R的实现)
- centos6.9-vsftpd安装配置
- sizeof strlen wcslen tcslen 比较
- Shell编程循环结构中continue和break的用法