需求:在jeecg-boot原有的定时任务中,实现分布式定时任务。
目标:
思路:使用quartz,jeecg本来就集成好了的。使用Redis,分布式锁,保证事务的一致性。使用Aop,目标方法执行前,执行后进行事务校验。

第一步:自定义注解,定义与Redis连接条件

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface RedisLock {/*** 业务键** @return*/String key();/*** 锁的过期秒数,默认是5秒** @return*/int expire() default 5;/*** 尝试加锁,最多等待时间** @return*/long waitTime() default Long.MIN_VALUE;/*** 锁的超时时间单位** @return*/TimeUnit timeUnit() default TimeUnit.SECONDS;
}

第二步:定义Redis工具类

@Component
public class RedisUtil {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;
//  @Autowired
//  private StringRedisTemplate stringRedisTemplate;/*** 指定缓存失效时间** @param key  键* @param time 时间(秒)* @return*/public boolean expire(String key, long time) {try {if (time > 0) {redisTemplate.expire(key, time, TimeUnit.SECONDS);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 根据key 获取过期时间** @param key 键 不能为null* @return 时间(秒) 返回0代表为永久有效*/public long getExpire(String key) {return redisTemplate.getExpire(key, TimeUnit.SECONDS);}/*** 判断key是否存在** @param key 键* @return true 存在 false不存在*/public boolean hasKey(String key) {try {return redisTemplate.hasKey(key);} catch (Exception e) {e.printStackTrace();return false;}}/*** 删除缓存** @param key 可以传一个值 或多个*/@SuppressWarnings("unchecked")public void del(String... key) {if (key != null && key.length > 0) {if (key.length == 1) {redisTemplate.delete(key[0]);} else {redisTemplate.delete(CollectionUtils.arrayToList(key));}}}// ============================String=============================/*** 普通缓存获取** @param key 键* @return 值*/public Object get(String key) {return key == null ? null : redisTemplate.opsForValue().get(key);}/*** 普通缓存放入** @param key   键* @param value 值* @return true成功 false失败*/public boolean set(String key, Object value) {try {redisTemplate.opsForValue().set(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 普通缓存放入并设置时间** @param key   键* @param value 值* @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期* @return true成功 false 失败*/public boolean set(String key, Object value, long time) {try {if (time > 0) {redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);} else {set(key, value);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 递增** @param key 键* @param by  要增加几(大于0)* @return*/public long incr(String key, long delta) {if (delta < 0) {throw new RuntimeException("递增因子必须大于0");}return redisTemplate.opsForValue().increment(key, delta);}/*** 递减** @param key 键* @param by  要减少几(小于0)* @return*/public long decr(String key, long delta) {if (delta < 0) {throw new RuntimeException("递减因子必须大于0");}return redisTemplate.opsForValue().increment(key, -delta);}// ================================Map=================================/*** HashGet** @param key  键 不能为null* @param item 项 不能为null* @return 值*/public Object hget(String key, String item) {return redisTemplate.opsForHash().get(key, item);}/*** 获取hashKey对应的所有键值** @param key 键* @return 对应的多个键值*/public Map<Object, Object> hmget(String key) {return redisTemplate.opsForHash().entries(key);}/*** HashSet** @param key 键* @param map 对应多个键值* @return true 成功 false 失败*/public boolean hmset(String key, Map<String, Object> map) {try {redisTemplate.opsForHash().putAll(key, map);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** HashSet 并设置时间** @param key  键* @param map  对应多个键值* @param time 时间(秒)* @return true成功 false失败*/public boolean hmset(String key, Map<String, Object> map, long time) {try {redisTemplate.opsForHash().putAll(key, map);if (time > 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 向一张hash表中放入数据,如果不存在将创建** @param key   键* @param item  项* @param value 值* @return true 成功 false失败*/public boolean hset(String key, String item, Object value) {try {redisTemplate.opsForHash().put(key, item, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 向一张hash表中放入数据,如果不存在将创建** @param key   键* @param item  项* @param value 值* @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间* @return true 成功 false失败*/public boolean hset(String key, String item, Object value, long time) {try {redisTemplate.opsForHash().put(key, item, value);if (time > 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 删除hash表中的值** @param key  键 不能为null* @param item 项 可以使多个 不能为null*/public void hdel(String key, Object... item) {redisTemplate.opsForHash().delete(key, item);}/*** 判断hash表中是否有该项的值** @param key  键 不能为null* @param item 项 不能为null* @return true 存在 false不存在*/public boolean hHasKey(String key, String item) {return redisTemplate.opsForHash().hasKey(key, item);}/*** hash递增 如果不存在,就会创建一个 并把新增后的值返回** @param key  键* @param item 项* @param by   要增加几(大于0)* @return*/public double hincr(String key, String item, double by) {return redisTemplate.opsForHash().increment(key, item, by);}/*** hash递减** @param key  键* @param item 项* @param by   要减少记(小于0)* @return*/public double hdecr(String key, String item, double by) {return redisTemplate.opsForHash().increment(key, item, -by);}// ============================set=============================/*** 根据key获取Set中的所有值** @param key 键* @return*/public Set<Object> sGet(String key) {try {return redisTemplate.opsForSet().members(key);} catch (Exception e) {e.printStackTrace();return null;}}/*** 根据value从一个set中查询,是否存在** @param key   键* @param value 值* @return true 存在 false不存在*/public boolean sHasKey(String key, Object value) {try {return redisTemplate.opsForSet().isMember(key, value);} catch (Exception e) {e.printStackTrace();return false;}}/*** 将数据放入set缓存** @param key    键* @param values 值 可以是多个* @return 成功个数*/public long sSet(String key, Object... values) {try {return redisTemplate.opsForSet().add(key, values);} catch (Exception e) {e.printStackTrace();return 0;}}/*** 将set数据放入缓存** @param key    键* @param time   时间(秒)* @param values 值 可以是多个* @return 成功个数*/public long sSetAndTime(String key, long time, Object... values) {try {Long count = redisTemplate.opsForSet().add(key, values);if (time > 0) {expire(key, time);}return count;} catch (Exception e) {e.printStackTrace();return 0;}}/*** 获取set缓存的长度** @param key 键* @return*/public long sGetSetSize(String key) {try {return redisTemplate.opsForSet().size(key);} catch (Exception e) {e.printStackTrace();return 0;}}/*** 移除值为value的** @param key    键* @param values 值 可以是多个* @return 移除的个数*/public long setRemove(String key, Object... values) {try {Long count = redisTemplate.opsForSet().remove(key, values);return count;} catch (Exception e) {e.printStackTrace();return 0;}}// ===============================list=================================/*** 获取list缓存的内容** @param key   键* @param start 开始* @param end   结束 0 到 -1代表所有值* @return*/public List<Object> lGet(String key, long start, long end) {try {return redisTemplate.opsForList().range(key, start, end);} catch (Exception e) {e.printStackTrace();return null;}}/*** 获取list缓存的长度** @param key 键* @return*/public long lGetListSize(String key) {try {return redisTemplate.opsForList().size(key);} catch (Exception e) {e.printStackTrace();return 0;}}/*** 通过索引 获取list中的值** @param key   键* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推* @return*/public Object lGetIndex(String key, long index) {try {return redisTemplate.opsForList().index(key, index);} catch (Exception e) {e.printStackTrace();return null;}}/*** 将list放入缓存** @param key   键* @param value 值* @param time  时间(秒)* @return*/public boolean lSet(String key, Object value) {try {redisTemplate.opsForList().rightPush(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存** @param key   键* @param value 值* @param time  时间(秒)* @return*/public boolean lSet(String key, Object value, long time) {try {redisTemplate.opsForList().rightPush(key, value);if (time > 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存** @param key   键* @param value 值* @param time  时间(秒)* @return*/public boolean lSet(String key, List<Object> value) {try {redisTemplate.opsForList().rightPushAll(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存** @param key   键* @param value 值* @param time  时间(秒)* @return*/public boolean lSet(String key, List<Object> value, long time) {try {redisTemplate.opsForList().rightPushAll(key, value);if (time > 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 根据索引修改list中的某条数据** @param key   键* @param index 索引* @param value 值* @return*/public boolean lUpdateIndex(String key, long index, Object value) {try {redisTemplate.opsForList().set(key, index, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 移除N个值为value** @param key   键* @param count 移除多少个* @param value 值* @return 移除的个数*/public long lRemove(String key, long count, Object value) {try {Long remove = redisTemplate.opsForList().remove(key, count, value);return remove;} catch (Exception e) {e.printStackTrace();return 0;}}/** setnx*/public boolean setnx(String key, String value) {try {return redisTemplate.opsForValue().setIfAbsent(key, value);} catch (Exception e) {e.printStackTrace();return false;}}/** setnx*/public boolean setnx(String key, String value,long time) {try {return redisTemplate.opsForValue().setIfAbsent(key,value,Duration.ofSeconds(time));} catch (Exception e) {e.printStackTrace();return false;}}/** eval*/public Boolean eval(RedisScript<Long> lua_scripts, List<String> keys, String values) {Long flag = redisTemplate.execute(lua_scripts,  keys, values);//判断是不是为1return flag == 1L;}
}

