diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8a3e1a7..18ef1b4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -45,3 +45,11 @@
| 序号 | 变更类型 | 说明 | 时间 | 备注 |
|:---|:---|:---|:---|:--|
| 1 | A | 整合 spring | 2022-12-7 14:45:40 | |
+
+# release_1.2.0
+
+| 序号 | 变更类型 | 说明 | 时间 | 备注 |
+|:---|:---|:---|:---|:--|
+| 1 | A | 添加 keyFormat | 2022-12-8 14:45:40 | |
+| 2 | A | 添加锁释放失败 | 2022-12-8 14:45:40 | |
+| 3 | O | 加锁处理优化,接口统一优化 | 2022-12-8 14:45:40 | |
diff --git a/README.md b/README.md
index 6fdc148..9a82742 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# 项目简介
-为 java 设计的锁。
+[lock](https://github.com/houbb/lock) 为 java 设计的分布式锁。
[](http://mvnrepository.com/artifact/com.github.houbb/lock)
[](https://www.travis-ci.org/houbb/lock?branch=master)
@@ -8,15 +8,15 @@
## 目的
+- 开箱即用,支持注解式和过程式调用
+
- 基于 redis 的分布式锁
- 整合 spring
- 整合 spring-boot
-- 开箱即用,支持注解。
-
-- 支持多种 redis 的声明方式
+- 支持多种 redis 的整合方式
# 变更日志
@@ -36,7 +36,7 @@ maven 3.x+
com.github.houbb
lock-core
- 1.1.0
+ 1.2.0
```
@@ -45,22 +45,36 @@ maven 3.x+
基于本地 redis 的测试案例。
```java
-ILock lock = LockBs.newInstance()
- .init();
-
-String key = "ddd";
-try {
- // 加锁
- lock.tryLock(key);
- System.out.println("业务处理");
-} catch (Exception e) {
- throw new RuntimeException(e);
-} finally {
- // 释放锁
- lock.unlock(key);
+public void helloTest() {
+ ILock lock = LockBs.newInstance();
+ String key = "ddd";
+ try {
+ // 加锁
+ lock.tryLock(key);
+ System.out.println("业务处理");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ } finally {
+ // 释放锁
+ lock.unlock(key);
+ }
}
```
+### 配置化
+
+为了便于拓展,LockBs 的配置支持自定义:
+
+```java
+LockBs.newInstance()
+ .id(Ids.uuid32()) //id 生成策略
+ .cache(JedisRedisServiceFactory.pooled("127.0.0.1", 6379)) //缓存策略
+ .lockSupport(new RedisLockSupport()) // 锁实现策略
+ .lockKeyFormat(new LockKeyFormat()) // 针对 key 的格式化处理策略
+ .lockReleaseFailHandler(new LockReleaseFailHandler()) //释放锁失败处理
+ ;
+```
+
# 整合 spring
## maven 引入
@@ -69,7 +83,7 @@ try {
com.github.houbb
lock-spring
- 1.1.0
+ 1.2.0
```
@@ -87,9 +101,58 @@ public class SpringConfig {
}
```
+`EnableLock` 注解说明,和引导类对应:
+
+```java
+public @interface EnableLock {
+
+ /**
+ * 唯一标识生成策略
+ * @return 结果
+ * @since 1.1.0
+ */
+ String id() default "lockId";
+
+ /**
+ * 缓存实现策略 bean 名称
+ *
+ * 默认引入 redis-config 中的配置
+ *
+ * @return 实现
+ * @since 1.1.0
+ */
+ String cache() default "springRedisService";
+
+ /**
+ * 加锁 key 格式化策略
+ * @return 策略
+ * @since 1.2.0
+ */
+ String lockKeyFormat() default "lockKeyFormat";
+
+ /**
+ * 锁释放失败处理类
+ * @return 结果
+ * @since 1.2.0
+ */
+ String lockReleaseFailHandler() default "lockReleaseFailHandler";
+
+}
+```
+
+其中 `springRedisService` 使用的是 [redis-config](https://github.com/houbb/redis-config) 中的实现。
+
+redis 的配置信息如下:
+
+| 配置 | 说明 | 默认值
+|:---|:---|:----|
+| redis.address | redis 地址 | 127.0.0.1 |
+| redis.port | redis 端口 | 6379 |
+| redis.password | redis 密码 | |
+
### 使用 LockBs
-我们可以直接 `LockBs` 的引导类。
+我们可以直接 `LockBs` 的引导类,这种适合一些更加灵活的场景。
```java
@ContextConfiguration(classes = SpringConfig.class)
@@ -124,7 +187,7 @@ public class SpringServiceRawTest {
当然,我们可以在方法上直接指定注解 `@Lock`,使用更加方便。
-支持 SPEL 表达式。
+直接使用,AOP 切面生效即可。
```java
@Service
@@ -140,9 +203,44 @@ public class UserService {
}
```
-直接使用,AOP 切面生效即可。
+`@Lock` 属性说明,value 用于指定 key,支持 SPEL 表达式。
-# springboot 整合
+其他属性,和引导类的方法参数一一对应。
+
+```java
+public @interface Lock {
+
+ /**
+ * 缓存的 key 策略,支持 SpEL
+ * @return 结果
+ */
+ String value() default "";
+
+ /**
+ * 时间单位
+ * @return 单位
+ * @since 1.2.0
+ */
+ TimeUnit timeUnit() default TimeUnit.SECONDS;
+
+ /**
+ * 等待锁时间
+ * @return 等待锁时间
+ * @since 1.2.0
+ */
+ long waitLockTime() default 10;
+
+ /**
+ * 业务加锁时间
+ * @return 加锁时间
+ * @since 1.2.0
+ */
+ long lockTime() default 60;
+
+}
+```
+
+# spring boot 整合
## maven 引入
@@ -150,7 +248,7 @@ public class UserService {
com.github.houbb
lock-springboot-starter
- 1.1.0
+ 1.2.0
```
@@ -164,5 +262,4 @@ public class UserService {
持有锁的线程可以多次获取锁
-- [x] 分布式锁注解支持
-
+- [x] 分布式锁注解支持
\ No newline at end of file
diff --git a/lock-api/src/main/java/com/github/houbb/lock/api/core/ILock.java b/lock-api/src/main/java/com/github/houbb/lock/api/core/ILock.java
index e0b80a4..f6ae518 100644
--- a/lock-api/src/main/java/com/github/houbb/lock/api/core/ILock.java
+++ b/lock-api/src/main/java/com/github/houbb/lock/api/core/ILock.java
@@ -12,13 +12,33 @@ public interface ILock {
/**
* 尝试加锁,如果失败,会一直尝试。
*
- * @param time 时间
- * @param unit 当为
* @param key key
+ * @param timeUnit 时间单位
+ * @param waitLockTime 等待锁时间
+ * @param lockTime 加锁时间
* @return 返回
* @since 0.0.1
*/
- boolean tryLock(long time, TimeUnit unit, String key);
+ boolean tryLock(String key, TimeUnit timeUnit, long lockTime, long waitLockTime);
+
+ /**
+ * 尝试加锁,只加锁一次
+ * @param key key
+ * @param timeUnit 时间单位
+ * @param lockTime 加锁时间
+ * @return 返回
+ * @since 0.0.1
+ */
+ boolean tryLock(String key, TimeUnit timeUnit, long lockTime);
+
+ /**
+ * 尝试加锁,只加锁一次
+ * @param key key
+ * @param lockTime 加锁时间
+ * @return 返回
+ * @since 0.0.1
+ */
+ boolean tryLock(String key, long lockTime);
/**
* 尝试加锁,只加锁一次
@@ -32,6 +52,7 @@ public interface ILock {
* 解锁
* @param key key
* @since 0.0.1
+ * @return 是否释放锁成功
*/
boolean unlock(String key);
diff --git a/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockKeyFormat.java b/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockKeyFormat.java
new file mode 100644
index 0000000..d326afb
--- /dev/null
+++ b/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockKeyFormat.java
@@ -0,0 +1,20 @@
+package com.github.houbb.lock.api.core;
+
+/**
+ * 分布式锁 KEY 格式化处理
+ *
+ * @author binbin.hou
+ * @since 0.0.1
+ */
+public interface ILockKeyFormat {
+
+ /**
+ * 对 key 进行格式化处理,避免缓存命名空间处理等问题
+ *
+ * @param formatContext 上下文
+ * @return 返回
+ * @since 1.2.0
+ */
+ String format(ILockKeyFormatContext formatContext);
+
+}
diff --git a/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockKeyFormatContext.java b/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockKeyFormatContext.java
new file mode 100644
index 0000000..b8b6a79
--- /dev/null
+++ b/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockKeyFormatContext.java
@@ -0,0 +1,19 @@
+package com.github.houbb.lock.api.core;
+
+/**
+ * 分布式锁 KEY 格式化处理上下文
+ *
+ * @author binbin.hou
+ * @since 0.0.1
+ */
+public interface ILockKeyFormatContext {
+
+ /**
+ * 原始的 key
+ *
+ * @return 返回
+ * @since 1.2.0
+ */
+ String rawKey();
+
+}
diff --git a/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockReleaseFailHandler.java b/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockReleaseFailHandler.java
new file mode 100644
index 0000000..24763bc
--- /dev/null
+++ b/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockReleaseFailHandler.java
@@ -0,0 +1,18 @@
+package com.github.houbb.lock.api.core;
+
+/**
+ * 释放锁失败处理类
+ *
+ * @author binbin.hou
+ * @since 1.2.0
+ */
+public interface ILockReleaseFailHandler {
+
+ /**
+ * 处理
+ * @param context 上下文
+ * @since 1.2.0
+ */
+ void handle(ILockReleaseFailHandlerContext context);
+
+}
diff --git a/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockReleaseFailHandlerContext.java b/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockReleaseFailHandlerContext.java
new file mode 100644
index 0000000..ce441a2
--- /dev/null
+++ b/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockReleaseFailHandlerContext.java
@@ -0,0 +1,18 @@
+package com.github.houbb.lock.api.core;
+
+/**
+ * 释放锁失败处理类
+ *
+ * @author binbin.hou
+ * @since 1.2.0
+ */
+public interface ILockReleaseFailHandlerContext {
+
+ /**
+ * 释放锁对应的 key
+ * @return 结果
+ * @since 1.2.0
+ */
+ String key();
+
+}
diff --git a/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockSupport.java b/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockSupport.java
index 38b439b..37ab907 100644
--- a/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockSupport.java
+++ b/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockSupport.java
@@ -12,30 +12,21 @@ public interface ILockSupport {
/**
* 尝试加锁,如果失败,会一直尝试。
*
- * @param time 时间
- * @param unit 单位
- * @param key key
+ * 1. 如果等待时间小于等于0,则直接执行加锁
+ * 2. 如果等待时间大于等于0,则尝试循环等待。
+ *
* @param context 上下文
* @return 返回
* @since 0.0.1
*/
- boolean tryLock(long time, TimeUnit unit, String key, final ILockSupportContext context);
-
- /**
- * 尝试加锁,只加锁一次
- * @param key key
- * @param context 上下文
- * @return 返回
- * @since 0.0.1
- */
- boolean tryLock(String key, final ILockSupportContext context);
+ boolean tryLock(final ILockSupportContext context);
/**
* 解锁
- * @param key key
* @param context 上下文
* @since 0.0.1
+ * @return 结果
*/
- boolean unlock(String key, final ILockSupportContext context);
+ boolean unlock(final ILockSupportContext context);
}
diff --git a/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockSupportContext.java b/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockSupportContext.java
index 7cff82f..ca57ffd 100644
--- a/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockSupportContext.java
+++ b/lock-api/src/main/java/com/github/houbb/lock/api/core/ILockSupportContext.java
@@ -3,6 +3,8 @@ package com.github.houbb.lock.api.core;
import com.github.houbb.common.cache.api.service.ICommonCacheService;
import com.github.houbb.id.api.Id;
+import java.util.concurrent.TimeUnit;
+
/**
* 分布式锁接口定义
* @author binbin.hou
@@ -11,21 +13,60 @@ import com.github.houbb.id.api.Id;
public interface ILockSupportContext {
/**
+ * 标识
+ *
* @return 标识策略
* @since 0.0.4
*/
Id id();
/**
+ * 缓存
* @return 缓存策略
* @since 0.0.4
*/
ICommonCacheService cache();
/**
- * 锁的过期时间
- * @return 结果
+ * 时间单位
+ * @return 时间单位
*/
- int lockExpireMills();
+ TimeUnit timeUnit();
+
+ /**
+ * 加锁时间
+ * @return 时间
+ * @since 1.2.0
+ */
+ long lockTime();
+
+ /**
+ * 等待加锁时间
+ * @return 等待时间
+ * @since 1.2.0
+ */
+ long waitLockTime();
+
+ /**
+ * 缓存对应的 key
+ * @return 结果
+ * @since 1.2.0
+ */
+ String key();
+
+ /**
+ * 锁 key 格式化
+ * @since 1.2.0
+ * @return 格式化
+ */
+ ILockKeyFormat lockKeyFormat();
+
+ /**
+ * 锁释放失败处理类
+ * @since 1.2.0
+ * @return 失败处理
+ */
+ ILockReleaseFailHandler lockReleaseFailHandler();
+
}
diff --git a/lock-core/src/main/java/com/github/houbb/lock/core/bs/LockBs.java b/lock-core/src/main/java/com/github/houbb/lock/core/bs/LockBs.java
index 862aec6..ad3f2d8 100644
--- a/lock-core/src/main/java/com/github/houbb/lock/core/bs/LockBs.java
+++ b/lock-core/src/main/java/com/github/houbb/lock/core/bs/LockBs.java
@@ -4,10 +4,9 @@ import com.github.houbb.common.cache.api.service.ICommonCacheService;
import com.github.houbb.heaven.util.common.ArgUtil;
import com.github.houbb.id.api.Id;
import com.github.houbb.id.core.core.Ids;
-import com.github.houbb.lock.api.core.ILock;
-import com.github.houbb.lock.api.core.ILockSupport;
-import com.github.houbb.lock.api.core.ILockSupportContext;
-import com.github.houbb.lock.core.constant.LockConst;
+import com.github.houbb.lock.api.core.*;
+import com.github.houbb.lock.core.support.format.LockKeyFormat;
+import com.github.houbb.lock.core.support.handler.LockReleaseFailHandler;
import com.github.houbb.lock.core.support.lock.LockSupportContext;
import com.github.houbb.lock.core.support.lock.RedisLockSupport;
import com.github.houbb.redis.config.core.factory.JedisRedisServiceFactory;
@@ -20,48 +19,49 @@ import java.util.concurrent.TimeUnit;
* @author binbin.hou
* @since 0.0.4
*/
-public final class LockBs implements ILock{
+public final class LockBs implements ILock {
- private LockBs(){}
+ private LockBs() {
+ }
public static LockBs newInstance() {
return new LockBs();
}
- /**
- * 加锁锁定时间
- * @since 0.0.4
- */
- private int lockExpireMills = LockConst.DEFAULT_EXPIRE_MILLS;
-
/**
* 标识策略
+ *
* @since 0.0.4
*/
private Id id = Ids.uuid32();
/**
* 缓存策略
+ *
* @since 0.0.4
*/
private ICommonCacheService cache = JedisRedisServiceFactory.pooled("127.0.0.1", 6379);
/**
* 锁支持策略
+ *
* @since 1.0.0
*/
private ILockSupport lockSupport = new RedisLockSupport();
/**
- * 锁上下文
- * @since 1.0.0
+ * 锁 key 格式化
+ *
+ * @since 1.2.0
*/
- private ILockSupportContext lockSupportContext = null;
+ private ILockKeyFormat lockKeyFormat = new LockKeyFormat();
- public LockBs lockExpireMills(int lockExpireMills) {
- this.lockExpireMills = lockExpireMills;
- return this;
- }
+ /**
+ * 锁释放失败处理类
+ *
+ * @since 1.2.0
+ */
+ private ILockReleaseFailHandler lockReleaseFailHandler = new LockReleaseFailHandler();
public LockBs id(Id id) {
ArgUtil.notNull(id, "id");
@@ -84,48 +84,67 @@ public final class LockBs implements ILock{
return this;
}
+ public LockBs lockKeyFormat(ILockKeyFormat lockKeyFormat) {
+ ArgUtil.notNull(lockKeyFormat, "lockKeyFormat");
- /**
- * 初始化
- */
- public LockBs init() {
- this.lockSupportContext = LockSupportContext.newInstance()
- .id(id)
- .cache(cache)
- .lockExpireMills(lockExpireMills);
-
+ this.lockKeyFormat = lockKeyFormat;
return this;
}
+ public LockBs lockReleaseFailHandler(ILockReleaseFailHandler lockReleaseFailHandler) {
+ ArgUtil.notNull(lockReleaseFailHandler, "lockReleaseFailHandler");
+
+ this.lockReleaseFailHandler = lockReleaseFailHandler;
+ return this;
+ }
+
+ public ILockReleaseFailHandler lockReleaseFailHandler() {
+ return lockReleaseFailHandler;
+ }
+
@Override
- public boolean tryLock(long time, TimeUnit unit, String key) {
- ArgUtil.notEmpty(key, "key");
- this.checkInitStatus();
+ public boolean tryLock(String key, TimeUnit timeUnit, long lockTime, long waitLockTime) {
+ ILockSupportContext supportContext = buildLockSupportContext(key, timeUnit, lockTime, waitLockTime);
+ return this.lockSupport.tryLock(supportContext);
+ }
+ @Override
+ public boolean tryLock(String key, TimeUnit timeUnit, long lockTime) {
+ return this.tryLock(key, timeUnit, lockTime, 0);
+ }
- return this.lockSupport.tryLock(time, unit, key, lockSupportContext);
+ @Override
+ public boolean tryLock(String key, long lockTime) {
+ return this.tryLock(key, TimeUnit.SECONDS, lockTime);
}
@Override
public boolean tryLock(String key) {
- ArgUtil.notEmpty(key, "key");
- this.checkInitStatus();
-
- return this.lockSupport.tryLock(key, lockSupportContext);
+ return this.tryLock(key, 60);
}
@Override
public boolean unlock(String key) {
+ ILockSupportContext supportContext = buildLockSupportContext(key, TimeUnit.SECONDS, 0, 0);
+ return this.lockSupport.unlock(supportContext);
+ }
+
+ /**
+ * 构建上下文
+ *
+ * @param key key
+ * @param timeUnit 时间
+ * @param lockTime 加锁时间
+ * @param waitLockTime 等待加锁时间
+ * @return 结果
+ * @since 1.0.0
+ */
+ private ILockSupportContext buildLockSupportContext(String key, TimeUnit timeUnit, long lockTime, long waitLockTime) {
ArgUtil.notEmpty(key, "key");
- this.checkInitStatus();
+ ArgUtil.notNull(timeUnit, "timeUnit");
- return this.lockSupport.unlock(key, lockSupportContext);
+ ILockSupportContext context = LockSupportContext.newInstance().id(id).cache(cache).lockKeyFormat(lockKeyFormat).lockReleaseFailHandler(lockReleaseFailHandler).key(key).timeUnit(timeUnit).lockTime(lockTime).waitLockTime(waitLockTime);
+ return context;
}
-
- private void checkInitStatus() {
- ArgUtil.notNull(lockSupportContext, "please init() first!");
- }
-
-
}
diff --git a/lock-core/src/main/java/com/github/houbb/lock/core/support/format/LockKeyFormat.java b/lock-core/src/main/java/com/github/houbb/lock/core/support/format/LockKeyFormat.java
new file mode 100644
index 0000000..f9c299e
--- /dev/null
+++ b/lock-core/src/main/java/com/github/houbb/lock/core/support/format/LockKeyFormat.java
@@ -0,0 +1,21 @@
+package com.github.houbb.lock.core.support.format;
+
+import com.github.houbb.lock.api.core.ILockKeyFormat;
+import com.github.houbb.lock.api.core.ILockKeyFormatContext;
+
+/**
+ * 简单的格式化处理
+ * @since 1.2.0
+ * @author dh
+ */
+public class LockKeyFormat implements ILockKeyFormat {
+
+ @Override
+ public String format(ILockKeyFormatContext formatContext) {
+ String rawKey = formatContext.rawKey();
+
+ String format = "DISTRIBUTED-LOCK:%s";
+ return String.format(format, rawKey);
+ }
+
+}
diff --git a/lock-core/src/main/java/com/github/houbb/lock/core/support/format/LockKeyFormatContext.java b/lock-core/src/main/java/com/github/houbb/lock/core/support/format/LockKeyFormatContext.java
new file mode 100644
index 0000000..3e4c044
--- /dev/null
+++ b/lock-core/src/main/java/com/github/houbb/lock/core/support/format/LockKeyFormatContext.java
@@ -0,0 +1,33 @@
+package com.github.houbb.lock.core.support.format;
+
+import com.github.houbb.lock.api.core.ILockKeyFormatContext;
+
+/**
+ * 上下文
+ * @since 1.2.0
+ */
+public class LockKeyFormatContext implements ILockKeyFormatContext {
+
+
+ /**
+ * 原始的 key
+ *
+ * @since 1.2.0
+ */
+ private String rawKey;
+
+ public static LockKeyFormatContext newInstance() {
+ return new LockKeyFormatContext();
+ }
+
+ @Override
+ public String rawKey() {
+ return rawKey;
+ }
+
+ public LockKeyFormatContext rawKey(String rawKey) {
+ this.rawKey = rawKey;
+ return this;
+ }
+
+}
diff --git a/lock-core/src/main/java/com/github/houbb/lock/core/support/handler/LockReleaseFailHandler.java b/lock-core/src/main/java/com/github/houbb/lock/core/support/handler/LockReleaseFailHandler.java
new file mode 100644
index 0000000..673b780
--- /dev/null
+++ b/lock-core/src/main/java/com/github/houbb/lock/core/support/handler/LockReleaseFailHandler.java
@@ -0,0 +1,23 @@
+package com.github.houbb.lock.core.support.handler;
+
+import com.github.houbb.lock.api.core.ILockReleaseFailHandler;
+import com.github.houbb.lock.api.core.ILockReleaseFailHandlerContext;
+import com.github.houbb.log.integration.core.Log;
+import com.github.houbb.log.integration.core.LogFactory;
+
+/**
+ * 释放锁失败处理类
+ *
+ * @author binbin.hou
+ * @since 1.2.0
+ */
+public class LockReleaseFailHandler implements ILockReleaseFailHandler {
+
+ private static final Log LOG = LogFactory.getLog(LockReleaseFailHandler.class);
+
+ @Override
+ public void handle(ILockReleaseFailHandlerContext context) {
+ LOG.error("[LOCK] release lock failed {}", context.key());
+ }
+
+}
diff --git a/lock-core/src/main/java/com/github/houbb/lock/core/support/handler/LockReleaseFailHandlerContext.java b/lock-core/src/main/java/com/github/houbb/lock/core/support/handler/LockReleaseFailHandlerContext.java
new file mode 100644
index 0000000..e7d89d8
--- /dev/null
+++ b/lock-core/src/main/java/com/github/houbb/lock/core/support/handler/LockReleaseFailHandlerContext.java
@@ -0,0 +1,31 @@
+package com.github.houbb.lock.core.support.handler;
+
+import com.github.houbb.lock.api.core.ILockReleaseFailHandlerContext;
+
+/**
+ * 锁释放失败上下文
+ * @since 1.2.0
+ */
+public class LockReleaseFailHandlerContext implements ILockReleaseFailHandlerContext {
+
+ /**
+ * 释放锁对应的 key
+ * @since 1.2.0
+ */
+ private String key;
+
+ public static LockReleaseFailHandlerContext newInstance() {
+ return new LockReleaseFailHandlerContext();
+ }
+
+ @Override
+ public String key() {
+ return key;
+ }
+
+ public LockReleaseFailHandlerContext key(String key) {
+ this.key = key;
+ return this;
+ }
+
+}
diff --git a/lock-core/src/main/java/com/github/houbb/lock/core/support/lock/LockSupportContext.java b/lock-core/src/main/java/com/github/houbb/lock/core/support/lock/LockSupportContext.java
index 75145d3..d58ff7b 100644
--- a/lock-core/src/main/java/com/github/houbb/lock/core/support/lock/LockSupportContext.java
+++ b/lock-core/src/main/java/com/github/houbb/lock/core/support/lock/LockSupportContext.java
@@ -2,7 +2,13 @@ package com.github.houbb.lock.core.support.lock;
import com.github.houbb.common.cache.api.service.ICommonCacheService;
import com.github.houbb.id.api.Id;
+import com.github.houbb.lock.api.core.ILockKeyFormat;
+import com.github.houbb.lock.api.core.ILockReleaseFailHandler;
import com.github.houbb.lock.api.core.ILockSupportContext;
+import com.github.houbb.lock.core.support.format.LockKeyFormat;
+import com.github.houbb.lock.core.support.handler.LockReleaseFailHandler;
+
+import java.util.concurrent.TimeUnit;
/**
* 分布式锁接口定义
@@ -25,13 +31,43 @@ public class LockSupportContext implements ILockSupportContext {
* 缓存策略
* @since 0.0.4
*/
- private ICommonCacheService commonCacheService;
+ private ICommonCacheService cache;
/**
- * 锁的过期时间
- * @since 1.0.0
+ * 时间单位
*/
- private int lockExpireMills;
+ private TimeUnit timeUnit;
+
+ /**
+ * 加锁时间
+ * @since 1.2.0
+ */
+ private long lockTime;
+
+ /**
+ * 等待加锁时间
+ * @since 1.2.0
+ */
+ private long waitLockTime;
+
+ /**
+ * 缓存对应的 key
+ * @since 1.2.0
+ */
+ private String key;
+
+ /**
+ * 锁 key 格式化
+ * @since 1.2.0
+ */
+ private ILockKeyFormat lockKeyFormat;
+
+ /**
+ * 锁释放失败处理类
+ * @since 1.2.0
+ */
+ private ILockReleaseFailHandler lockReleaseFailHandler;
+
@Override
public Id id() {
@@ -45,21 +81,72 @@ public class LockSupportContext implements ILockSupportContext {
@Override
public ICommonCacheService cache() {
- return commonCacheService;
+ return cache;
}
- public LockSupportContext cache(ICommonCacheService commonCacheService) {
- this.commonCacheService = commonCacheService;
+ public LockSupportContext cache(ICommonCacheService cache) {
+ this.cache = cache;
return this;
}
@Override
- public int lockExpireMills() {
- return lockExpireMills;
+ public TimeUnit timeUnit() {
+ return timeUnit;
}
- public LockSupportContext lockExpireMills(int lockExpireMills) {
- this.lockExpireMills = lockExpireMills;
+ public LockSupportContext timeUnit(TimeUnit timeUnit) {
+ this.timeUnit = timeUnit;
return this;
}
+
+ @Override
+ public long lockTime() {
+ return lockTime;
+ }
+
+ public LockSupportContext lockTime(long lockTime) {
+ this.lockTime = lockTime;
+ return this;
+ }
+
+ @Override
+ public long waitLockTime() {
+ return waitLockTime;
+ }
+
+ public LockSupportContext waitLockTime(long waitLockTime) {
+ this.waitLockTime = waitLockTime;
+ return this;
+ }
+
+ @Override
+ public String key() {
+ return key;
+ }
+
+ public LockSupportContext key(String key) {
+ this.key = key;
+ return this;
+ }
+
+ @Override
+ public ILockKeyFormat lockKeyFormat() {
+ return lockKeyFormat;
+ }
+
+ public LockSupportContext lockKeyFormat(ILockKeyFormat lockKeyFormat) {
+ this.lockKeyFormat = lockKeyFormat;
+ return this;
+ }
+
+ @Override
+ public ILockReleaseFailHandler lockReleaseFailHandler() {
+ return lockReleaseFailHandler;
+ }
+
+ public LockSupportContext lockReleaseFailHandler(ILockReleaseFailHandler lockReleaseFailHandler) {
+ this.lockReleaseFailHandler = lockReleaseFailHandler;
+ return this;
+ }
+
}
diff --git a/lock-core/src/main/java/com/github/houbb/lock/core/support/lock/RedisLockSupport.java b/lock-core/src/main/java/com/github/houbb/lock/core/support/lock/RedisLockSupport.java
index 1a8a307..114a8e2 100644
--- a/lock-core/src/main/java/com/github/houbb/lock/core/support/lock/RedisLockSupport.java
+++ b/lock-core/src/main/java/com/github/houbb/lock/core/support/lock/RedisLockSupport.java
@@ -1,10 +1,13 @@
package com.github.houbb.lock.core.support.lock;
import com.github.houbb.common.cache.api.service.ICommonCacheService;
+import com.github.houbb.heaven.util.lang.StringUtil;
import com.github.houbb.id.api.Id;
import com.github.houbb.id.core.util.IdThreadLocalHelper;
+import com.github.houbb.lock.api.core.ILockKeyFormat;
import com.github.houbb.lock.api.core.ILockSupport;
import com.github.houbb.lock.api.core.ILockSupportContext;
+import com.github.houbb.lock.core.support.format.LockKeyFormatContext;
import com.github.houbb.log.integration.core.Log;
import com.github.houbb.log.integration.core.LogFactory;
import com.github.houbb.redis.config.core.constant.JedisConst;
@@ -22,25 +25,27 @@ public class RedisLockSupport implements ILockSupport {
private final Log log = LogFactory.getLog(RedisLockSupport.class);
@Override
- public boolean tryLock(long time, TimeUnit unit, String key, ILockSupportContext context) {
+ public boolean tryLock(ILockSupportContext context) {
long startTimeMills = System.currentTimeMillis();
// 一次获取,直接成功
- boolean result = this.tryLock(key, context);
+ boolean result = this.doLock(context);
if(result) {
return true;
}
// 时间判断
- if(time <= 0) {
+ final TimeUnit timeUnit = context.timeUnit();
+ final long waitLockTime = context.waitLockTime();
+ if(waitLockTime <= 0) {
return false;
}
- long durationMills = unit.toMillis(time);
+ long durationMills = timeUnit.toMillis(waitLockTime);
long endMills = startTimeMills + durationMills;
// 循环等待
while (System.currentTimeMillis() < endMills) {
- result = tryLock(key, context);
+ result = this.doLock(context);
if(result) {
return true;
}
@@ -49,33 +54,48 @@ public class RedisLockSupport implements ILockSupport {
try {
TimeUnit.MILLISECONDS.sleep(1);
} catch (InterruptedException e) {
- throw new RuntimeException(e);
+ log.debug("[LOCK] try lock wait 1 mills.");
}
}
return false;
}
- @Override
- public boolean tryLock(String key, ILockSupportContext context) {
+ /**
+ * 执行加锁
+ * @param context 上下文
+ * @return 结果
+ */
+ protected boolean doLock(ILockSupportContext context) {
+ final String key = this.getActualKey(context);
+
// 生成当前线程的唯一标识
- Id id = context.id();
+ final Id id = context.id();
final String requestId = id.id();
IdThreadLocalHelper.put(requestId);
log.info("[LOCK] BEGIN TRY LOCK key: {} requestId: {}", key, requestId);
final ICommonCacheService commonCacheService = context.cache();
- final int lockExpireMills = context.lockExpireMills();
+ final TimeUnit timeUnit = context.timeUnit();
+ final long lockTime = context.lockTime();
+ final int lockExpireMills = (int) timeUnit.toMillis(lockTime);
String result = commonCacheService.set(key, requestId, JedisConst.SET_IF_NOT_EXIST, JedisConst.SET_WITH_EXPIRE_TIME, lockExpireMills);
- log.info("[LOCK] END TRY LOCK key: {}, requestId: {}, result: {}", key, requestId, result);
+ log.info("[LOCK] END TRY LOCK key: {}, requestId: {}, lockExpireMills: {}, result: {}", key, requestId, lockExpireMills, result);
return JedisConst.OK.equalsIgnoreCase(result);
}
@Override
- public boolean unlock(String key, ILockSupportContext context) {
+ public boolean unlock(ILockSupportContext context) {
+ final String key = this.getActualKey(context);
+
String requestId = IdThreadLocalHelper.get();
log.info("[LOCK] BEGIN UN LOCK key: {} requestId: {}", key, requestId);
+ if(StringUtil.isEmpty(requestId)) {
+ log.warn("[LOCK] UNLOCK requestId not found, ignore");
+ return false;
+ }
+
final ICommonCacheService commonCacheService = context.cache();
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = commonCacheService.eval(script, Collections.singletonList(key), Collections.singletonList(requestId));
@@ -83,4 +103,21 @@ public class RedisLockSupport implements ILockSupport {
return JedisConst.RELEASE_SUCCESS.equals(result);
}
+ /**
+ * 构建真正的 key
+ * @param context 上下文
+ * @return 结果
+ * @since 1.2.0
+ */
+ private String getActualKey(ILockSupportContext context) {
+ final String rawKey = context.key();
+ final ILockKeyFormat keyFormat = context.lockKeyFormat();
+ LockKeyFormatContext formatContext = LockKeyFormatContext.newInstance()
+ .rawKey(rawKey);
+
+ String key = keyFormat.format(formatContext);
+ log.info("[LOCK] format rawKey: {} to key: {}", rawKey, key);
+ return key;
+ }
+
}
diff --git a/lock-spring/src/main/java/com/github/houbb/lock/spring/annotation/EnableLock.java b/lock-spring/src/main/java/com/github/houbb/lock/spring/annotation/EnableLock.java
index e4ae0d5..445bb30 100644
--- a/lock-spring/src/main/java/com/github/houbb/lock/spring/annotation/EnableLock.java
+++ b/lock-spring/src/main/java/com/github/houbb/lock/spring/annotation/EnableLock.java
@@ -18,13 +18,6 @@ import java.lang.annotation.*;
@EnableAspectJAutoProxy
public @interface EnableLock {
- /**
- * 加锁过期时间
- * @return 时间
- * @since 1.1.0
- */
- int lockExpireMills() default 60 * 1000;
-
/**
* 唯一标识生成策略
* @return 结果
@@ -42,5 +35,18 @@ public @interface EnableLock {
*/
String cache() default "springRedisService";
+ /**
+ * 加锁 key 格式化策略
+ * @return 策略
+ * @since 1.2.0
+ */
+ String lockKeyFormat() default "lockKeyFormat";
+
+ /**
+ * 锁释放失败处理类
+ * @return 结果
+ * @since 1.2.0
+ */
+ String lockReleaseFailHandler() default "lockReleaseFailHandler";
}
diff --git a/lock-spring/src/main/java/com/github/houbb/lock/spring/annotation/Lock.java b/lock-spring/src/main/java/com/github/houbb/lock/spring/annotation/Lock.java
index ac00652..0e33f92 100644
--- a/lock-spring/src/main/java/com/github/houbb/lock/spring/annotation/Lock.java
+++ b/lock-spring/src/main/java/com/github/houbb/lock/spring/annotation/Lock.java
@@ -1,6 +1,7 @@
package com.github.houbb.lock.spring.annotation;
import java.lang.annotation.*;
+import java.util.concurrent.TimeUnit;
/**
* 分布式加锁注解
@@ -20,9 +21,24 @@ public @interface Lock {
String value() default "";
/**
- * 尝试获取锁等待时间
- * @return 结果
+ * 时间单位
+ * @return 单位
+ * @since 1.2.0
*/
- long tryLockMills() default 10 * 1000;
+ TimeUnit timeUnit() default TimeUnit.SECONDS;
+
+ /**
+ * 等待锁时间
+ * @return 等待锁时间
+ * @since 1.2.0
+ */
+ long waitLockTime() default 10;
+
+ /**
+ * 业务加锁时间
+ * @return 加锁时间
+ * @since 1.2.0
+ */
+ long lockTime() default 60;
}
diff --git a/lock-spring/src/main/java/com/github/houbb/lock/spring/aop/LockAspect.java b/lock-spring/src/main/java/com/github/houbb/lock/spring/aop/LockAspect.java
index 6086faa..25be52e 100644
--- a/lock-spring/src/main/java/com/github/houbb/lock/spring/aop/LockAspect.java
+++ b/lock-spring/src/main/java/com/github/houbb/lock/spring/aop/LockAspect.java
@@ -6,6 +6,7 @@ import com.github.houbb.heaven.util.lang.StringUtil;
import com.github.houbb.heaven.util.util.ArrayUtil;
import com.github.houbb.lock.api.exception.LockException;
import com.github.houbb.lock.core.bs.LockBs;
+import com.github.houbb.lock.core.support.handler.LockReleaseFailHandlerContext;
import com.github.houbb.lock.spring.annotation.Lock;
import com.github.houbb.log.integration.core.Log;
import com.github.houbb.log.integration.core.LogFactory;
@@ -24,9 +25,7 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
-import java.lang.reflect.Parameter;
import java.util.Arrays;
-import java.util.concurrent.TimeUnit;
/**
* @author binbin.hou
@@ -61,16 +60,14 @@ public class LockAspect {
boolean isLockAnnotation = method.isAnnotationPresent(Lock.class);
if(isLockAnnotation) {
- Lock lock = method.getAnnotation(Lock.class);
+ Lock lockAnnotation = method.getAnnotation(Lock.class);
// 如果构建 key?
- String lockKey = buildLockKey(lock, point);
+ String lockKey = buildLockKey(lockAnnotation, point);
boolean tryLockFlag = false;
try {
- long tryLockMills = lock.tryLockMills();
-
- tryLockFlag = lockBs.tryLock(tryLockMills, TimeUnit.MILLISECONDS, lockKey);
+ tryLockFlag = lockBs.tryLock(lockKey, lockAnnotation.timeUnit(), lockAnnotation.lockTime(), lockAnnotation.waitLockTime());
if(!tryLockFlag) {
log.warn("[LOCK] TRY LOCK FAILED {}", lockKey);
throw new LockException("[LOCK] TRY LOCK FAILED " + lockKey);
@@ -84,7 +81,9 @@ public class LockAspect {
// 只有获取锁的情况下,才尝试释放锁
if(tryLockFlag) {
boolean unLockFlag = lockBs.unlock(lockKey);
- // 异常处理等
+
+ // 释放锁结果
+ this.lockReleaseFailHandle(unLockFlag, lockKey);
}
}
} else {
@@ -92,6 +91,24 @@ public class LockAspect {
}
}
+ /**
+ * 锁释放失败
+ * @param unLockFlag 释放结果
+ * @param lockKey 加锁信息
+ */
+ private void lockReleaseFailHandle(boolean unLockFlag,
+ String lockKey) {
+ if(unLockFlag) {
+ return;
+ }
+
+ // 触发通知,便于用户自定义处理。
+ // 比如报警
+ LockReleaseFailHandlerContext handlerContext = LockReleaseFailHandlerContext.newInstance()
+ .key(lockKey);
+ this.lockBs.lockReleaseFailHandler().handle(handlerContext);
+ }
+
/**
* 构建加锁的 key
*
diff --git a/lock-spring/src/main/java/com/github/houbb/lock/spring/config/LockAopConfig.java b/lock-spring/src/main/java/com/github/houbb/lock/spring/config/LockAopConfig.java
index 8cb4820..a56d9a1 100644
--- a/lock-spring/src/main/java/com/github/houbb/lock/spring/config/LockAopConfig.java
+++ b/lock-spring/src/main/java/com/github/houbb/lock/spring/config/LockAopConfig.java
@@ -4,6 +4,8 @@ import com.github.houbb.common.cache.api.service.ICommonCacheService;
import com.github.houbb.heaven.util.lang.StringUtil;
import com.github.houbb.id.api.Id;
import com.github.houbb.id.core.core.Ids;
+import com.github.houbb.lock.api.core.ILockKeyFormat;
+import com.github.houbb.lock.api.core.ILockReleaseFailHandler;
import com.github.houbb.lock.core.bs.LockBs;
import com.github.houbb.lock.spring.annotation.EnableLock;
import com.github.houbb.redis.config.core.factory.JedisRedisServiceFactory;
@@ -28,15 +30,16 @@ public class LockAopConfig implements ImportAware, BeanFactoryPostProcessor {
@Bean("lockBs")
public LockBs lockBs() {
- int lockExpireMills = (int) enableLockAttributes.get("lockExpireMills");
ICommonCacheService commonCacheService = beanFactory.getBean(enableLockAttributes.getString("cache"), ICommonCacheService.class);
Id id = beanFactory.getBean(enableLockAttributes.getString("id"), Id.class);
+ ILockKeyFormat lockKeyFormat = beanFactory.getBean(enableLockAttributes.getString("lockKeyFormat"), ILockKeyFormat.class);
+ ILockReleaseFailHandler lockReleaseFailHandler = beanFactory.getBean(enableLockAttributes.getString("lockReleaseFailHandler"), ILockReleaseFailHandler.class);
return LockBs.newInstance()
.cache(commonCacheService)
.id(id)
- .lockExpireMills(lockExpireMills)
- .init();
+ .lockKeyFormat(lockKeyFormat)
+ .lockReleaseFailHandler(lockReleaseFailHandler);
}
/**
diff --git a/lock-spring/src/main/java/com/github/houbb/lock/spring/config/LockBeanConfig.java b/lock-spring/src/main/java/com/github/houbb/lock/spring/config/LockBeanConfig.java
index 65651ad..211e4f3 100644
--- a/lock-spring/src/main/java/com/github/houbb/lock/spring/config/LockBeanConfig.java
+++ b/lock-spring/src/main/java/com/github/houbb/lock/spring/config/LockBeanConfig.java
@@ -2,6 +2,10 @@ package com.github.houbb.lock.spring.config;
import com.github.houbb.id.api.Id;
import com.github.houbb.id.core.core.Ids;
+import com.github.houbb.lock.api.core.ILockKeyFormat;
+import com.github.houbb.lock.api.core.ILockReleaseFailHandler;
+import com.github.houbb.lock.core.support.format.LockKeyFormat;
+import com.github.houbb.lock.core.support.handler.LockReleaseFailHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@@ -21,4 +25,14 @@ public class LockBeanConfig {
return Ids.uuid32();
}
+ @Bean("lockKeyFormat")
+ public ILockKeyFormat lockKeyFormat() {
+ return new LockKeyFormat();
+ }
+
+ @Bean("lockReleaseFailHandler")
+ public ILockReleaseFailHandler lockReleaseFailHandler() {
+ return new LockReleaseFailHandler();
+ }
+
}
diff --git a/lock-test/src/test/java/com/github/houbb/lock/test/core/LockBsTest.java b/lock-test/src/test/java/com/github/houbb/lock/test/core/LockBsTest.java
index b6f9261..ef1f686 100644
--- a/lock-test/src/test/java/com/github/houbb/lock/test/core/LockBsTest.java
+++ b/lock-test/src/test/java/com/github/houbb/lock/test/core/LockBsTest.java
@@ -1,17 +1,20 @@
package com.github.houbb.lock.test.core;
+import com.github.houbb.id.core.core.Ids;
import com.github.houbb.lock.api.core.ILock;
import com.github.houbb.lock.core.bs.LockBs;
+import com.github.houbb.lock.core.support.format.LockKeyFormat;
+import com.github.houbb.lock.core.support.handler.LockReleaseFailHandler;
+import com.github.houbb.lock.core.support.lock.RedisLockSupport;
+import com.github.houbb.redis.config.core.factory.JedisRedisServiceFactory;
import org.junit.Test;
public class LockBsTest {
@Test
public void helloTest() {
- ILock lock = LockBs.newInstance()
- .init();
-
+ ILock lock = LockBs.newInstance();
String key = "ddd";
try {
// 加锁
@@ -25,4 +28,15 @@ public class LockBsTest {
}
}
+ @Test
+ public void configTest() {
+ LockBs.newInstance()
+ .id(Ids.uuid32()) //id 生成策略
+ .cache(JedisRedisServiceFactory.pooled("127.0.0.1", 6379)) //缓存策略
+ .lockSupport(new RedisLockSupport()) // 锁实现策略
+ .lockKeyFormat(new LockKeyFormat()) // 针对 key 的格式化处理策略
+ .lockReleaseFailHandler(new LockReleaseFailHandler()) //释放锁失败处理
+ ;
+ }
+
}