定时任务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的数据管理和存储
0x00 概述 数据是应用程序重要的产出,所以很好的管理和存储数据,是对应用程序劳动结果的尊重.特别是在大数据时代,所有的数据都是重要的资产,保护好数据是每个开发者必须掌握的技能.我们知道,在 Doc ...
- Django设计模式
单例模式: 建造者模式: 示例: from enum import Enum import time PizzaProgress = Enum('PizzaProgress', 'queued pre ...
- Spring Boot中注入配置文件application.properties中的list 对象参数
例如要注入下列参数: dyn.spring.datasources[0].name=branchtadyn.spring.datasources[0].driverClassName=oracle.j ...
- eclipse maven jar工程导出项目依赖的jar包
今天遇到个事,给业务开发/测试搞个了转换工具,是使用swing写的,依赖了很多的三方包,为了方便打算以bat方式提供,但是要导出依赖的三方jar,网上搜了下,如下(已测试): 一.导出到默认目录 ta ...
- java中线程的停止以及LockSupport工具类
看jstack输出的时候,可以发现很多状态都是TIMED_WAITING(parking),如下所示: "http-bio-8080-exec-16" #70 daemon pri ...
- 【题解】Luogu P2787 语文1(chin1)- 理理思维
原题传送门:P2787 语文1(chin1)- 理理思维 前置芝士:珂朵莉树 窝博客里对珂朵莉树的介绍 没什么好说的自己看看吧 珂朵莉树跑的飞快,但还是没有memset0小姐姐跑得快 操作1:暴力统计 ...
- 成绩统计程序(Java)
我的程序: package day20181018;/** * 成绩统计系统 * @author Administrator */import java.util.Scanner;//提供计算机直接扫 ...
- 我仅使用到的dd if
备份一个分区 分区 镜像名 读写块大小 dd if=/dev/sdb of=/diskone.img bs=512 注:可以为了提升I/O把bs设为较高的数值例:bs=1024k 挂载一个分区 mou ...
- Guitar Pro怎样可以快速打出三连音?
相信很多的音乐爱好初学者在使用入门编曲软件Guitar Pro的时候,都会碰到这样一个问题,很多乐谱中都出现了三连音,那么我们使用Guitar Pro的时候,如何能够打出三连音呢? 三连音,就是三等分 ...
- Java TreeSet的定制排序
注:只贴出实现类 package Test3; import java.util.Comparator;import java.util.TreeSet; public class Test { pu ...