定时任务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) { //第一种,无登 ...
随机推荐
- P2233 [HNOI2002]公交车路线
洛咕原题 dp->矩阵乘法 首先我们可以得出一个状态转移方程 f[i][j]=f[i-1][j-1]+f[i-1][j+1] 蓝后发现,我们可以把这转化为一个8*8的转移矩阵 然后跑一遍矩阵快速 ...
- android样式之按钮&&图片
在drawable-hdpi中添加xml文件 <?xml version="1.0" encoding="utf-8"?> <selector ...
- Unity3D之主菜单
1.新建一个名为MainMenu的C#脚本,修改编码后拖动到主摄像机,并给主摄像机添加一个AudioSource声音源作为背景音乐.将音乐文件赋值给Audio Clip属性. 2.创建一个Common ...
- oracle基础——内存管理、优化
内存图解: 自动管理:11g:AMM 10g:ASMM SGA(system global area):由所有服务进程和后台进程共享 PGA(program global area): 由每个服务 ...
- 如何用Tensorflow训练模型成pb文件和和如何加载已经训练好的模型文件
这篇薄荷主要是讲了如何用tensorflow去训练好一个模型,然后生成相应的pb文件.最后会将如何重新加载这个pb文件. 首先先放出PO主的github: https://github.com/ppp ...
- 【Python55--爬虫:代理】
一.反爬虫之隐藏 1.网站检查访问的是正常用户还是程序,关键在于User-Agent 1).第一种方法:采用header --修改header(两种方法): --> 在Request之前通过h ...
- 快速自动安装dart
@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat Non ...
- HDU 4632 Palindrome subsequence & FJUT3681 回文子序列种类数(回文子序列个数/回文子序列种数 容斥 + 区间DP)题解
题意1:问你一个串有几个不连续子序列(相同字母不同位置视为两个) 题意2:问你一个串有几种不连续子序列(相同字母不同位置视为一个,空串视为一个子序列) 思路1:由容斥可知当两个边界字母相同时 dp[i ...
- 外观模式Facade pattern
http://www.runoob.com/design-pattern/facade-pattern.html 外观模式 外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一 ...
- (zhuan) Where can I start with Deep Learning?
Where can I start with Deep Learning? By Rotek Song, Deep Reinforcement Learning/Robotics/Computer V ...