定时任务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) { //第一种,无登 ...
随机推荐
- Docker学习笔记之使用 Docker Compose 管理容器
0x00 概述 通过之前的介绍,我们已经基本掌握了构建.运行容器的方法,但这还远远不够,由于 Docker 采用轻量级容器的设计,每个容器一般只运行一个软件,而目前绝大多数应用系统都绝不是一个软件所能 ...
- Zookeeper本地模式安装
本地模式安装部署 1.安装前准备 (1)安装Jdk (2)拷贝Zookeeper安装包到Linux系统下 (3)解压到指定目录 tar -zxvf zookeeper-3.4.10.tar.gz -C ...
- fjwc2019 D2T3 排序(堆)
#183. 「2019冬令营提高组」排序 贴一段ppt 考虑模拟出这个算法进行k轮(即外层的i循环到k)时的序列,之后再暴力模拟零散的步. 考虑这个算法在01序列上的表现,k轮后实际上就是将最开始的不 ...
- git-tag 标签相关操作
标签可以针对某一时间点的版本做标记,常用于版本发布. 列出标签 $ git tag # 在控制台打印出当前仓库的所有标签$ git tag -l ‘v0.1.*’ # 搜索符合模式的标签 打标签 gi ...
- neutron full stack
1. 通读一下 neutron的那个文档. 里面介绍了, db怎么隔离的, amqp怎么隔离的. 2. 记住文档中,那个full stack的图. 3. 走读代码 从TestOvsC ...
- shell脚本一键安装redis集群[最终版]
直接上shell了. #!/bin/bash #---------------------------------------------------------------------------- ...
- topcoder srm 701 div1 -3
1.一堆石子有$n$个,Alice,Bob轮流拿,给定每个人每次可以拿的石子的数目的集合.谁先不能拿谁输.问谁能赢? 思路:对于先手来说,输赢的局面一定是从某个数字开始呈循环状态.所以找到这个循环开始 ...
- linux任务计划及周期性任务计划
相关命令:at.batch.cron.mailx 未来某时间执行一次任务:at, batch 周期性运行某任务: cron 一.未来某时间执行一次任务:at命令 at, batch, atq, atr ...
- Win10 使用命令修复系统坏死点
我的电脑win10系统升级多次以后,经常会出现所有设置有时点不开的情况 解决: C:\WINDOWS\system32>sfc /SCANNOW 开始系统扫描.此过程将需要一些时间. 开始系统扫 ...
- LuoguP2161 [SHOI2009]会场预约
题目地址 题目链接 题解 用fhqtreap对区间进行维护. 可以注意到的是,对于当前存在的预约,他们一定是升序排列的(有重叠的都被删了). 那么就可以用按照位置分裂的fhqtreap搞了(预约无论按 ...