分布式锁一般使用缓存实现的,下面提供一个工具类,
1、缓存配置:
bootstrap.yml 或者 application.properties:
spring.redis.cluster.nodes: " x.x.x.x:6379, x.x.x.x:6379"
spring.cache.redis.timeToLive: 360
2、添加pom依赖:
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.17</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<!-- 引入redisTemplate-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.7.0</version>
</dependency>
3、辅助工具类:
ErrorInfoEnum.java
/**
* 系统及业务级别的通用错误码
*/
public enum ErrorInfoEnum {
//成功
SUCCESS("0", "success"),
ORDER_RESERVATION_GET_LOCK_ERROR("-110","加锁失败"),
//失败
FAILED("-1", "system exception");
private String code;
private String message;
ErrorInfoEnum(String code, String message) {
this.code = code;
this.message = message;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "ErrorInfoEnum{" +
"code='" + code + '\'' +
", message='" + message + '\'' +
'}';
}
}
ReqBody.java
/**
* 请求体
* @param <T>
*/
public final class ReqBody<T> {
private static final long serialVersionUID = 1L;
// 调用方应用名称
protected String appName;
// 数据来源
protected String source;
// 加密签名
private String sign;
//时间戳
private String timestamp;
//参数体
protected T param;
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName = appName;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
public T getParam() {
return param;
}
public void setParam(T param) {
this.param = param;
}
@Override
public String toString() {
return "ReqBody{" +
"appName='" + appName + '\'' +
", source='" + source + '\'' +
", sign='" + sign + '\'' +
", timestamp='" + timestamp + '\'' +
", param=" + param +
'}';
}
}
RespBody.java
/**
* 返回体
*/
public final class RespBody<T> implements Serializable {
private static final long serialVersionUID = -3455864552409084052L;
/**
* 响应代码
*/
private String code;
/**
* 响应消息
*/
private String message;
/**
* 响应结果
*/
private transient T result;
public RespBody() {
successParam();
}
public RespBody(T result) {
successParam();
this.result = result;
}
public void setIErrorInfo(ErrorInfoEnum errorInfo) {
this.code = errorInfo.getCode();
this.message = errorInfo.getMessage();
}
private void successParam() {
this.code = ErrorInfoEnum.SUCCESS.getCode();
this.message = ErrorInfoEnum.SUCCESS.getMessage();
}
}
4、缓存工具类:
import com.alibaba.fastjson2.JSONObject;
import com.zwt.common.ReqBody;
import com.zwt.common.RespBody;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
@Service
public class RedisTemplateService {
private Logger log = LoggerFactory.getLogger(RedisTemplateService.class);
@Autowired
private RedisTemplate redisTemplate;
public RespBody<String> getRedisObjectByKey(ReqBody<String> reqBody) {
String result = null;
if (!StringUtils.isEmpty(reqBody.getParam())) {
Object lightObject = redisTemplate.opsForValue().get(reqBody.getParam());
result = JSONObject.toJSONString(lightObject);
}
return new RespBody<>(result);
}
public RespBody<String> getRedisListByKey(ReqBody<String> reqBody) {
String result = null;
if (!StringUtils.isEmpty(reqBody.getParam())) {
Object lightObject = redisTemplate.opsForList().range(reqBody.getParam(), 0, -1);
result = JSONObject.toJSONString(lightObject);
}
return new RespBody<>(result);
}
public RespBody<Object> setRedisObjectByKey(ReqBody<List<String>> reqBody) {
redisTemplate.opsForValue().set(reqBody.getParam().get(0), reqBody.getParam().get(1),
Integer.parseInt(reqBody.getParam().get(2)), TimeUnit.MINUTES);
return new RespBody<>();
}
public RespBody<Object> setRedisListByKey(ReqBody<List<String>> reqBody) {
Object ob = redisTemplate.opsForList().rightPush(reqBody.getParam().get(0), reqBody.getParam().get(1));
return new RespBody<>(ob);
}
public RespBody<Object> deleteRedisObjectByKey(ReqBody<String> reqBody) {
// 删除redis的值
redisTemplate.opsForValue().set(reqBody.getParam(), 1, 1, TimeUnit.MILLISECONDS);
return new RespBody<>();
}
public RespBody<Object> deleteRedisListByKey(ReqBody<List<String>> reqBody) {
// 删除redis的值
redisTemplate.opsForList().remove(reqBody.getParam().get(0), 0, reqBody.getParam().get(1));
return new RespBody<>();
}
/**
* 加分布式锁
*
* @param key
* @return
*/
public boolean lock(String key,int seconds) {
Boolean result = redisTemplate.opsForValue().setIfAbsent(key, System.currentTimeMillis(), seconds, TimeUnit.SECONDS);
log.info("[{}] 加分布式锁,过期时间 {} 秒,加锁结果 = {}", key, seconds, result);
return result;
}
/**
* 解锁
* @param key
* @return
*/
public Boolean unlock(String key) {
log.info("{} 解锁", key);
return redisTemplate.opsForValue().getOperations().delete(key);
}
}
3、业务代码中使用分布式锁:
1)引用缓存工具类
@Autowired
private RedisTemplateService redisTemplateService;
2)方法中伪代码
// 加锁,一般1个固定字符串加一个字段(具有唯一性)
String key = REDIS_KEY + reserveId;
boolean lock = redisTemplateService.lock(key, EXPIRETIME_10S);
if (!lock) {
log.error("key={}加锁失败", key);
throw new GlobalErrorInfoException(ErrorInfoEnum .ORDER_RESERVATION_GET_LOCK_ERROR);
}
try {
// 业务代码
} finally {
//锁释放
redisTemplateService.unlock(key);
}
4、涉及的常量:
// 锁的key,根据自身业务自定义
public static final String REDIS_KEY = "MT:RESERVE:";
// 锁过期时间,根据方法逻辑处理复杂度自定义
public static final Integer EXPIRETIME_10S = 10;