java基于redis实现分布式定时任务的实现
使用初衷
多个服务,定时任务日常使用中设计到问题
- 数据统计 ,会导致数据重复统计;
- 发送短信 ,重复发送;
- 优惠券,重复发送;
- 目前我举例这几个,欢迎补充
目前主流的xxl-job
1.由于xxl-job需要部署,以及需要数据库,不熟悉的人可能使用会有问题;
2.由于系统设计到金额,数据统计等,xxl-job需要登入后台设置,密码可能会被破解,出于安全考虑问题,
不使用此方法
实现步骤
1. 由于导入redis的jar包简单,省略
2. 切面注解
package com.redis.base.config;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedisLock {
/**
* 锁的有效时间长,单位:秒
*
* @return
*/
int expireTime() default 10;
/**
* 自定义锁的keyName(不用包含namespace,内部已实现)
*/
String keyName() default "";
}
3. 代码实现
@Slf4j
@Aspect
@Component
public class RedisLockAspect {
@Resource
private StringRedisTemplate stringRedisTemplate;
//annotation包后面需要制指定切换。
@Around("execution(* *.*(..)) && @annotation(com.redis.base.config.RedisTryLock)")
public void redisTryLockPoint(ProceedingJoinPoint pjp) {
String defKey = "redis:lock:";
RedisTryLock annotation;
Method method = null;
//获得所在切点的该类的class对象
Class<?> aClass = pjp.getTarget().getClass();
//获取该切点所在方法的名称,每一个使用切面的方法
String name = pjp.getSignature().getName();
try {
//通过反射获得该方法
method = aClass.getMethod(name);
//获得该注解
annotation = method.getAnnotation(RedisTryLock.class);
//获取注解对应的值keyName,expireTime
String keyName = annotation.keyName();
int expireTime = annotation.expireTime();
String ip =127.0.0.1; //根据实际情况获取ip地址
// 设置redis键值
defKey = StringUtils.isBlank(keyName) ? aClass.getName() + defKey + aClass.getDeclaringClass() + method.getName() : aClass.getName() + defKey + keyName;
//根据redis 锁的原理判断是否执行成功,设值成功说明其他服务器没有执行定时任务,反则正在执行
String v = stringRedisTemplate.opsForValue().get(RedisKey.TASK_INSTANCE_PREFIX + ip); //如果将此值改成0则任务不执行
boolean equals = "1".equals(v) && Boolean.TRUE.equals(stringRedisTemplate.opsForValue().setIfAbsent(defKey, ip, expireTime, TimeUnit.SECONDS));
log.info("任务:{},是否可执行:{}", defKey,equals);
if (equals) {
pjp.proceed();
stringRedisTemplate.delete(defKey);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
log.error("Facet aop failed error {}", e.getLocalizedMessage());
stringRedisTemplate.delete(defKey);
} catch (Throwable throwable) {
throwable.printStackTrace();
if (method != null) {
log.info("方法名{},执行失败,错误信息", method.getName(), throwable);
} else {
log.info("方法名,执行失败,错误信息", throwable);
}
stringRedisTemplate.delete(defKey);
}
}
}
4. 在方法执行示例
@Component
@Slf4j
public class Test{
@Scheduled(fixedDelay = 100)
@RedisLock(keyName = "test")
//keyName的键值可以不写,默认读取方法名。
public void test() {
//方法实现
}
}