定时任务redis锁+自定义lambda优化提取冗余代码
功能介绍:
我系统中需要跑三个定时任务,由于是多节点部署,为了防止多个节点的定时任务重复执行。所以在定时任务执行时加个锁,抢到锁的节点才能执行定时任务,没有抢到锁的节点就不执行。从而避免了定时任务重复执行的情况
没有使用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优化提取冗余代码的更多相关文章
- springboot+redis+Interceptor+自定义annotation实现接口自动幂等
前言: 在实际的开发项目中,一个对外暴露的接口往往会面临很多次请求,我们来解释一下幂等的概念:任意多次执行所产生的影响均与一次执行的影响相同.按照这个含义,最终的含义就是 对数据库的影响只能是一次性的 ...
- 解锁redis锁的正确姿势
解锁redis锁的正确姿势 redis是php的好朋友,在php写业务过程中,有时候会使用到锁的概念,同时只能有一个人可以操作某个行为.这个时候我们就要用到锁.锁的方式有好几种,php不能在内存中用锁 ...
- zookeeper分布式锁和服务优化配置
转自:https://www.jianshu.com/p/02eeaee4357f?utm_campaign=maleskine&utm_content=note&utm_medium ...
- (实例篇)php 使用redis锁限制并发访问类示例
1.并发访问限制问题 对于一些需要限制同一个用户并发访问的场景,如果用户并发请求多次,而服务器处理没有加锁限制,用户则可以多次请求成功. 例如换领优惠券,如果用户同一时间并发提交换领码,在没有加锁限制 ...
- php 使用redis锁限制并发访问类
1.并发访问限制问题 对于一些需要限制同一个用户并发访问的场景,如果用户并发请求多次,而服务器处理没有加锁限制,用户则可以多次请求成功. 例如换领优惠券,如果用户同一时间并发提交换领码,在没有加锁限制 ...
- Redis数据导入工具优化过程总结
Redis数据导入工具优化过程总结 背景 使用C++开发了一个Redis数据导入工具 从oracle中将所有表数据导入到redis中: 不是单纯的数据导入,每条oracle中的原有记录,需要经过业务逻 ...
- redis锁处理并发问题
redis锁处理并发问题 redis锁处理高并发问题十分常见,使用的时候常见有几种错误,和对应的解决办法. set方式 setnx方式 setnx+getset方式 set方式 加锁:redis中se ...
- redis结合自定义注解实现基于方法的注解缓存,及托底缓存的实现
本次分享如何使用redis结合自定义注解实现基于方法的注解缓存,及托底缓存的实现思路 现在的互联网公司大多数都是以Redis作为缓存,使用缓存的优点就不赘述了,写这篇文章的目的就是想帮助同学们如 ...
- redis 初步认识四(redis锁,防并发)
using System; namespace ConsoleAppRedis { class Program { static void Main(string[] args) { //第一种,无登 ...
随机推荐
- 自动发现实现url+响应时间监控
url自动发现脚本: [root@jenkins scripts]# cat urlDiscovery.py #!/usr/bin/env python #coding:utf-8 import o ...
- JavaScript中数组的排序方法:1.冒泡排序 2.选择排序
//1.选择排序: //从小到大排序:通过比较首先选出最小的数放在第一个位置上,然后在其余的数中选出次小数放在第二个位置上,依此类推,直到所有的数成为有序序列. var arr2=[19, 8, ...
- vsCode设置中文
1.安装软件之后,关闭欢迎界面,Ctrl+shift+p打开命令窗口,输入lang,选择configuration display language,改为 "locale":&qu ...
- aop的使用
最近考虑需要记录客户对项目的操作日志. 如果在每一个客户都有可能操作的类增加日志对象,改动量太大,同时如果有新的功能增加,又要在新的功能上增加日志操作. 因此考虑后决定采用spring的aop功能. ...
- 从Git上导入Maven 项目到Eclipse
Note: 经验之谈,操作过程中有不懂的地方可以留言问. Step: Open the Eclipse: --1.File>>Import>>Git:Project from ...
- C# 文件与二进制之间的转换
/// <summary> /// 工具类:文件与二进制流间的转换 /// </summary> public class FileBinaryConvertHelper { ...
- Android 充电信息的获取【转】
本文转载自:https://blog.csdn.net/wateryi/article/details/50834821 在android系统中,电池信息是由BatteryService.java统一 ...
- AndroidO Treble架构分析【转】
本文转载自:https://blog.csdn.net/yangwen123/article/details/79835965 从AndroidO开始,google引入了Treble架构,目的是为了方 ...
- Linux 使用statvfs读取文件系统信息
本文转载自:https://blog.csdn.net/papiping/article/details/6980573 在测试过程中,f_bfree的值比f_frsize的值大于10%的尺寸大小,意 ...
- 2-sat相关复习
2-sat相关复习 noi曾经考过,谁能说得准呢 sat问题 通俗的sat问题表述一般是这样的:有很多个集合,每个集合里面有若干元素,现给出一些取元素的规则,要你判断是否可行,可行则给出一个可行方案. ...