基於redis的spring task集群配置
阿新 • • 發佈:2018-04-30
ron runt parseint 工作空間 runtime point ava 自定義 ips
項目從單節點環境變為集群環境,這個時候要確保項目中的定時任務同一時刻只能在集群中的其中一個服務器中運行,但又不能寫死在哪一臺服務器上運行,怎樣才能實現這樣的需求?
思路:
可以做一個切面,掃描定時任務,在任務開始之前使用redis緩存判斷任務是否啟動,由於Redis是單線程按順序執行命令,可以在每個定時任務執行前,使用setnx方法判斷是否可以添加值,如果添加成功,說明這個定時任務沒有被執行,設置key的過期時間並讓定時任務執行,如果不可以添加值,說明該定時任務已經在其他服務器上跑了,方法之間返回。Key的過期時間要大於集群中方法執行setnx的總和,不大於定時任務的時間間隔。
1.定義一個自定義註解,用於配置定時任務的key,到期時間,存儲庫等信息
package com.study.task; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documentedpublic @interface TaskRedisConfig { /**redis存儲對應的key*/ String key() default ""; /**key對應的value值*/ String value() default "1"; /**選用第一個redis庫存儲該key*/ String index() default "0"; /**該key的過期時間*/ String timeout() default "30"; /**是否允許多節點並發運行 */ boolean isConcurrent() defaultfalse; }
2.定義一個切面,掃描定時任務,在定時任務執行之前用redis的setnx做判斷
package com.study.aop; import java.lang.reflect.Method; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.stereotype.Component; import com.study.task.TaskRedisConfig; import com.study.util.RedisUtil; import redis.clients.jedis.Jedis; @Aspect @Component @EnableAspectJAutoProxy public class TaskAop { @Pointcut("@annotation(org.springframework.scheduling.annotation.Scheduled)") public void pointcut(){} /*@Before("pointcut()") public void taskBefore(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); System.out.println(methodName); Method method = (Method)joinPoint.getThis(); //Method method = (Method)joinPoint.getTarget(); if(method.isAnnotationPresent(TaskRedisConfig.class)) { System.out.println("aaaaaaaaaa---------"); } }*/ @Around("pointcut()") public void taskBefore(ProceedingJoinPoint joinPoint) throws Throwable { String methodName = joinPoint.getSignature().getName(); System.out.println(methodName); MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature(); Method method = methodSignature.getMethod(); if(method.isAnnotationPresent(TaskRedisConfig.class)) { TaskRedisConfig tc = method.getAnnotation(TaskRedisConfig.class); String index = tc.index(); Jedis jedis = RedisUtil.getJedis(); jedis.select(Integer.parseInt(index)); String key = tc.key(); String value = tc.value(); String timeout = tc.timeout(); boolean isConcurrent = tc.isConcurrent(); if(!isConcurrent) { //設置成功,返回 1 。 設置失敗,返回 0 。 long result = jedis.setnx(key, value); if(result==1) { long exresult = jedis.expire(key, Integer.parseInt(timeout)); joinPoint.proceed(); jedis.close(); }else { return; } } } } }
3.寫一個定時任務類,用於測試
package com.study.task; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class SimpleTask { @Scheduled(cron = "0/10 * * * * ?") @TaskRedisConfig(key="simpleTask:methodOne",timeout="9",index="2") @Transactional public void methodOne() { System.out.println("methodOne----,時間:" + System.currentTimeMillis()/1000); } @Scheduled(cron = "0/20 * * * * ?") @TaskRedisConfig(key="simpleTask:methodTwo",timeout="18",index="2") public void methodTwo() { System.out.println("methodTwo----,時間:" + System.currentTimeMillis()/1000); } }
可以向項目放到兩個工作空間下,用兩個eclipse進行分別啟動該兩個項目,查看結果
代碼git地址:https://gitee.com/sjcq/redis.git
基於redis的spring task集群配置