redis实现,使用setnx+value+expireTime
![](https://img-blog.csdnimg.cn/img_convert/fd29faa837b550e1d32762247aae6379.png)
maven依赖
<!-- redis实现分布式锁,maven仓库用户最多 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<!-- 注解开发 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- hutool jar SpringUtil工具类使用 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.7</version>
</dependency>
Java接口定义
package com.hncu.ex.common.base.service;
/**
* 分布式锁定义
*/
public interface IDistributeLockService {
/**
* 获取锁
*
* @param lockKey 锁
* @param lockValue 请求对象
* @param expireTime 过期时间
* @return 是否获取锁
*/
boolean lock(String lockKey, String lockValue, int expireTime);
/**
* 释放锁
*
* @param lockKey 锁
* @param lockValue 请求对象
* @return 是否释放锁
*/
boolean unlock(String lockKey, String lockValue);
}
接口实现
package com.hncu.ex.common.base.service.impl;
import com.hncu.ex.common.base.service.IDistributeLockService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import java.util.Collections;
/**
* redis实现分布式锁
*/
@Slf4j
@Component("redisDistributeLockService")
public class RedisDistributeLockService implements IDistributeLockService {
/**
* 设置成功标识
*/
private static final String SET_SUCCESS = "OK";
/**
* NX :表示key不存在的时候,才能set成功,也即保证只有第一个客户端请求才能获得锁,而其他客户端请求只能等其释放锁,才能获取。
* EX seconds :设定key的过期时间,时间单位是秒。
* PX milliseconds: 设定key的过期时间,单位为毫秒
* XX: 仅当key存在时设置值
*/
private static final String LUA_SCRIPTS = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
/**
* redis对象
*/
private Jedis jedis;
public RedisDistributeLockService() {
init();
}
// 初始化加载
private void init() {
//1. 获取连接
jedis = new Jedis("localhost", 6379);
log.info("redis初始化成功。。。");
}
/**
* 获取锁
*
* @param lockKey 锁
* @param lockValue 请求对象
* @param expireTime 过期时间,单位:毫秒
* @return 是否获取锁
*/
@Override
public boolean lock(String lockKey, String lockValue, int expireTime) {
log.info("获取锁,lockKey-[{}],lockValue-[{}],毫秒-[{}]", lockKey, lockValue, expireTime);
String msg = jedis.set(lockKey, lockValue, "NX", "PX", expireTime);
//判断是否成功
return SET_SUCCESS.equals(msg);
}
/**
* 释放锁
*
* @param lockKey 锁
* @param lockValue 请求对象
* @return 是否释放锁
*/
@Override
public boolean unlock(String lockKey, String lockValue) {
log.info("释放锁,lockKey-[{}],lockValue-[{}]", lockKey, lockValue);
// 调用lua脚本,执行setnx命令
Object result = jedis.eval(LUA_SCRIPTS, Collections.singletonList(lockKey), Collections.singletonList(lockValue));
//判断是否成功
return result.equals(1L);
}
}
工具类
package com.hncu.ex.common.base.util;
import cn.hutool.extra.spring.SpringUtil;
import com.hncu.ex.common.base.service.impl.RedisDistributeLockService;
/**
* 分布式锁工具类
*/
public class DistributeLockUtils {
/**
* key前缀
*/
public static final String REDIS_KEY_PREFIX = "hncu.lock.redis.";
/**
* key过期时间,3秒=3*1000
*/
public static final int REDIS_KEY_EXPIRE_TIME = 3000;
/**
* 获取锁
*
* @param lockKey 锁
* @param lockValue 请求对象
* @return 是否获取锁
*/
public static boolean lock(String lockKey, String lockValue) {
String key = REDIS_KEY_PREFIX + lockKey;
RedisDistributeLockService lockService = SpringUtil.getBean(RedisDistributeLockService.class);
return lockService.lock(key, lockValue, REDIS_KEY_EXPIRE_TIME);
}
/**
* 释放锁
*
* @param lockKey 锁
* @param lockValue 请求对象
* @return 是否释放锁
*/
public static boolean unlock(String lockKey, String lockValue) {
String key = REDIS_KEY_PREFIX + lockKey;
RedisDistributeLockService lockService = SpringUtil.getBean(RedisDistributeLockService.class);
return lockService.unlock(key, lockValue);
}
}
业务调用案例
package com.hncu.ex.common.base.service.impl;
import com.hncu.ex.common.base.service.IBusinessService;
import com.hncu.ex.common.base.util.DistributeLockUtils;
import com.hncu.ex.common.dto.common.base.InjectTaskReq;
import com.hncu.ex.common.dto.common.base.InjectTaskRsp;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.UUID;
@Slf4j
@Service
public class BusinessServiceImpl implements IBusinessService {
@Override
public InjectTaskRsp injectTask(InjectTaskReq req) {
InjectTaskRsp result = new InjectTaskRsp();
String taskId = req.getTaskId();
if (StringUtils.isEmpty(taskId)) {
result.setMsg("taskId is not empty");
return result;
}
String lockKey = taskId;
String lockValue = UUID.randomUUID().toString().replaceAll("-", "");
boolean lock = DistributeLockUtils.lock(lockKey, lockValue);
String msg = "失败";
try {
if (lock) {
msg = "锁获取成功,注入任务成功,该接口需要运行3秒,期间不可访问-" + lockValue;
log.info(msg);
Thread.sleep(3000);
} else {
msg = "获取分布式锁失败-" + lockValue;
log.info(msg);
}
} catch (Exception e) {
log.error("注入任务失败-" + lockValue, e);
} finally {
if (lock) {
DistributeLockUtils.unlock(lockKey, lockValue);
}
}
result.setMsg(msg);
return result;
}
}
postMan调用
http://localhost:6666/common/api/inject/injectTask
{
"taskId": "202303070001"
}
日志打印
2023-03-07 20:20:16.842 INFO [http-nio-6666-exec-2] c.h.e.c.b.service.impl.RedisDistributeLockService:53 - 获取锁,lockKey-[hncu.lock.redis.202303070001],lockValue-[58339998d0c0405c8a3210b6cd05a6d4],毫秒-[3000]
2023-03-07 20:20:16.847 INFO [http-nio-6666-exec-2] c.h.e.common.base.service.impl.BusinessServiceImpl:34 - 锁获取成功,注入任务成功,该接口需要运行3秒,期间不可访问-58339998d0c0405c8a3210b6cd05a6d4
2023-03-07 20:20:17.812 INFO [http-nio-6666-exec-4] c.h.e.c.b.service.impl.RedisDistributeLockService:53 - 获取锁,lockKey-[hncu.lock.redis.202303070001],lockValue-[c91acf4299094676a62f437f0c85e31f],毫秒-[3000]
2023-03-07 20:20:17.812 INFO [http-nio-6666-exec-4] c.h.e.common.base.service.impl.BusinessServiceImpl:38 - 获取分布式锁失败-c91acf4299094676a62f437f0c85e31f
2023-03-07 20:20:18.189 INFO [http-nio-6666-exec-10] c.h.e.c.b.service.impl.RedisDistributeLockService:53 - 获取锁,lockKey-[hncu.lock.redis.202303070001],lockValue-[d5102e3063a94def8f7603b1b4f92e5f],毫秒-[3000]
2023-03-07 20:20:18.189 INFO [http-nio-6666-exec-10] c.h.e.common.base.service.impl.BusinessServiceImpl:38 - 获取分布式锁失败-d5102e3063a94def8f7603b1b4f92e5f
2023-03-07 20:20:18.724 INFO [http-nio-6666-exec-3] c.h.e.c.b.service.impl.RedisDistributeLockService:53 - 获取锁,lockKey-[hncu.lock.redis.202303070001],lockValue-[043bb1466a7048da9e560a6981799c08],毫秒-[3000]
2023-03-07 20:20:18.724 INFO [http-nio-6666-exec-3] c.h.e.common.base.service.impl.BusinessServiceImpl:38 - 获取分布式锁失败-043bb1466a7048da9e560a6981799c08
2023-03-07 20:20:19.266 INFO [http-nio-6666-exec-7] c.h.e.c.b.service.impl.RedisDistributeLockService:53 - 获取锁,lockKey-[hncu.lock.redis.202303070001],lockValue-[5d659ebab64b40f1bb911ccd5abc0596],毫秒-[3000]
2023-03-07 20:20:19.268 INFO [http-nio-6666-exec-7] c.h.e.common.base.service.impl.BusinessServiceImpl:38 - 获取分布式锁失败-5d659ebab64b40f1bb911ccd5abc0596
2023-03-07 20:20:19.847 INFO [http-nio-6666-exec-2] c.h.e.c.b.service.impl.RedisDistributeLockService:68 - 释放锁,lockKey-[hncu.lock.redis.202303070001],lockValue-[58339998d0c0405c8a3210b6cd05a6d4]
2023-03-07 20:20:19.848 INFO [http-nio-6666-exec-8] c.h.e.c.b.service.impl.RedisDistributeLockService:53 - 获取锁,lockKey-[hncu.lock.redis.202303070001],lockValue-[de03d1ad87ca444291c1f9162825ce90],毫秒-[3000]
2023-03-07 20:20:19.848 INFO [http-nio-6666-exec-8] c.h.e.common.base.service.impl.BusinessServiceImpl:34 - 锁获取成功,注入任务成功,该接口需要运行3秒,期间不可访问-de03d1ad87ca444291c1f9162825ce90
2023-03-07 20:20:22.849 INFO [http-nio-6666-exec-8] c.h.e.c.b.service.impl.RedisDistributeLockService:68 - 释放锁,lockKey-[hncu.lock.redis.202303070001],lockValue-[de03d1ad87ca444291c1f9162825ce90]
启动类
package com.hncu.ex.common;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
// mybatisPlus需要加载mapper接口
@MapperScan(value = {"com.hncu.ex.**.dao", "com.hncu.ex.**.mapper"})
// 使用HuTool工具包中的SpringUtil
@SpringBootApplication(scanBasePackages = {"com.hncu.ex", "cn.hutool.extra.spring"})
public class CommonWebApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(CommonWebApplication.class, args);
System.out.println("服务启动,接口为 " + run.getEnvironment().getProperty("server.port"));
}
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,
* 需要设置 MybatisConfiguration#useDeprecatedExecutor = false
* 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
return interceptor;
}
}
日志配置文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60000">
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{50}:%L - %msg%n</pattern>
</encoder>
</appender>
<!-- 自定义日志级别 -->
<logger name="org.springframework.web" level="INFO"/>
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
公共返回类
package com.hncu.ex.common.base;
import lombok.Data;
/**
* 接口返回类
*/
@Data
public class EduResult<T> {
public static final String SUCCESS = "200";
public static final String SUCCESS_MSG = "success";
public static final String FAIL = "401";
public static final String FAIL_MSG = "fail";
private String code;
private String msg;
private T data;
public EduResult() {
this(SUCCESS, SUCCESS_MSG, null);
}
public EduResult(String code, String msg) {
this(code, msg, null);
}
public EduResult(String code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public static <T> EduResult build(String code, String msg, T data) {
return new EduResult(code, msg, data);
}
public static <T> EduResult success(String code, String msg, T data) {
return new EduResult(code, msg, data);
}
public static <T> EduResult success(String msg, T data) {
return new EduResult(SUCCESS, msg, data);
}
public static EduResult success(String msg) {
return new EduResult(SUCCESS, msg, null);
}
public static <T> EduResult success(T data) {
return new EduResult(SUCCESS, SUCCESS_MSG, data);
}
public static EduResult success() {
return new EduResult(SUCCESS, SUCCESS_MSG, null);
}
public static <T> EduResult fail(String code, String msg, T data) {
return new EduResult(code, msg, data);
}
public static <T> EduResult fail(String msg, T data) {
return new EduResult(FAIL, msg, data);
}
public static EduResult fail(String msg) {
return new EduResult(FAIL, msg, null);
}
public static <T> EduResult fail(T data) {
return new EduResult(FAIL, FAIL_MSG, data);
}
public static EduResult fail() {
return new EduResult(FAIL, FAIL_MSG, null);
}
}
请求对象
package com.hncu.ex.common.dto.common.base;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
/**
* <p>
* 注入任务表
* </p>
*
* @author LiuQiang
* @since 2023-03-07
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class InjectTaskReq implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 任务主键
*/
private String taskId;
/**
* 任务主题
*/
private String taskTile;
/**
* 任务内容
*/
private String taskContent;
}
返回对象
package com.hncu.ex.common.dto.common.base;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
/**
* <p>
* 注入任务表
* </p>
*
* @author LiuQiang
* @since 2023-03-07
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class InjectTaskRsp implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 任务消息
*/
private String msg;
}
代码仓库地址
https://gitee.com/jadenJunLanLiu/study3.git
![](https://img-blog.csdnimg.cn/img_convert/a92a6d92bf96909a92ed87e851180b28.png)
在common-web工程中