第三步:定义Redis连接类

@Component
public class RedisLockHelper {private long sleepTime = 100;/*** 直接使用setnx + expire方式获取分布式锁* 非原子性** @param key* @param value* @param timeout* @return*/public boolean lock_setnx(RedisUtil redisTemplate, String key, String value, int timeout) {if ( redisTemplate.setnx(key, value)) {return redisTemplate.expire(key, timeout);} else {return false;}}/*** 使用Lua脚本,脚本中使用setnex+expire命令进行加锁操作** @param redisTemplate* @param key* @param UniqueId* @param seconds* @return*/public boolean Lock_with_lua(RedisUtil redisTemplate, String key, String UniqueId, int seconds) {String luaScript = "if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then" +"redis.call('expire',KEYS[1],ARGV[2]) return 1 else return 0 end";
//        List<String> values = new ArrayList<>();
//        values.add(UniqueId);
//        values.add(String.valueOf(seconds));Object result = redisTemplate.eval(RedisScript.of(luaScript),  Collections.singletonList(key), UniqueId);//判断是否成功return result.equals(1L);}/*** 在Redis的2.6.12及以后中,使用 set key value [NX] [EX] 命令** @param key* @param value* @param timeout* @return*/public boolean lock(RedisUtil redisTemplate, String key, String value, int timeout, TimeUnit timeUnit) {long seconds = timeUnit.toSeconds(timeout);return redisTemplate.setnx(key, value, seconds);}/*** 自定义获取锁的超时时间** @param redisTemplate* @param key* @param value* @param timeout* @param waitTime* @param timeUnit* @return* @throws InterruptedException*/public boolean lock_with_waitTime(RedisUtil redisTemplate, String key, String value, int timeout, long waitTime, TimeUnit timeUnit) throws InterruptedException {long seconds = timeUnit.toSeconds(timeout);while (waitTime >= 0) {if (redisTemplate.setnx(key, value, seconds)) {return true;}waitTime -= sleepTime;Thread.sleep(sleepTime);}return false;}/*** 错误的解锁方法—直接删除key** @param key*/public void unlock_with_del(RedisUtil redisTemplate, String key) {redisTemplate.del(key);}/*** 使用Lua脚本进行解锁操纵,解锁的时候验证value值** @param redisTemplate* @param key* @param value* @return*/public boolean unlock(RedisUtil redisTemplate, String key, String value) {String luaScript = "if redis.call('get',KEYS[1]) == ARGV[1] then " +"return redis.call('del',KEYS[1]) else return 0 end";DefaultRedisScript<Long> redisScript =new DefaultRedisScript<> ();redisScript.setScriptText(luaScript);// 这个值类型要跟lua返回值类型一致才行,否则就会报 java.lang.IllegalStateExceptionredisScript.setResultType(Long.class);return redisTemplate.eval(redisScript, Collections.singletonList(key), value);}
}

第四步:定义切面类

@Aspect
@Component
public class LockMethodAspect {@Autowiredprivate RedisLockHelper redisLockHelper;@Autowiredprivate RedisUtil redisUtis;private Logger logger = LoggerFactory.getLogger(LockMethodAspect.class);@Around("@annotation(org.jeecg.common.aspect.annotation.RedisLock)")public Object around(ProceedingJoinPoint joinPoint) {MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();RedisLock redisLock = method.getAnnotation(RedisLock.class);String value = UUID.randomUUID().toString();logger.info ("====value====="+value+"===========");String key = redisLock.key();logger.info ("====key======"+key+"===========");try {final boolean islock = redisLockHelper.lock(redisUtis,key, value, redisLock.expire(), redisLock.timeUnit());logger.info("isLock : {}",islock);if (!islock) {logger.error("获取锁失败");throw new RuntimeException("获取锁失败");}try {return joinPoint.proceed();} catch (Throwable throwable) {throw new RuntimeException("系统异常");}}  finally {logger.info("释放锁");redisLockHelper.unlock(redisUtis,key, value);}}
}

第五步:定时任务类

@Slf4j
@Service
@DisallowConcurrentExecution//顺序执行,不争抢
public class TestJob implements Job {@Autowiredprivate user userservice;@Override@RedisLock(key = "lock----TestJob",expire=60,timeUnit=TimeUnit.SECONDS)public void execute(JobExecutionContext context) throws JobExecutionException {log.info("执行TestJob定时任务查询");try {QueryWrapper<User> queryWrapper = new QueryWrapper<User>();queryWrapper.eq("id", "304994209985859584");User one = userservice.getOne(queryWrapper);log.info("========================begin===============================");log.info("============================="+one.getCode ()+"=============================");Long aLong = Long.valueOf(one.getCode ());aLong --;one.setCode(aLong+"");log.info("============================="+aLong +"=============================");TimeUnit.SECONDS.sleep(10);userservice.update (one,queryWrapper);log.info("==========================end=============================");} catch (InterruptedException e) {e.printStackTrace();}
}
}

欢迎指正!

jeecg-boot实现分布式定时任务相关推荐

