如何使用注解方式实现 Redis 分布式锁
短信预约 -IT技能 免费直播动态提醒
引入 Redisson
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.14.1</version>
</dependency>
初始化 Redisson
@Configuration
public class RedissonConfiguration {
// 此处更换自己的 Redis 地址即可
@Value("${redis.addr}")
private String addr;
@Bean
public RedissonClient redisson() {
Config config = new Config();
config.useSingleServer()
.setAddress(String.format("%s%s", "redis://", addr))
.setConnectionPoolSize(64) // 连接池大小
.setConnectionMinimumIdleSize(8) // 保持最小连接数
.setConnectTimeout(1500) // 建立连接超时时间
.setTimeout(2000) // 执行命令的超时时间, 从命令发送成功时开始计时
.setRetryAttempts(2) // 命令执行失败重试次数
.setRetryInterval(1000); // 命令重试发送时间间隔
return Redisson.create(config);
}
}
这样我们就可以在项目里面使用 Redisson 了。
编写 Redisson 分布式锁工具类
Redis 分布式锁的工具类,主要是调用 Redisson 客户端实现,做了轻微的封装。
@Service
@Slf4j
public class LockManager {
private static final int MIN_WAIT_TIME = 10;
@Resource
private RedissonClient redisson;
public LockResult lock(String key, long expireTime, long waitTime) {
return lock(key, expireTime, waitTime, () -> new BizException(ResponseEnum.COMMON_FREQUENT_OPERATION_ERROR));
}
private LockResult lock(String key, long expireTime, long waitTime, Supplier<BizException> exceptionSupplier) {
if (waitTime < MIN_WAIT_TIME) {
waitTime = MIN_WAIT_TIME;
}
LockResult result = new LockResult();
try {
RLock rLock = redisson.getLock(key);
try {
if (rLock.tryLock(waitTime, expireTime, TimeUnit.MILLISECONDS)) {
result.setLockResultStatus(LockResultStatus.SUCCESS);
result.setRLock(rLock);
} else {
result.setLockResultStatus(LockResultStatus.FAILURE);
}
} catch (InterruptedException e) {
log.error("Redis 获取分布式锁失败, key: {}, e: {}", key, e.getMessage());
result.setLockResultStatus(LockResultStatus.EXCEPTION);
rLock.unlock();
}
} catch (Exception e) {
log.error("Redis 获取分布式锁失败, key: {}, e: {}", key, e.getMessage());
result.setLockResultStatus(LockResultStatus.EXCEPTION);
}
if (exceptionSupplier != null && LockResultStatus.FAILURE.equals(result.getLockResultStatus())) {
log.warn("Redis 加锁失败, key: {}", key);
throw exceptionSupplier.get();
}
log.info("Redis 加锁结果:{}, key: {}", result.getLockResultStatus(), key);
return result;
}
public void unlock(RLock rLock) {
try {
rLock.unlock();
} catch (Exception e) {
log.warn("Redis 解锁失败", e);
}
}
}
加锁结果状态枚举类。
public enum LockResultStatus {
SUCCESS,
FAILURE,
EXCEPTION;
}
加锁结果类封装了加锁状态和RLock。
@Setter
@Getter
public class LockResult {
private LockResultStatus lockResultStatus;
private RLock rLock;
}
自此我们就可以使用分布式锁了,使用方式:
@Service
@Slf4j
public class TestService {
@Resource
private LockManager lockManager;
public String test(String userId) {
// 锁:userId, 锁超时时间:5s, 锁等待时间:50ms
LockResult lockResult = lockManager.lock(userId, 5000, 50);
try {
// 业务代码
} finally {
lockManager.unlock(lockResult.getRLock());
}
return "";
}
}
为了防止程序发生异常,所以每次我们都需要在finally
代码块里手动释放锁。为了更方便优雅的使用 Redis 分布式锁,我们使用注解方式实现下。
声明注解 @Lock
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Lock {
String value();
long expireTime() default 5000L;
long waitTime() default 50L;
}
注解解析类
@ASPect
@Component
@Slf4j
public class LockAnnotationParser {
@Resource
private LockManager lockManager;
@Pointcut(value = "@annotation(Lock)")
private void cutMethod() {
}
@Around(value = "cutMethod() && @annotation(lock)")
public Object parser(ProceedingJoinPoint point, Lock lock) throws Throwable {
String value = lock.value();
if (isEl(value)) {
value = getByEl(value, point);
}
LockResult lockResult = lockManager.lock(getRealLockKey(value), lock.expireTime(), lock.waitTime());
try {
return point.proceed();
} finally {
lockManager.unlock(lockResult.getRLock());
}
}
private String getByEl(String el, ProceedingJoinPoint point) {
Method method = ((MethodSignature) point.getSignature()).getMethod();
String[] paramNames = getParameterNames(method);
Object[] arguments = point.getArgs();
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(el);
EvaLuationContext context = new StandardEvaluationContext();
for (int i = 0; i < arguments.length; i++) {
context.setVariable(paramNames[i], arguments[i]);
}
return expression.getValue(context, String.class);
}
private String[] getParameterNames(Method method) {
LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
return u.getParameterNames(method);
}
private boolean isEl(String str) {
return str.contains("#");
}
private String getRealLockKey(String value) {
return String.format("lock:%s", value);
}
}
下面使用注解方式使用分布式锁:
@Service
@Slf4j
public class TestService {
@Lock("'test_'+#user.userId")
public String test(User user) {
// 业务代码
return "";
}
}
当然也可以自定义锁的超时时间和等待时间
@Service
@Slf4j
public class TestService {
@Lock(value = "'test_'+#user.userId", expireTime = 3000, waitTime = 30)
public String test(User user) {
// 业务代码
return "";
}
}
到此这篇关于如何使用注解方式实现 Redis 分布式锁的文章就介绍到这了,更多相关Redis 分布式锁内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341