博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
定时任务redis锁+自定义lambda优化提取冗余代码
阅读量:7174 次
发布时间:2019-06-29

本文共 4684 字,大约阅读时间需要 15 分钟。

功能介绍:

我系统中需要跑三个定时任务,由于是多节点部署,为了防止多个节点的定时任务重复执行。所以在定时任务执行时加个锁,抢到锁的节点才能执行定时任务,没有抢到锁的节点就不执行。从而避免了定时任务重复执行的情况

没有使用lambda表达式时的代码是这样的:

  @Scheduled(cron = "${task.syncIncrement}")    private void syncIncrementComment() {
     //获取redis锁,并把当前时间放入redis,锁定lockSeconds秒【插入之前判断是否已经有lockName了,如果存在则获取锁失败】 boolean lockKeyResult = redisTemplateHandler.redisSetNX(lockName, String.valueOf(Calendar.getInstance().getTimeInMillis()), lockSeconds); if (lockKeyResult) {
//如果获取锁成功,执行业务代码 LOGGER.info("catch Redis-Task lock"); long startTime = System.currentTimeMillis(); LOGGER.info("开始同步原始数据..."); handler.getInterfaceCommentByCond(0); LOGGER.info("原始数据同步完成!用时:" + (System.currentTimeMillis() - startTime));        //业务代码执行完成,释放锁 boolean delResult = redisTemplateHandler.redisDelNX(lockName); LOGGER.info("free Redis-Task lock: {}", delResult); } else {
//获取锁失败 LOGGER.info("do not catch Redis-Task lock"); if (!redisTemplateHandler.redisCheckNX(lockName, lockSeconds)) { //根据时间判断redis锁是否是失效锁, 执行锁失效,造成死锁 LOGGER.info("Redis-Task lock"); boolean redel = redisTemplateHandler.redisDelNX(lockName); // 释放执行锁 LOGGER.info("free Redis-Task lock: {}", redel); } } }

灰色部分就是对定时任务加的redis锁,可以看出,如果我要写10个定时任务那就要写十遍这些代码。这显然是不优雅的。所以我就想能不能把模板代码提取出来呢?然后把我们的要执行的业务代码当做参数传进来,这样的话我们就不用重复编写这些模板代码。而只需要关注我们的业务代码就好。

解决方案就是函数式接口->lambda表达式

改造:

1.编写函数式接口

@FunctionalInterfacepublic interface RedisLockFunction {    public void excuteMonitor();}

2.提取模板代码

public void excuteInRedisLock(String lockName,RedisLockFunction lock) {        boolean lockKeyResult = redisTemplateHandler.redisSetNX(lockName,                String.valueOf(Calendar.getInstance().getTimeInMillis()), lockSeconds);        if (lockKeyResult) {
lock.excuteMonitor(); //业务代码,就这一行 boolean delResult = redisTemplateHandler.redisDelNX(lockName); LOGGER.info("free Redis-Task lock: {}", delResult); } else { LOGGER.info("do not catch Redis-Task lock"); if (!redisTemplateHandler.redisCheckNX(lockName, lockSeconds)) { // 执行锁失效,造成死锁 LOGGER.info("Redis-Task lock"); boolean redel = redisTemplateHandler.redisDelNX(lockName); // 释放执行锁 LOGGER.info("free Redis-Task lock: {}", redel); } } }

3.调用,可以与上面的做对比

@Scheduled(cron = "${task.syncIncrement}")    private void syncIncrementComment() {        excuteInRedisLock(WebConstants.TASK_LOCK_SYNCINCREMENTCOMMENT_HANDLE_MESSAGE,()->{            LOGGER.info("catch Redis-Task lock");            long startTime = System.currentTimeMillis();            LOGGER.info("开始同步原始数据...");            handler.getInterfaceCommentByCond(0);            LOGGER.info("原始数据同步完成!用时:" + (System.currentTimeMillis() - startTime));        });    }

这样就实现了把代码当做参数传递到一个方法中取执行的功能。从而实现了代码的复用。

附redis锁的工具类代码

package com.ch.evaluation.handler;import java.util.Calendar;import java.util.concurrent.TimeUnit;import org.apache.commons.lang3.StringUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisCallback;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.stereotype.Component;@Componentpublic class RedisTemplateHandler {        @Autowired    private StringRedisTemplate stringRedisTemplate;        /**     * 插入分布式Job Redis锁     */    public boolean redisSetNX(String key, String val, long expire) {        boolean result = stringRedisTemplate.execute((RedisCallback
) connection -> { return connection.setNX(key.getBytes(), val.getBytes()); }); if (result) { stringRedisTemplate.expire(key, expire, TimeUnit.SECONDS); } return result; } /** * 删除分布式Job Redis锁 */ public boolean redisDelNX(String key) { boolean result = stringRedisTemplate.delete(key); return result; } /** * 检查分布式Job Redis锁 */ public boolean redisCheckNX(String key, int lockSeconds) { long expireTime = stringRedisTemplate.getExpire(key); String nxValue = stringRedisTemplate.opsForValue().get(key); long time = 0; if (StringUtils.isNotBlank(nxValue)) { time = Calendar.getInstance().getTimeInMillis() - Long.valueOf(nxValue).longValue(); } if (expireTime <= 0 || time > lockSeconds * 1000L) { redisDelNX(key); return false; } return true; }}

 

转载于:https://www.cnblogs.com/UncleWang001/p/9987104.html

你可能感兴趣的文章
编码原理(附二)----二值化
查看>>
技能大赛规程
查看>>
涓栫晫鐢靛奖绠€鍙测€?
查看>>
Redis入门系列之队列和发布订阅模式
查看>>
Ceph学习笔记
查看>>
unity自带的水
查看>>
LVS搭建过程中需要用到的命令-- ipvsadm
查看>>
【No.9 内存泄漏了么】
查看>>
想成为一名DBA 至少要具备哪些技术
查看>>
CentOS 编译安装php5.5, 并配制支持apach,nignx核心代码
查看>>
第3章 初探HTML
查看>>
基于S/MIME V2标准的加密和解密的控件software IP*Works! S/MIME
查看>>
mysql 备份数据库脚本
查看>>
Linux文件系统上的特殊权限
查看>>
IBM携手红帽将助力企业加快虚拟化步伐
查看>>
8.C++引用
查看>>
利用imgateaselect插件实现前端页面图片截取功能
查看>>
Java super()
查看>>
xinetd服务介绍及配置
查看>>
在Redis-Sentinel的client-reconfig-script脚本中设置VIP
查看>>