  1. spring boot基于redis的分布式定时任务

    第一步. 自动配置类 主启动类添加:@EnableScheduling //开启定时任务 aop和redis  POM添加: <!--redis驱动--><dependency> ...

  2. 使用Spring Boot + Quartz 实现分布式定时任务平台

    本文将从项目实战出发来介绍分布式定时任务的实现.在某些应用场景下要求任务必须具备高可用性和可扩展性,单台服务器不能满足业务需求,这时就需要使用Quartz实现分布式定时任务. 一.分布式任务应用场景 ...

  3. JAVA学习笔记JEECG BOOT介绍

    JEECG JEECG BOOT 低代码开发平台(前后端分离版本) 当前最新版本: 3.1.0(发布日期:2022-03-01) AUR GitHub stars GitHub forks 项目介绍: ...

  4. [零代码工具推荐] 快速建站神器 Jeecg/Boot

    项目介绍 JeecgBoot 是一款基于代码生成器的低代码开发平台!前后端分离架构 SpringBoot2.x,SpringCloud,Ant Design&Vue,Mybatis-plus, ...

  5. 【redis】分布式锁实现,与分布式定时任务

    如果你还不知道redis的基本命令与基本使用方法,请看 [redis]redis基础命令学习集合 写在前面 redis辣么多数据结构,这么多命令,具体一点,都可以应用在什么场景呢?用来解决什么具体的问 ...

