功能介绍:

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

没有使用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.编写函数式接口

@FunctionalInterface
public 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; @Component
public class RedisTemplateHandler { @Autowired
private StringRedisTemplate stringRedisTemplate; /**
* 插入分布式Job Redis锁
*/
public boolean redisSetNX(String key, String val, long expire) {
boolean result = stringRedisTemplate.execute((RedisCallback<Boolean>) 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;
} }

定时任务redis锁+自定义lambda优化提取冗余代码的更多相关文章

  1. springboot+redis+Interceptor+自定义annotation实现接口自动幂等

    前言: 在实际的开发项目中,一个对外暴露的接口往往会面临很多次请求,我们来解释一下幂等的概念:任意多次执行所产生的影响均与一次执行的影响相同.按照这个含义,最终的含义就是 对数据库的影响只能是一次性的 ...

  2. 解锁redis锁的正确姿势

    解锁redis锁的正确姿势 redis是php的好朋友,在php写业务过程中,有时候会使用到锁的概念,同时只能有一个人可以操作某个行为.这个时候我们就要用到锁.锁的方式有好几种,php不能在内存中用锁 ...

  3. zookeeper分布式锁和服务优化配置

    转自:https://www.jianshu.com/p/02eeaee4357f?utm_campaign=maleskine&utm_content=note&utm_medium ...

  4. (实例篇)php 使用redis锁限制并发访问类示例

    1.并发访问限制问题 对于一些需要限制同一个用户并发访问的场景,如果用户并发请求多次,而服务器处理没有加锁限制,用户则可以多次请求成功. 例如换领优惠券,如果用户同一时间并发提交换领码,在没有加锁限制 ...

  5. php 使用redis锁限制并发访问类

    1.并发访问限制问题 对于一些需要限制同一个用户并发访问的场景,如果用户并发请求多次,而服务器处理没有加锁限制,用户则可以多次请求成功. 例如换领优惠券,如果用户同一时间并发提交换领码,在没有加锁限制 ...

  6. Redis数据导入工具优化过程总结

    Redis数据导入工具优化过程总结 背景 使用C++开发了一个Redis数据导入工具 从oracle中将所有表数据导入到redis中: 不是单纯的数据导入,每条oracle中的原有记录,需要经过业务逻 ...

  7. redis锁处理并发问题

    redis锁处理并发问题 redis锁处理高并发问题十分常见,使用的时候常见有几种错误,和对应的解决办法. set方式 setnx方式 setnx+getset方式 set方式 加锁:redis中se ...

  8. redis结合自定义注解实现基于方法的注解缓存,及托底缓存的实现

    本次分享如何使用redis结合自定义注解实现基于方法的注解缓存,及托底缓存的实现思路    现在的互联网公司大多数都是以Redis作为缓存,使用缓存的优点就不赘述了,写这篇文章的目的就是想帮助同学们如 ...

  9. redis 初步认识四(redis锁,防并发)

    using System; namespace ConsoleAppRedis { class Program { static void Main(string[] args) { //第一种,无登 ...

随机推荐

  1. php中session同ip不同端口的多个网站session冲突的解决办法

    在局域网内使用IP加端口的访问方式搭了两个相同程序的站,结果发现用户在一个站下登录后,在另一个站也同时登录了,在一个退出后,另一个站也同时退出了.看了下程序发现两个站都是使用纯session方式记录登 ...

  2. Redis Desktop Manager连接Redis

    1.注释redis.conf文件中的:bind 127.0.0.1修改为自己的IP 2.ifconfig查看自己的虚拟机ip 3.拿到IP后,返回Windows,开启cmd,通过telnet命令,测试 ...

  3. Golang错误处理函数defer、panic、recover、errors.New介绍

    在默认情况下,当发生错误(panic)后,程序就会终止运行 如果发生错误后,可以捕获错误,并通知管理人员(邮件或者短信),程序还可以继续运行,这当然无可厚非 errors.New("错误信息 ...

  4. javaScript中ajax、axios总结

    一.原生js实现ajax请求: 步骤: get请求: // 1.创建一个XMLHttpRequest的对象. var xml=null; //初始值设为空 if(XMLHttpRequest){ xm ...

  5. C# 反射小结

    废话不多说,直接上代码. 1.typeof(类名):它是一个运算符 eg_1:Type type = typeof(int) ; eg_2:public class Student { Type ty ...

  6. win7系统Oracle数据库本地备份

    第一步:命令行登录 sqlplus sys/root@orcl as sysdba sqlplus 超级管理员/密码@数据对象 as sysdba 第二步:创建DIRECTORY create dir ...

  7. linux服务器的所有服务web/dns/dhcp/vsftp-nfs-samba的配置

    nfs是sun开发的 network filesystem 网络文件系统,相当于win的映射网络驱动器, 可以将一台nfs服务器上的目录"挂载"到本地机器上的 一个 本地目录 一样 ...

  8. #2718. 「NOI2018」归程 kruskal重构树

    链接 https://loj.ac/problem/2718 思路 我们希望x所在的连通块尽量的大,而且尽量走高处 离线的话可以询问排序,kruskal过程中更新答案 在线就要用kruskal重构树 ...

  9. P4137 Rmq Problem / mex

    目录 链接 思路 线段树 莫队 链接 https://www.luogu.org/problemnew/show/P4137 思路 做了好几次,每次都得想一会,再记录一下 可持久化权值线段树 区间出现 ...

  10. hihoCoder week13 最近公共祖先·一

    用的dfs,自下往上搜索一个节点的所有祖先,然后在相应祖先 判断是否是另一个节点的祖先,如果是 就截止,否则继续往上搜索,直到搜索到,或者知道所有的祖先都被扫描完成 #include <bits ...