  6. 【xxl-job】轻松实现分布式定时任务demo实例

    [项目描述] 前段时间专门独立了一个spring boot服务,用于做和第三方erp系统的对接工作.此服务的第一个需求工作就是可以通过不同的规则,设置不同的定时任务,从而获取erp系统的商品数据.所以 ...

  7. Java 实现分布式定时任务

    文章目录 前言 一.技术点 二.代码实践 1.引入库 2.创建启动线程入口 3.表结构 4.任务解析 5.任务拉取 三.结果展示 四.总结 前言 最近有一个需求:需要实现分布式定时任务.而市面上的定时 ...

  8. 浅谈传统定时任务和分布式定时任务

    为什么用定时任务? 定时任务平台可以在后台自动检测数据并进行操作.主要应用在订单状态改变.后台统计.定时发送邮件或短信等. 定时任务怎么部署实现? 传统的定时任务可以通过可定时线程池.timertas ...

  9. SpringBoot2.x整合轻量级分布式定时任务ShedLock3.x的使用详解

    目录 前言 SpringBoot2.x整合轻量级分布式定时任务ShedLock3.x的使用详解 一.关于ShedLock 二.ShedLock的三个核心组件 三.ShedLock使用三步走 四.Spr ...

最新文章

  1. 美团、饿了么,你凭什么让我多等几分钟?
  2. 机器学习——决策树的三种学习方法
  3. FastReport使用方法(C/S版)
  4. oracle库创建2个监听,Oracle 添加第二个实例 和 监听
  5. 鸿蒙技术论坛,鸿蒙应用开发入门(六):页面间跳转
  6. 微软将开源查询处理工具Trill,怎么下载部署?
  7. MySQL查询数据表中数据记录(包括多表查询)
  8. 谷歌开源语音识别AI技术,可以从人群中区分每个人的发言
  9. 高性能集群软件Keepalived之安装配置篇
  10. Atitit vod ver 12 new feature v12 pb2 影吧 视频 电影 点播 播放系统v12新特性
  11. Kalman滤波器(1960年论文原文翻译)
  12. python 函数注释出现 :expected an indented block
  13. c报错pointer being freed was not allocated
  14. 用友NC 如何进行增补模块
  15. win10计算机休眠后无法唤醒,win10电脑休眠后无法唤醒的解决办法
  16. 数字时钟程序c语言,C语言实现电子时钟程序
  17. 趋势科技Pc-cillin 2011网友使用体验
  18. 基于HTML旅游网站设计与实现 途游网站制作 学生DW静态旅游网页设计 html静态旅游风景区网页设计制作 web前端课程设计 web前端课程设计代码 web课程设计
  19. 文献阅读笔记--深度学习图像修复方法综述
  20. matlab仿真电气连接,电气系统模块库-simulink与电气系统接口

热门文章

  1. 百度云加速zblog和WordPress规则自定义
  2. (十四)懈寄生(4)
  3. kuangbin专题八 HDU4081 Qin Shi Huang's National Road System(次小生成树)
  4. Windows一键启动jar包bat脚本制作
  5. 《SQL与关系数据库理论——如何编写健壮的SQL代码》一3.7 TABLE_DUM和TABLE_DEE
  6. DUM 与 SIPstack多线程
  7. After Effects Guru: Plugins You Should Know After Effects 大师教程之你必须了解的插件 Lynda课程中文字幕
  8. 双十一买什么比较划算?四款实用性超强不吃灰的数码好物推荐
  9. 教育专家李彦良谈双减后的素质教育和智慧教育
  10. 7种流行PHP编辑器的比较(PHP IDE)