Compare commits
19 Commits
release_0.
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ef657d0ba | ||
|
|
0f9ce0441e | ||
|
|
bc1fd8fc71 | ||
|
|
4ce823e161 | ||
|
|
62d2a60edc | ||
|
|
0bae77fc8a | ||
|
|
c45f6b53a0 | ||
|
|
27a931ae77 | ||
|
|
8bf1df19c2 | ||
|
|
c0e3cc4bf2 | ||
|
|
402653393d | ||
|
|
7fd29cd1f2 | ||
|
|
ecd879763a | ||
|
|
d06effa306 | ||
|
|
bce83c6d1e | ||
|
|
2016605dfd | ||
|
|
d273441a72 | ||
|
|
2cc9eccdee | ||
|
|
4beac5f889 |
41
CHANGELOG.md
41
CHANGELOG.md
@@ -19,4 +19,43 @@
|
|||||||
|
|
||||||
| 序号 | 变更类型 | 说明 | 时间 | 备注 |
|
| 序号 | 变更类型 | 说明 | 时间 | 备注 |
|
||||||
|:---|:---|:---|:---|:--|
|
|:---|:---|:---|:---|:--|
|
||||||
| 1 | A | 常见锁添加 | 2020-9-2 14:45:40 | |
|
| 1 | A | 常见锁添加 | 2020-9-2 14:45:40 | |
|
||||||
|
|
||||||
|
# release_0.0.3
|
||||||
|
|
||||||
|
| 序号 | 变更类型 | 说明 | 时间 | 备注 |
|
||||||
|
|:---|:---|:---|:---|:--|
|
||||||
|
| 1 | A | redis 锁独立,便于使用 | 2021-12-08 14:45:40 | |
|
||||||
|
|
||||||
|
# release_0.0.4
|
||||||
|
|
||||||
|
| 序号 | 变更类型 | 说明 | 时间 | 备注 |
|
||||||
|
|:---|:---|:---|:---|:--|
|
||||||
|
| 1 | A | 简单锁的实现,优化 redisLock 实现策略 | 2022-04-17 14:45:40 | |
|
||||||
|
|
||||||
|
|
||||||
|
# release_1.0.0
|
||||||
|
|
||||||
|
| 序号 | 变更类型 | 说明 | 时间 | 备注 |
|
||||||
|
|:---|:---|:---|:---|:--|
|
||||||
|
| 1 | A | 基于 redis 实现的分布式锁策略 | 2022-12-7 14:45:40 | |
|
||||||
|
|
||||||
|
# release_1.1.0
|
||||||
|
|
||||||
|
| 序号 | 变更类型 | 说明 | 时间 | 备注 |
|
||||||
|
|:---|:---|:---|:---|:--|
|
||||||
|
| 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 | |
|
||||||
|
|
||||||
|
# release_1.3.0
|
||||||
|
|
||||||
|
| 序号 | 变更类型 | 说明 | 时间 | 备注 |
|
||||||
|
|:---|:---|:---|:---|:--|
|
||||||
|
| 1 | A | 让 redis-config 主动启用,避免无法主动管理的情况 | 2022-12-8 14:45:40 | |
|
||||||
|
|||||||
257
README.md
257
README.md
@@ -1,22 +1,30 @@
|
|||||||
# 项目简介
|
# 项目简介
|
||||||
|
|
||||||
为 java 设计的锁。
|
[lock](https://github.com/houbb/lock) 为 java 设计的分布式锁,开箱即用,纵享丝滑。
|
||||||
|
|
||||||
[](http://mvnrepository.com/artifact/com.github.houbb/lock)
|
[](http://mvnrepository.com/artifact/com.github.houbb/lock)
|
||||||
[](https://www.travis-ci.org/houbb/lock?branch=master)
|
[](https://www.travis-ci.org/houbb/lock?branch=master)
|
||||||
[](https://coveralls.io/github/houbb/lock?branch=master)
|
[](https://coveralls.io/github/houbb/lock?branch=master)
|
||||||
|
|
||||||
|
开源地址:[https://github.com/houbb/lock](https://github.com/houbb/lock)
|
||||||
|
|
||||||
## 目的
|
## 目的
|
||||||
|
|
||||||
|
- 开箱即用,支持注解式和过程式调用
|
||||||
|
|
||||||
- 基于 redis 的分布式锁
|
- 基于 redis 的分布式锁
|
||||||
|
|
||||||
- 基于 oracle 的分布式锁
|
- 内置支持多种 redis 的整合方式
|
||||||
|
|
||||||
- 基于 mysql 的分布式锁
|
- 渐进式设计,可独立于 spring 使用
|
||||||
|
|
||||||
|
- 整合 spring
|
||||||
|
|
||||||
|
- 整合 spring-boot
|
||||||
|
|
||||||
# 变更日志
|
# 变更日志
|
||||||
|
|
||||||
> [变更日志](doc/CHANGELOG.md)
|
> [变更日志](CHANGELOG.md)
|
||||||
|
|
||||||
# 快速开始
|
# 快速开始
|
||||||
|
|
||||||
@@ -32,7 +40,7 @@ maven 3.x+
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.houbb</groupId>
|
<groupId>com.github.houbb</groupId>
|
||||||
<artifactId>lock-core</artifactId>
|
<artifactId>lock-core</artifactId>
|
||||||
<version>${最新版本}</version>
|
<version>1.3.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -41,38 +49,237 @@ maven 3.x+
|
|||||||
基于本地 redis 的测试案例。
|
基于本地 redis 的测试案例。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
Jedis jedis = new Jedis("127.0.0.1", 6379);
|
public void helloTest() {
|
||||||
IOperator operator = new JedisOperator(jedis);
|
ILock lock = LockBs.newInstance();
|
||||||
|
String key = "ddd";
|
||||||
// 获取锁
|
try {
|
||||||
ILock lock = LockRedisBs.newInstance().operator(operator).lock();
|
// 加锁
|
||||||
|
lock.tryLock(key);
|
||||||
try {
|
System.out.println("业务处理");
|
||||||
boolean lockResult = lock.tryLock();
|
} catch (Exception e) {
|
||||||
System.out.println(lockResult);
|
throw new RuntimeException(e);
|
||||||
// 业务处理
|
} finally {
|
||||||
} catch (Exception e) {
|
// 释放锁
|
||||||
e.printStackTrace();
|
lock.unlock(key);
|
||||||
} finally {
|
}
|
||||||
lock.unlock();
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 配置化
|
||||||
|
|
||||||
|
为了便于拓展,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 引入
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>lock-spring</artifactId>
|
||||||
|
<version>1.3.0</version>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 指定 bean 使用
|
||||||
|
|
||||||
|
### 启用分布式锁
|
||||||
|
|
||||||
|
`@EnableLock` 启用分布式锁。
|
||||||
|
|
||||||
|
`@EnableRedisConfig` 启用 redis 的默认配置。
|
||||||
|
|
||||||
|
```xml
|
||||||
|
@Configurable
|
||||||
|
@ComponentScan(basePackages = "com.github.houbb.lock.test.service")
|
||||||
|
@EnableLock
|
||||||
|
@EnableRedisConfig
|
||||||
|
public class SpringConfig {
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`EnableLock` 注解说明,和引导类对应:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public @interface EnableLock {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 唯一标识生成策略
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
String id() default "lockId";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存实现策略 bean 名称
|
||||||
|
*
|
||||||
|
* 默认引入 redis-config 中的配置
|
||||||
|
*
|
||||||
|
* @return 实现
|
||||||
|
*/
|
||||||
|
String cache() default "springRedisService";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加锁 key 格式化策略
|
||||||
|
* @return 策略
|
||||||
|
*/
|
||||||
|
String lockKeyFormat() default "lockKeyFormat";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 锁释放失败处理类
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
String lockReleaseFailHandler() default "lockReleaseFailHandler";
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
其中 `springRedisService` 使用的是 [redis-config](https://github.com/houbb/redis-config) 中的实现。
|
||||||
|
|
||||||
|
对应注解 `@EnableRedisConfig`,redis 的配置信息如下:
|
||||||
|
|
||||||
|
| 配置 | 说明 | 默认值
|
||||||
|
|:---|:---|:----|
|
||||||
|
| redis.address | redis 地址 | 127.0.0.1 |
|
||||||
|
| redis.port | redis 端口 | 6379 |
|
||||||
|
| redis.password | redis 密码 | |
|
||||||
|
|
||||||
|
### 使用 LockBs
|
||||||
|
|
||||||
|
我们可以直接 `LockBs` 的引导类,这种适合一些更加灵活的场景。
|
||||||
|
|
||||||
|
```java
|
||||||
|
@ContextConfiguration(classes = SpringConfig.class)
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
public class SpringServiceRawTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private LockBs lockBs;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void queryLogTest() {
|
||||||
|
final String key = "name";
|
||||||
|
try {
|
||||||
|
lockBs.tryLock(key);
|
||||||
|
final String value = userService.rawUserName(1L);
|
||||||
|
} catch (Exception exception) {
|
||||||
|
throw new RuntimeException(exception);
|
||||||
|
} finally {
|
||||||
|
lockBs.unlock(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## aop 注解使用
|
||||||
|
|
||||||
|
### 指定方法注解
|
||||||
|
|
||||||
|
当然,我们可以在方法上直接指定注解 `@Lock`,使用更加方便。
|
||||||
|
|
||||||
|
直接使用,AOP 切面生效即可。
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Service
|
||||||
|
public class UserService {
|
||||||
|
|
||||||
|
@Lock
|
||||||
|
public String queryUserName(Long userId) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Lock(value = "#user.name")
|
||||||
|
public void queryUserName2(User user) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`@Lock` 属性说明,value 用于指定 key,支持 SPEL 表达式。
|
||||||
|
|
||||||
|
其他属性,和引导类的方法参数一一对应。
|
||||||
|
|
||||||
|
```java
|
||||||
|
public @interface Lock {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存的 key 策略,支持 SpEL
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
String value() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间单位
|
||||||
|
* @return 单位
|
||||||
|
*/
|
||||||
|
TimeUnit timeUnit() default TimeUnit.SECONDS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 等待锁时间
|
||||||
|
* @return 等待锁时间
|
||||||
|
*/
|
||||||
|
long waitLockTime() default 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务加锁时间
|
||||||
|
* @return 加锁时间
|
||||||
|
*/
|
||||||
|
long lockTime() default 60;
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# spring boot 整合
|
||||||
|
|
||||||
|
## maven 引入
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>lock-springboot-starter</artifactId>
|
||||||
|
<version>1.3.0</version>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用
|
||||||
|
|
||||||
|
同 spring
|
||||||
|
|
||||||
# 后期 Road-MAP
|
# 后期 Road-MAP
|
||||||
|
|
||||||
- [ ] 支持锁的可重入
|
- [ ] 支持锁的可重入
|
||||||
|
|
||||||
持有锁的线程可以多次获取锁
|
持有锁的线程可以多次获取锁
|
||||||
|
|
||||||
- [ ] redis 实现的支持
|
- [x] 分布式锁注解支持
|
||||||
|
|
||||||
cluster 支持
|
# 拓展阅读
|
||||||
|
|
||||||
redis 支持
|
[Redis 分布式锁](https://houbb.github.io/2018/09/08/redis-learn-42-distributed-lock-redis)
|
||||||
|
|
||||||
aliyun-redis 支持
|
[java 从零实现 redis 分布式锁](https://houbb.github.io/2018/09/08/redis-learn-43-distributed-lock-redis-java-impl)
|
||||||
|
|
||||||
各种各样的声明方式的默认支持
|
# 缓存相关工具
|
||||||
|
|
||||||
- [ ] 分布式锁注解支持
|
[cache: 手写渐进式 redis](https://github.com/houbb/cache)
|
||||||
|
|
||||||
|
[common-cache: 通用缓存标准定义](https://github.com/houbb/common-cache)
|
||||||
|
|
||||||
|
[redis-config: 兼容各种常见的 redis 配置模式](https://github.com/houbb/redis-config)
|
||||||
|
|
||||||
|
[lock: 开箱即用的分布式锁](https://github.com/houbb/lock)
|
||||||
|
|
||||||
|
[resubmit: 防重复提交](https://github.com/houbb/resubmit)
|
||||||
|
|
||||||
|
[rate-limit: 限流](https://github.com/houbb/rate-limit/)
|
||||||
|
|||||||
@@ -5,11 +5,21 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>lock</artifactId>
|
<artifactId>lock</artifactId>
|
||||||
<groupId>com.github.houbb</groupId>
|
<groupId>com.github.houbb</groupId>
|
||||||
<version>0.0.2</version>
|
<version>1.3.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<artifactId>lock-api</artifactId>
|
<artifactId>lock-api</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>common-cache-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>id-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -1,29 +1,47 @@
|
|||||||
package com.github.houbb.lock.api.core;
|
package com.github.houbb.lock.api.core;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.locks.Lock;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 锁定义
|
* 分布式锁接口定义
|
||||||
* @author binbin.hou
|
* @author binbin.hou
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
public interface ILock extends Lock {
|
public interface ILock {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 尝试加锁
|
* 尝试加锁,如果失败,会一直尝试。
|
||||||
* @param time 时间
|
*
|
||||||
* @param unit 当为
|
|
||||||
* @param key key
|
* @param key key
|
||||||
|
* @param timeUnit 时间单位
|
||||||
|
* @param waitLockTime 等待锁时间
|
||||||
|
* @param lockTime 加锁时间
|
||||||
* @return 返回
|
* @return 返回
|
||||||
* @throws InterruptedException 异常
|
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
boolean tryLock(long time, TimeUnit unit,
|
boolean tryLock(String key, TimeUnit timeUnit, long lockTime, long waitLockTime);
|
||||||
String key) throws InterruptedException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 尝试加锁
|
* 尝试加锁,只加锁一次
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 尝试加锁,只加锁一次
|
||||||
* @param key key
|
* @param key key
|
||||||
* @return 返回
|
* @return 返回
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
@@ -34,7 +52,8 @@ public interface ILock extends Lock {
|
|||||||
* 解锁
|
* 解锁
|
||||||
* @param key key
|
* @param key key
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
|
* @return 是否释放锁成功
|
||||||
*/
|
*/
|
||||||
void unlock(String key);
|
boolean unlock(String key);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.github.houbb.lock.api.core;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分布式锁接口定义
|
||||||
|
* @author binbin.hou
|
||||||
|
* @since 0.0.1
|
||||||
|
*/
|
||||||
|
public interface ILockSupport {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 尝试加锁,如果失败,会一直尝试。
|
||||||
|
*
|
||||||
|
* 1. 如果等待时间小于等于0,则直接执行加锁
|
||||||
|
* 2. 如果等待时间大于等于0,则尝试循环等待。
|
||||||
|
*
|
||||||
|
* @param context 上下文
|
||||||
|
* @return 返回
|
||||||
|
* @since 0.0.1
|
||||||
|
*/
|
||||||
|
boolean tryLock(final ILockSupportContext context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解锁
|
||||||
|
* @param context 上下文
|
||||||
|
* @since 0.0.1
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
boolean unlock(final ILockSupportContext context);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
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
|
||||||
|
* @since 0.0.1
|
||||||
|
*/
|
||||||
|
public interface ILockSupportContext {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标识
|
||||||
|
*
|
||||||
|
* @return 标识策略
|
||||||
|
* @since 0.0.4
|
||||||
|
*/
|
||||||
|
Id id();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存
|
||||||
|
* @return 缓存策略
|
||||||
|
* @since 0.0.4
|
||||||
|
*/
|
||||||
|
ICommonCacheService cache();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间单位
|
||||||
|
* @return 时间单位
|
||||||
|
*/
|
||||||
|
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();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package com.github.houbb.lock.api.core;
|
|
||||||
|
|
||||||
import java.util.concurrent.locks.ReadWriteLock;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 读写锁定义
|
|
||||||
* @author binbin.hou
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
public interface IReadWriteLock extends ReadWriteLock {
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.github.houbb.lock.api.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加锁运行时异常
|
||||||
|
* @since 1.0.0
|
||||||
|
* @author dh
|
||||||
|
*/
|
||||||
|
public class LockException extends RuntimeException {
|
||||||
|
|
||||||
|
public LockException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||||
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>lock</artifactId>
|
<artifactId>lock</artifactId>
|
||||||
<groupId>com.github.houbb</groupId>
|
<groupId>com.github.houbb</groupId>
|
||||||
<version>0.0.2</version>
|
<version>1.3.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
@@ -36,12 +36,16 @@
|
|||||||
<groupId>com.github.houbb</groupId>
|
<groupId>com.github.houbb</groupId>
|
||||||
<artifactId>log-integration</artifactId>
|
<artifactId>log-integration</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!--============================== OTHER ==============================-->
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>redis.clients</groupId>
|
<groupId>com.github.houbb</groupId>
|
||||||
<artifactId>jedis</artifactId>
|
<artifactId>common-cache-core</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>redis-config-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
package com.github.houbb.lock.core.bs;
|
||||||
|
|
||||||
|
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.*;
|
||||||
|
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;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 锁引导类
|
||||||
|
*
|
||||||
|
* @author binbin.hou
|
||||||
|
* @since 0.0.4
|
||||||
|
*/
|
||||||
|
public final class LockBs implements ILock {
|
||||||
|
|
||||||
|
private LockBs() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LockBs newInstance() {
|
||||||
|
return new LockBs();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标识策略
|
||||||
|
*
|
||||||
|
* @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();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 锁 key 格式化
|
||||||
|
*
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
private ILockKeyFormat lockKeyFormat = new LockKeyFormat();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 锁释放失败处理类
|
||||||
|
*
|
||||||
|
* @since 1.2.0
|
||||||
|
*/
|
||||||
|
private ILockReleaseFailHandler lockReleaseFailHandler = new LockReleaseFailHandler();
|
||||||
|
|
||||||
|
public LockBs id(Id id) {
|
||||||
|
ArgUtil.notNull(id, "id");
|
||||||
|
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockBs cache(ICommonCacheService cache) {
|
||||||
|
ArgUtil.notNull(cache, "cache");
|
||||||
|
|
||||||
|
this.cache = cache;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockBs lockSupport(ILockSupport lockSupport) {
|
||||||
|
ArgUtil.notNull(lockSupport, "lockSupport");
|
||||||
|
|
||||||
|
this.lockSupport = lockSupport;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockBs lockKeyFormat(ILockKeyFormat lockKeyFormat) {
|
||||||
|
ArgUtil.notNull(lockKeyFormat, "lockKeyFormat");
|
||||||
|
|
||||||
|
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(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean tryLock(String key, long lockTime) {
|
||||||
|
return this.tryLock(key, TimeUnit.SECONDS, lockTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean tryLock(String key) {
|
||||||
|
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");
|
||||||
|
ArgUtil.notNull(timeUnit, "timeUnit");
|
||||||
|
|
||||||
|
ILockSupportContext context = LockSupportContext.newInstance().id(id).cache(cache).lockKeyFormat(lockKeyFormat).lockReleaseFailHandler(lockReleaseFailHandler).key(key).timeUnit(timeUnit).lockTime(lockTime).waitLockTime(waitLockTime);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.github.houbb.lock.core.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用锁常量
|
||||||
|
*
|
||||||
|
* @author binbin.hou
|
||||||
|
* @since 0.0.3
|
||||||
|
*/
|
||||||
|
public final class LockConst {
|
||||||
|
|
||||||
|
private LockConst() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认的失效时间
|
||||||
|
*
|
||||||
|
* 暂时定为 1min
|
||||||
|
* @since 0.0.1
|
||||||
|
*/
|
||||||
|
public static final int DEFAULT_EXPIRE_MILLS = 1000 * 60;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认锁为全局锁
|
||||||
|
* @since 0.0.1
|
||||||
|
*/
|
||||||
|
public static final String DEFAULT_KEY = "GLOBAL";
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
package com.github.houbb.lock.core;
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分布式锁接口定义
|
||||||
|
* @author binbin.hou
|
||||||
|
* @since 0.0.1
|
||||||
|
*/
|
||||||
|
public class LockSupportContext implements ILockSupportContext {
|
||||||
|
|
||||||
|
public static LockSupportContext newInstance() {
|
||||||
|
return new LockSupportContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标识策略
|
||||||
|
* @since 0.0.4
|
||||||
|
*/
|
||||||
|
private Id id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存策略
|
||||||
|
* @since 0.0.4
|
||||||
|
*/
|
||||||
|
private ICommonCacheService cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间单位
|
||||||
|
*/
|
||||||
|
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() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockSupportContext id(Id id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ICommonCacheService cache() {
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockSupportContext cache(ICommonCacheService cache) {
|
||||||
|
this.cache = cache;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TimeUnit timeUnit() {
|
||||||
|
return timeUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分布式锁接口定义
|
||||||
|
* @author binbin.hou
|
||||||
|
* @since 0.0.1
|
||||||
|
*/
|
||||||
|
public class RedisLockSupport implements ILockSupport {
|
||||||
|
|
||||||
|
private final Log log = LogFactory.getLog(RedisLockSupport.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean tryLock(ILockSupportContext context) {
|
||||||
|
long startTimeMills = System.currentTimeMillis();
|
||||||
|
|
||||||
|
// 一次获取,直接成功
|
||||||
|
boolean result = this.doLock(context);
|
||||||
|
if(result) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 时间判断
|
||||||
|
final TimeUnit timeUnit = context.timeUnit();
|
||||||
|
final long waitLockTime = context.waitLockTime();
|
||||||
|
if(waitLockTime <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
long durationMills = timeUnit.toMillis(waitLockTime);
|
||||||
|
long endMills = startTimeMills + durationMills;
|
||||||
|
|
||||||
|
// 循环等待
|
||||||
|
while (System.currentTimeMillis() < endMills) {
|
||||||
|
result = this.doLock(context);
|
||||||
|
if(result) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待 1ms
|
||||||
|
try {
|
||||||
|
TimeUnit.MILLISECONDS.sleep(1);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
log.debug("[LOCK] try lock wait 1 mills.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行加锁
|
||||||
|
* @param context 上下文
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
protected boolean doLock(ILockSupportContext context) {
|
||||||
|
final String key = this.getActualKey(context);
|
||||||
|
|
||||||
|
// 生成当前线程的唯一标识
|
||||||
|
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 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: {}, lockExpireMills: {}, result: {}", key, requestId, lockExpireMills, result);
|
||||||
|
return JedisConst.OK.equalsIgnoreCase(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
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));
|
||||||
|
log.info("[LOCK] END UN LOCK key: {}, requestId: {}, result: {}", key, requestId, result);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
/**
|
||||||
|
* @author binbin.hou
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
package com.github.houbb.lock.core.support;
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
package com.github.houbb.lock.redis.bs;
|
|
||||||
|
|
||||||
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.redis.core.LockRedis;
|
|
||||||
import com.github.houbb.lock.redis.support.operator.IOperator;
|
|
||||||
import com.github.houbb.wait.api.IWait;
|
|
||||||
import com.github.houbb.wait.core.Waits;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 引导类
|
|
||||||
* @author binbin.hou
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
public final class LockRedisBs {
|
|
||||||
|
|
||||||
private LockRedisBs(){}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 等待实现
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
private IWait wait = Waits.threadSleep();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 等待实现
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
private Id id = Ids.uuid32();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 操作类
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
private IOperator operator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新建对象实例
|
|
||||||
* @return 对象实例
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
public static LockRedisBs newInstance() {
|
|
||||||
return new LockRedisBs();
|
|
||||||
}
|
|
||||||
|
|
||||||
public LockRedisBs wait(IWait wait) {
|
|
||||||
this.wait = wait;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LockRedisBs id(Id id) {
|
|
||||||
this.id = id;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LockRedisBs operator(IOperator operator) {
|
|
||||||
this.operator = operator;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建锁
|
|
||||||
* @return 锁
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
public ILock lock() {
|
|
||||||
return new LockRedis(wait, operator, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
package com.github.houbb.lock.redis.constant;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* redis 锁常量
|
|
||||||
*
|
|
||||||
* @author binbin.hou
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
public final class LockRedisConst {
|
|
||||||
|
|
||||||
private LockRedisConst() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 加锁成功
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
public static final String LOCK_SUCCESS = "OK";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 如果不存在则设置值
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
public static final String SET_IF_NOT_EXIST = "NX";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置过期时间
|
|
||||||
*
|
|
||||||
* 单位:milliseconds
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
public static final String SET_WITH_EXPIRE_TIME = "PX";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解锁成功
|
|
||||||
*
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
public static final Long RELEASE_SUCCESS = 1L;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 默认的失效时间
|
|
||||||
*
|
|
||||||
* 暂时定为 30min
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
public static final int DEFAULT_EXPIRE_MILLS = 1000 * 60 * 30;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 默认锁为全局锁
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
public static final String DEFAULT_KEY = "GLOBAL";
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
package com.github.houbb.lock.redis.core;
|
|
||||||
|
|
||||||
import com.github.houbb.lock.api.core.ILock;
|
|
||||||
import com.github.houbb.lock.redis.constant.LockRedisConst;
|
|
||||||
import com.github.houbb.wait.api.IWait;
|
|
||||||
import com.github.houbb.wait.core.Waits;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.locks.Condition;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 抽象实现
|
|
||||||
* @author binbin.hou
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
public abstract class AbstractLock implements ILock {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 锁等待
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
private final IWait wait;
|
|
||||||
|
|
||||||
public AbstractLock() {
|
|
||||||
this.wait = Waits.threadSleep();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected AbstractLock(IWait wait) {
|
|
||||||
this.wait = wait;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void lock() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void lockInterruptibly() throws InterruptedException {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean tryLock() {
|
|
||||||
return tryLock(LockRedisConst.DEFAULT_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unlock() {
|
|
||||||
unlock(LockRedisConst.DEFAULT_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean tryLock(long time, TimeUnit unit, String key) throws InterruptedException {
|
|
||||||
long startTimeMills = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// 一次获取,直接成功
|
|
||||||
boolean result = this.tryLock(key);
|
|
||||||
if(result) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 时间判断
|
|
||||||
if(time <= 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
long durationMills = unit.toMillis(time);
|
|
||||||
long endMills = startTimeMills + durationMills;
|
|
||||||
|
|
||||||
// 循环等待
|
|
||||||
while (System.currentTimeMillis() < endMills) {
|
|
||||||
result = tryLock(key);
|
|
||||||
if(result) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 等待 1ms
|
|
||||||
wait.wait(TimeUnit.MILLISECONDS, 1);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
|
|
||||||
return tryLock(time, unit, LockRedisConst.DEFAULT_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Condition newCondition() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
package com.github.houbb.lock.redis.core;
|
|
||||||
|
|
||||||
import com.github.houbb.log.integration.core.Log;
|
|
||||||
import com.github.houbb.log.integration.core.LogFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 读写锁实现
|
|
||||||
*
|
|
||||||
* @author binbin.hou
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
public class LockReadWrite {
|
|
||||||
|
|
||||||
private static final Log log = LogFactory.getLog(LockReadWrite.class);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 读次数统计
|
|
||||||
*/
|
|
||||||
private int readCount = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 写次数统计
|
|
||||||
*/
|
|
||||||
private int writeCount = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取读锁,读锁在写锁不存在的时候才能获取
|
|
||||||
*
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
public synchronized void lockRead() throws InterruptedException {
|
|
||||||
// 写锁存在,需要wait
|
|
||||||
while (!tryLockRead()) {
|
|
||||||
wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
readCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 尝试获取读锁
|
|
||||||
*
|
|
||||||
* @return 是否成功
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
private boolean tryLockRead() {
|
|
||||||
if (writeCount > 0) {
|
|
||||||
log.debug("当前有写锁,获取读锁失败");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 释放读锁
|
|
||||||
*
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
public synchronized void unlockRead() {
|
|
||||||
readCount--;
|
|
||||||
notifyAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取写锁
|
|
||||||
*
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
public synchronized void lockWrite() throws InterruptedException {
|
|
||||||
// 写锁存在,需要wait
|
|
||||||
while (!tryLockWrite()) {
|
|
||||||
wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 此时已经不存在获取写锁的线程了,因此占坑,防止写锁饥饿
|
|
||||||
writeCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 尝试获取写锁
|
|
||||||
*
|
|
||||||
* @return 是否成功
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
private boolean tryLockWrite() {
|
|
||||||
if (writeCount > 0) {
|
|
||||||
log.debug("当前有其他写锁,获取写锁失败");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 读锁
|
|
||||||
if (readCount > 0) {
|
|
||||||
log.debug("当前有其他读锁,获取写锁失败。");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 释放写锁
|
|
||||||
*
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
public synchronized void unlockWrite() {
|
|
||||||
writeCount--;
|
|
||||||
notifyAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
package com.github.houbb.lock.redis.core;
|
|
||||||
|
|
||||||
import com.github.houbb.log.integration.core.Log;
|
|
||||||
import com.github.houbb.log.integration.core.LogFactory;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 读写锁实现-保证释放锁时为锁的持有者
|
|
||||||
*
|
|
||||||
* @author binbin.hou
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
public class LockReadWriteOwner {
|
|
||||||
|
|
||||||
private static final Log log = LogFactory.getLog(LockReadWriteOwner.class);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 如果使用类似 write 的方式,会导致读锁只能有一个。
|
|
||||||
* 调整为使用 HashMap 存放读的信息
|
|
||||||
*
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
private final Map<Thread, Integer> readCountMap = new HashMap<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* volatile 引用,保证线程间的可见性+易变性
|
|
||||||
*
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
private final AtomicReference<Thread> writeOwner = new AtomicReference<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 写次数统计
|
|
||||||
*/
|
|
||||||
private int writeCount = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取读锁,读锁在写锁不存在的时候才能获取
|
|
||||||
*
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
public synchronized void lockRead() throws InterruptedException {
|
|
||||||
// 写锁存在,需要wait
|
|
||||||
while (!tryLockRead()) {
|
|
||||||
log.debug("获取读锁失败,进入等待状态。");
|
|
||||||
wait();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 尝试获取读锁
|
|
||||||
*
|
|
||||||
* 读锁之间是不互斥的,这里后续需要优化。
|
|
||||||
*
|
|
||||||
* @return 是否成功
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
private boolean tryLockRead() {
|
|
||||||
if (writeCount > 0) {
|
|
||||||
log.debug("当前有写锁,获取读锁失败");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread currentThread = Thread.currentThread();
|
|
||||||
// 次数暂时固定为1,后面如果实现可重入,这里可以改进。
|
|
||||||
this.readCountMap.put(currentThread, 1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 释放读锁
|
|
||||||
*
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
public synchronized void unlockRead() {
|
|
||||||
Thread currentThread = Thread.currentThread();
|
|
||||||
Integer readCount = readCountMap.get(currentThread);
|
|
||||||
|
|
||||||
if (readCount == null) {
|
|
||||||
throw new RuntimeException("当前线程未持有任何读锁,释放锁失败!");
|
|
||||||
} else {
|
|
||||||
log.debug("释放读锁,唤醒所有等待线程。");
|
|
||||||
notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取写锁
|
|
||||||
*
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
public synchronized void lockWrite() throws InterruptedException {
|
|
||||||
// 写锁存在,需要wait
|
|
||||||
while (!tryLockWrite()) {
|
|
||||||
wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 此时已经不存在获取写锁的线程了,因此占坑,防止写锁饥饿
|
|
||||||
writeCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 尝试获取写锁
|
|
||||||
*
|
|
||||||
* @return 是否成功
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
private boolean tryLockWrite() {
|
|
||||||
if (writeCount > 0) {
|
|
||||||
log.debug("当前有其他写锁,获取写锁失败");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 读锁
|
|
||||||
if (!readCountMap.isEmpty()) {
|
|
||||||
log.debug("当前有其他读锁,获取写锁失败。");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread currentThread = Thread.currentThread();
|
|
||||||
boolean result = writeOwner.compareAndSet(null, currentThread);
|
|
||||||
log.debug("尝试获取写锁结果:{}", result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 释放写锁
|
|
||||||
*
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
public synchronized void unlockWrite() {
|
|
||||||
boolean toNullResult = writeOwner.compareAndSet(Thread.currentThread(), null);
|
|
||||||
|
|
||||||
if (toNullResult) {
|
|
||||||
writeCount--;
|
|
||||||
log.debug("写锁释放,唤醒所有等待线程。");
|
|
||||||
notifyAll();
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException("释放写锁失败");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
package com.github.houbb.lock.redis.core;
|
|
||||||
|
|
||||||
import com.github.houbb.log.integration.core.Log;
|
|
||||||
import com.github.houbb.log.integration.core.LogFactory;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 读写锁实现-可重入锁
|
|
||||||
*
|
|
||||||
* @author binbin.hou
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
public class LockReadWriteRe {
|
|
||||||
|
|
||||||
private static final Log log = LogFactory.getLog(LockReadWriteRe.class);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 如果使用类似 write 的方式,会导致读锁只能有一个。
|
|
||||||
* 调整为使用 HashMap 存放读的信息
|
|
||||||
*
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
private final Map<Thread, Integer> readCountMap = new HashMap<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* volatile 引用,保证线程间的可见性+易变性
|
|
||||||
*
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
private final AtomicReference<Thread> writeOwner = new AtomicReference<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 写次数统计
|
|
||||||
*/
|
|
||||||
private int writeCount = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取读锁,读锁在写锁不存在的时候才能获取
|
|
||||||
*
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
public synchronized void lockRead() throws InterruptedException {
|
|
||||||
// 写锁存在,需要wait
|
|
||||||
while (!tryLockRead()) {
|
|
||||||
log.debug("获取读锁失败,进入等待状态。");
|
|
||||||
wait();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 尝试获取读锁
|
|
||||||
*
|
|
||||||
* 读锁之间是不互斥的,这里后续需要优化。
|
|
||||||
*
|
|
||||||
* @return 是否成功
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
private boolean tryLockRead() {
|
|
||||||
if (writeCount > 0) {
|
|
||||||
log.debug("当前有写锁,获取读锁失败");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread currentThread = Thread.currentThread();
|
|
||||||
Integer count = readCountMap.get(currentThread);
|
|
||||||
if(count == null) {
|
|
||||||
count = 0;
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
|
|
||||||
this.readCountMap.put(currentThread, count);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 释放读锁
|
|
||||||
*
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
public synchronized void unlockRead() {
|
|
||||||
Thread currentThread = Thread.currentThread();
|
|
||||||
Integer readCount = readCountMap.get(currentThread);
|
|
||||||
|
|
||||||
if (readCount == null) {
|
|
||||||
throw new RuntimeException("当前线程未持有任何读锁,释放锁失败!");
|
|
||||||
} else {
|
|
||||||
readCount--;
|
|
||||||
|
|
||||||
// 已经是最后一次
|
|
||||||
if(readCount == 0) {
|
|
||||||
readCountMap.remove(currentThread);
|
|
||||||
} else {
|
|
||||||
readCountMap.put(currentThread, readCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug("释放读锁,唤醒所有等待线程。");
|
|
||||||
notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取写锁
|
|
||||||
*
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
public synchronized void lockWrite() throws InterruptedException {
|
|
||||||
// 写锁存在,需要wait
|
|
||||||
while (!tryLockWrite()) {
|
|
||||||
log.debug("获取写锁失败,进入等待状态。");
|
|
||||||
wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 此时已经不存在获取写锁的线程了,因此占坑,防止写锁饥饿
|
|
||||||
writeCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 尝试获取写锁
|
|
||||||
*
|
|
||||||
* @return 是否成功
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
private boolean tryLockWrite() {
|
|
||||||
if (writeCount > 0) {
|
|
||||||
log.debug("当前有其他写锁,获取写锁失败");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 读锁
|
|
||||||
if (!readCountMap.isEmpty()) {
|
|
||||||
log.debug("当前有其他读锁,获取写锁失败。");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread currentThread = Thread.currentThread();
|
|
||||||
// 多次重入
|
|
||||||
if(writeOwner.get() == currentThread) {
|
|
||||||
log.debug("为当前写线程多次重入,直接返回 true。");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean result = writeOwner.compareAndSet(null, currentThread);
|
|
||||||
log.debug("尝试获取写锁结果:{}", result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 释放写锁
|
|
||||||
*
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
public synchronized void unlockWrite() {
|
|
||||||
Thread currentThread = Thread.currentThread();
|
|
||||||
// 多次重入释放(当次数多于1时直接返回,否则需要释放 owner 信息)
|
|
||||||
if(writeCount > 1 && (currentThread == writeOwner.get())) {
|
|
||||||
log.debug("当前为写锁释放多次重入,直接返回成功。");
|
|
||||||
|
|
||||||
unlockWriteNotify();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean toNullResult = writeOwner.compareAndSet(currentThread, null);
|
|
||||||
if (toNullResult) {
|
|
||||||
unlockWriteNotify();
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException("释放写锁失败");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 释放写锁并且通知
|
|
||||||
*/
|
|
||||||
private void unlockWriteNotify() {
|
|
||||||
writeCount--;
|
|
||||||
log.debug("释放写锁成功,唤醒所有等待线程。");
|
|
||||||
notifyAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
package com.github.houbb.lock.redis.core;
|
|
||||||
|
|
||||||
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.redis.constant.LockRedisConst;
|
|
||||||
import com.github.houbb.lock.redis.exception.LockRedisException;
|
|
||||||
import com.github.houbb.lock.redis.support.operator.IOperator;
|
|
||||||
import com.github.houbb.wait.api.IWait;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 这里是基于 redis 实现
|
|
||||||
*
|
|
||||||
* 实际上也可以基于 zk/数据库等实现。
|
|
||||||
*
|
|
||||||
* @author binbin.hou
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
public class LockRedis extends AbstractLock {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* redis 操作实现
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
private final IOperator redisOperator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 主键标识
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
private final Id id;
|
|
||||||
|
|
||||||
public LockRedis(IWait wait, IOperator redisOperator, Id id) {
|
|
||||||
super(wait);
|
|
||||||
this.redisOperator = redisOperator;
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean tryLock(String key) {
|
|
||||||
final String requestId = id.id();
|
|
||||||
IdThreadLocalHelper.put(requestId);
|
|
||||||
|
|
||||||
return redisOperator.lock(key, requestId, LockRedisConst.DEFAULT_EXPIRE_MILLS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unlock(String key) {
|
|
||||||
final String requestId = IdThreadLocalHelper.get();
|
|
||||||
if(StringUtil.isEmpty(requestId)) {
|
|
||||||
String threadName = Thread.currentThread().getName();
|
|
||||||
throw new LockRedisException("Thread " + threadName +" not contains requestId");
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean unlock = redisOperator.unlock(key, requestId);
|
|
||||||
if(!unlock) {
|
|
||||||
throw new LockRedisException("Unlock key " + key + " result is failed!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package com.github.houbb.lock.redis.core;
|
|
||||||
|
|
||||||
import com.github.houbb.lock.redis.exception.LockRuntimeException;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 自旋锁
|
|
||||||
* @author binbin.hou
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
public class LockSpin extends AbstractLock {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* volatile 引用,保证线程间的可见性+易变性
|
|
||||||
*
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
private AtomicReference<Thread> owner =new AtomicReference<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void lock() {
|
|
||||||
// 循环等待,直到获取到锁
|
|
||||||
while (!tryLock()) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean tryLock(String key) {
|
|
||||||
Thread current = Thread.currentThread();
|
|
||||||
// CAS
|
|
||||||
return owner.compareAndSet(null, current);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unlock(String key) {
|
|
||||||
Thread current = Thread.currentThread();
|
|
||||||
boolean result = owner.compareAndSet(current, null);
|
|
||||||
if(!result) {
|
|
||||||
throw new LockRuntimeException("解锁失败");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
package com.github.houbb.lock.redis.core;
|
|
||||||
|
|
||||||
import com.github.houbb.heaven.util.util.DateUtil;
|
|
||||||
import com.github.houbb.lock.redis.exception.LockRuntimeException;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 自旋锁-可重入
|
|
||||||
* @author binbin.hou
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
public class LockSpinRe extends AbstractLock {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* volatile 引用,保证线程间的可见性+易变性
|
|
||||||
*
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
private AtomicReference<Thread> owner =new AtomicReference<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 计数统计类
|
|
||||||
*
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
private AtomicLong count = new AtomicLong(0);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void lock() {
|
|
||||||
// 循环等待,直到获取到锁
|
|
||||||
while (!tryLock()) {
|
|
||||||
// sleep
|
|
||||||
DateUtil.sleep(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean tryLock(String key) {
|
|
||||||
Thread current = Thread.currentThread();
|
|
||||||
// 判断是否已经拥有此锁
|
|
||||||
if(current == owner.get()) {
|
|
||||||
// 原子性自增 1
|
|
||||||
count.incrementAndGet();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// CAS
|
|
||||||
return owner.compareAndSet(null, current);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unlock(String key) {
|
|
||||||
Thread current = Thread.currentThread();
|
|
||||||
|
|
||||||
// 可重入实现
|
|
||||||
if(owner.get() == current && count.get() != 0) {
|
|
||||||
count.decrementAndGet();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean result = owner.compareAndSet(current, null);
|
|
||||||
if(!result) {
|
|
||||||
throw new LockRuntimeException("解锁失败");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
package com.github.houbb.lock.redis.core;
|
|
||||||
|
|
||||||
import com.github.houbb.lock.redis.exception.LockRuntimeException;
|
|
||||||
import com.github.houbb.log.integration.core.Log;
|
|
||||||
import com.github.houbb.log.integration.core.LogFactory;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 等待通知的锁实现
|
|
||||||
* @author binbin.hou
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
public class LockWaitNotify extends AbstractLock {
|
|
||||||
|
|
||||||
private static final Log log = LogFactory.getLog(LockWaitNotify.class);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* volatile 引用,保证线程间的可见性+易变性
|
|
||||||
*
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
private AtomicReference<Thread> owner =new AtomicReference<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void lock() {
|
|
||||||
while (!tryLock()) {
|
|
||||||
try {
|
|
||||||
log.debug("等待被唤醒");
|
|
||||||
wait();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
// 是否可以被打断
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean tryLock(String key) {
|
|
||||||
Thread current = Thread.currentThread();
|
|
||||||
// CAS
|
|
||||||
boolean result = owner.compareAndSet(null, current);
|
|
||||||
log.debug("尝试获取锁结果:{}", result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void unlock(String key) {
|
|
||||||
Thread current = Thread.currentThread();
|
|
||||||
boolean result = owner.compareAndSet(current, null);
|
|
||||||
if(!result) {
|
|
||||||
throw new LockRuntimeException("解锁失败");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 唤醒等待中的线程
|
|
||||||
log.debug("唤醒等待的进程");
|
|
||||||
notify();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
package com.github.houbb.lock.redis.core;
|
|
||||||
|
|
||||||
import com.github.houbb.lock.redis.exception.LockRuntimeException;
|
|
||||||
import com.github.houbb.log.integration.core.Log;
|
|
||||||
import com.github.houbb.log.integration.core.LogFactory;
|
|
||||||
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 等待通知的锁实现-可重入
|
|
||||||
* @author binbin.hou
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
public class LockWaitNotifyRe extends AbstractLock {
|
|
||||||
|
|
||||||
private static final Log log = LogFactory.getLog(LockWaitNotifyRe.class);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* volatile 引用,保证线程间的可见性+易变性
|
|
||||||
*
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
private AtomicReference<Thread> owner =new AtomicReference<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 次数统计
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
private AtomicInteger count = new AtomicInteger(0);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void lock() {
|
|
||||||
while (!tryLock()) {
|
|
||||||
try {
|
|
||||||
log.debug("等待被唤醒");
|
|
||||||
wait();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
// 是否可以被打断
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean tryLock(String key) {
|
|
||||||
Thread current = Thread.currentThread();
|
|
||||||
|
|
||||||
//可重入实现
|
|
||||||
if(current == owner.get()) {
|
|
||||||
count.incrementAndGet();
|
|
||||||
log.debug("当前线程已经拥有锁,直接返回 true");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// CAS
|
|
||||||
boolean result = owner.compareAndSet(null, current);
|
|
||||||
log.debug("尝试获取锁结果:{}", result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void unlock(String key) {
|
|
||||||
Thread current = Thread.currentThread();
|
|
||||||
|
|
||||||
// 可重入实现
|
|
||||||
if(owner.get() == current && count.get() != 0) {
|
|
||||||
count.decrementAndGet();
|
|
||||||
notifyAndLog();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean result = owner.compareAndSet(current, null);
|
|
||||||
if(!result) {
|
|
||||||
throw new LockRuntimeException("解锁失败");
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyAndLog();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyAndLog() {
|
|
||||||
// 唤醒等待中的线程
|
|
||||||
log.debug("唤醒等待的进程");
|
|
||||||
notify();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package com.github.houbb.lock.redis.exception;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author binbin.hou
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
public class LockRedisException extends RuntimeException {
|
|
||||||
|
|
||||||
public LockRedisException() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public LockRedisException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public LockRedisException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public LockRedisException(Throwable cause) {
|
|
||||||
super(cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public LockRedisException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
|
||||||
super(message, cause, enableSuppression, writableStackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package com.github.houbb.lock.redis.exception;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author binbin.hou
|
|
||||||
* @since 0.0.2
|
|
||||||
*/
|
|
||||||
public class LockRuntimeException extends RuntimeException {
|
|
||||||
|
|
||||||
public LockRuntimeException() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public LockRuntimeException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public LockRuntimeException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public LockRuntimeException(Throwable cause) {
|
|
||||||
super(cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public LockRuntimeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
|
||||||
super(message, cause, enableSuppression, writableStackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
package com.github.houbb.lock.redis;
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package com.github.houbb.lock.redis.support.operator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redis 客户端
|
|
||||||
* @author binbin.hou
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
public interface IOperator {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 尝试获取分布式锁
|
|
||||||
*
|
|
||||||
* @param lockKey 锁
|
|
||||||
* @param requestId 请求标识
|
|
||||||
* @param expireTimeMills 超期时间
|
|
||||||
* @return 是否获取成功
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
boolean lock(String lockKey, String requestId, int expireTimeMills);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解锁
|
|
||||||
* @param lockKey 锁 key
|
|
||||||
* @param requestId 请求标识
|
|
||||||
* @return 结果
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
boolean unlock(String lockKey, String requestId);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
package com.github.houbb.lock.redis.support.operator.impl;
|
|
||||||
|
|
||||||
import com.github.houbb.lock.redis.constant.LockRedisConst;
|
|
||||||
import com.github.houbb.lock.redis.support.operator.IOperator;
|
|
||||||
import redis.clients.jedis.Jedis;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redis 客户端
|
|
||||||
* @author binbin.hou
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
public class JedisOperator implements IOperator {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* jedis 客户端
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
private final Jedis jedis;
|
|
||||||
|
|
||||||
public JedisOperator(Jedis jedis) {
|
|
||||||
this.jedis = jedis;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 尝试获取分布式锁
|
|
||||||
*
|
|
||||||
* expireTimeMills 保证当前进程挂掉,也能释放锁
|
|
||||||
*
|
|
||||||
* requestId 保证解锁的是当前进程(锁的持有者)
|
|
||||||
*
|
|
||||||
* @param lockKey 锁
|
|
||||||
* @param requestId 请求标识
|
|
||||||
* @param expireTimeMills 超期时间
|
|
||||||
* @return 是否获取成功
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean lock(String lockKey, String requestId, int expireTimeMills) {
|
|
||||||
String result = jedis.set(lockKey, requestId, LockRedisConst.SET_IF_NOT_EXIST, LockRedisConst.SET_WITH_EXPIRE_TIME, expireTimeMills);
|
|
||||||
return LockRedisConst.LOCK_SUCCESS.equals(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解锁
|
|
||||||
*
|
|
||||||
* (1)使用 requestId,保证为当前锁的持有者
|
|
||||||
* (2)使用 lua 脚本,保证执行的原子性。
|
|
||||||
*
|
|
||||||
* @param lockKey 锁 key
|
|
||||||
* @param requestId 请求标识
|
|
||||||
* @return 结果
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean unlock(String lockKey, String requestId) {
|
|
||||||
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
|
|
||||||
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
|
|
||||||
return LockRedisConst.RELEASE_SUCCESS.equals(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
package com.github.houbb.lock.redis.support;
|
|
||||||
35
lock-spring/pom.xml
Normal file
35
lock-spring/pom.xml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>lock</artifactId>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<version>1.3.0</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>lock-spring</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>lock-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>aop-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>aop-spring</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>redis-config-spring</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package com.github.houbb.lock.spring.annotation;
|
||||||
|
|
||||||
|
import com.github.houbb.lock.spring.config.LockAopConfig;
|
||||||
|
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启用注解
|
||||||
|
* @author binbin.hou
|
||||||
|
* @since 0.0.2
|
||||||
|
*/
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Import(LockAopConfig.class)
|
||||||
|
@EnableAspectJAutoProxy
|
||||||
|
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";
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.github.houbb.lock.spring.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分布式加锁注解
|
||||||
|
* @author binbin.hou
|
||||||
|
* @since 0.0.2
|
||||||
|
*/
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Inherited
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
package com.github.houbb.lock.spring.aop;
|
||||||
|
|
||||||
|
import com.github.houbb.aop.spring.util.SpringAopUtil;
|
||||||
|
import com.github.houbb.heaven.util.lang.ObjectUtil;
|
||||||
|
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;
|
||||||
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
|
import org.aspectj.lang.Signature;
|
||||||
|
import org.aspectj.lang.annotation.Around;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.aspectj.lang.annotation.Pointcut;
|
||||||
|
import org.aspectj.lang.reflect.MethodSignature;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.expression.EvaluationContext;
|
||||||
|
import org.springframework.expression.ExpressionParser;
|
||||||
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
|
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author binbin.hou
|
||||||
|
* @since 1.1.0
|
||||||
|
*/
|
||||||
|
@Aspect
|
||||||
|
@Component
|
||||||
|
public class LockAspect {
|
||||||
|
|
||||||
|
private final Log log = LogFactory.getLog(LockAspect.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("lockBs")
|
||||||
|
private LockBs lockBs;
|
||||||
|
|
||||||
|
@Pointcut("@annotation(com.github.houbb.lock.spring.annotation.Lock)")
|
||||||
|
public void lockPointcut() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行核心方法
|
||||||
|
*
|
||||||
|
* 相当于 MethodInterceptor
|
||||||
|
* @param point 切点
|
||||||
|
* @return 结果
|
||||||
|
* @throws Throwable 异常信息
|
||||||
|
* @since 0.0.2
|
||||||
|
*/
|
||||||
|
@Around("lockPointcut()")
|
||||||
|
public Object around(ProceedingJoinPoint point) throws Throwable {
|
||||||
|
Method method = SpringAopUtil.getCurrentMethod(point);
|
||||||
|
|
||||||
|
boolean isLockAnnotation = method.isAnnotationPresent(Lock.class);
|
||||||
|
if(isLockAnnotation) {
|
||||||
|
Lock lockAnnotation = method.getAnnotation(Lock.class);
|
||||||
|
|
||||||
|
// 如果构建 key?
|
||||||
|
String lockKey = buildLockKey(lockAnnotation, point);
|
||||||
|
|
||||||
|
boolean tryLockFlag = false;
|
||||||
|
try {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行业务
|
||||||
|
return point.proceed();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
// 只有获取锁的情况下,才尝试释放锁
|
||||||
|
if(tryLockFlag) {
|
||||||
|
boolean unLockFlag = lockBs.unlock(lockKey);
|
||||||
|
|
||||||
|
// 释放锁结果
|
||||||
|
this.lockReleaseFailHandle(unLockFlag, lockKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return point.proceed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 锁释放失败
|
||||||
|
* @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
|
||||||
|
*
|
||||||
|
* https://www.cnblogs.com/fashflying/p/6908028.html spring cache
|
||||||
|
*
|
||||||
|
* https://www.cnblogs.com/best/p/5748105.html SpEL
|
||||||
|
* @param lock 注解信息
|
||||||
|
* @param joinPoint 参数
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
private String buildLockKey(Lock lock,
|
||||||
|
ProceedingJoinPoint joinPoint) {
|
||||||
|
final Object[] args = joinPoint.getArgs();
|
||||||
|
final String lockValue = lock.value();
|
||||||
|
|
||||||
|
//创建SpEL表达式的解析器
|
||||||
|
ExpressionParser parser = new SpelExpressionParser();
|
||||||
|
//1. 如果没有入参怎么办?
|
||||||
|
if(ArrayUtil.isEmpty(args)) {
|
||||||
|
log.warn("[LOCK] method args is empty, return lock.value() {}", lockValue);
|
||||||
|
return lockValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果 lockValue 的值为空呢?
|
||||||
|
if(StringUtil.isEmpty(lockValue)) {
|
||||||
|
return Arrays.toString(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如何获取参数名称呢?
|
||||||
|
|
||||||
|
//解析表达式需要的上下文,解析时有一个默认的上下文
|
||||||
|
// jdk1.7 之前,直接使用 arg0, arg1...
|
||||||
|
EvaluationContext ctx = new StandardEvaluationContext();
|
||||||
|
|
||||||
|
// 利用 spring 的处理方式
|
||||||
|
Signature signature = joinPoint.getSignature();
|
||||||
|
MethodSignature methodSignature = (MethodSignature) signature;
|
||||||
|
String[] paramNameList = methodSignature.getParameterNames();
|
||||||
|
|
||||||
|
for(int i = 0; i < paramNameList.length; i++) {
|
||||||
|
String paramName = paramNameList[i];
|
||||||
|
Object paramValue = args[i];
|
||||||
|
|
||||||
|
//在上下文中设置变量,变量名为user,内容为user对象
|
||||||
|
ctx.setVariable(paramName, paramValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 直接 toString,比较简单。
|
||||||
|
Object value = parser.parseExpression(lockValue).getValue(ctx);
|
||||||
|
return ObjectUtil.objectToString(value, "null");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package com.github.houbb.lock.spring.config;
|
||||||
|
|
||||||
|
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.core.bs.LockBs;
|
||||||
|
import com.github.houbb.lock.spring.annotation.EnableLock;
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||||
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
|
import org.springframework.context.annotation.*;
|
||||||
|
import org.springframework.core.annotation.AnnotationAttributes;
|
||||||
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* aop 配置
|
||||||
|
*
|
||||||
|
* @author binbin.hou
|
||||||
|
* @since 0.0.2
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ComponentScan(basePackages = "com.github.houbb.lock.spring")
|
||||||
|
@Import({LockBeanConfig.class})
|
||||||
|
public class LockAopConfig implements ImportAware, BeanFactoryPostProcessor {
|
||||||
|
|
||||||
|
@Bean("lockBs")
|
||||||
|
public LockBs lockBs() {
|
||||||
|
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)
|
||||||
|
.lockKeyFormat(lockKeyFormat)
|
||||||
|
.lockReleaseFailHandler(lockReleaseFailHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 属性信息
|
||||||
|
*/
|
||||||
|
private AnnotationAttributes enableLockAttributes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bean 工厂
|
||||||
|
*
|
||||||
|
* @since 0.0.5
|
||||||
|
*/
|
||||||
|
private ConfigurableListableBeanFactory beanFactory;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||||
|
this.beanFactory = beanFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setImportMetadata(AnnotationMetadata annotationMetadata) {
|
||||||
|
enableLockAttributes = AnnotationAttributes.fromMap(
|
||||||
|
annotationMetadata.getAnnotationAttributes(EnableLock.class.getName(), false));
|
||||||
|
if (enableLockAttributes == null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"@EnableLock is not present on importing class " + annotationMetadata.getClassName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bean 配置
|
||||||
|
*
|
||||||
|
* @author binbin.hou
|
||||||
|
* @since 0.0.2
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ComponentScan(basePackages = "com.github.houbb.lock.spring")
|
||||||
|
public class LockBeanConfig {
|
||||||
|
|
||||||
|
@Bean("lockId")
|
||||||
|
public Id lockId() {
|
||||||
|
return Ids.uuid32();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean("lockKeyFormat")
|
||||||
|
public ILockKeyFormat lockKeyFormat() {
|
||||||
|
return new LockKeyFormat();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean("lockReleaseFailHandler")
|
||||||
|
public ILockReleaseFailHandler lockReleaseFailHandler() {
|
||||||
|
return new LockReleaseFailHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
package com.github.houbb.lock.spring;
|
||||||
31
lock-springboot-starter/pom.xml
Normal file
31
lock-springboot-starter/pom.xml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>lock</artifactId>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<version>1.3.0</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>lock-springboot-starter</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>lock-spring</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>redis-config-springboot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.github.houbb.lock.springboot.starter.config;
|
||||||
|
|
||||||
|
import com.github.houbb.lock.spring.annotation.EnableLock;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分布式锁自动配置
|
||||||
|
* @author binbin.hou
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ConditionalOnClass(LockAutoConfig.class)
|
||||||
|
@EnableLock
|
||||||
|
public class LockAutoConfig {
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
/**
|
||||||
|
* @author d
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
package com.github.houbb.lock.springboot.starter;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.github.houbb.lock.springboot.starter.config.LockAutoConfig
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>lock</artifactId>
|
<artifactId>lock</artifactId>
|
||||||
<groupId>com.github.houbb</groupId>
|
<groupId>com.github.houbb</groupId>
|
||||||
<version>0.0.2</version>
|
<version>1.3.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
@@ -16,11 +16,37 @@
|
|||||||
<groupId>com.github.houbb</groupId>
|
<groupId>com.github.houbb</groupId>
|
||||||
<artifactId>lock-core</artifactId>
|
<artifactId>lock-core</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>lock-spring</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>test-spring</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>slf4j-common</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-core</artifactId>
|
||||||
|
<version>1.1.11</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
<version>1.1.11</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.github.houbb.lock.test.config;
|
||||||
|
|
||||||
|
|
||||||
|
import com.github.houbb.lock.spring.annotation.EnableLock;
|
||||||
|
import com.github.houbb.redis.config.spring.annotation.EnableRedisConfig;
|
||||||
|
import org.springframework.beans.factory.annotation.Configurable;
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author binbin.hou
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
@Configurable
|
||||||
|
@ComponentScan(basePackages = "com.github.houbb.lock.test.service")
|
||||||
|
@EnableLock
|
||||||
|
@EnableRedisConfig
|
||||||
|
public class SpringConfig {
|
||||||
|
}
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
package com.github.houbb.lock.test.core;
|
|
||||||
|
|
||||||
import com.github.houbb.lock.api.core.ILock;
|
|
||||||
import com.github.houbb.lock.redis.core.LockSpin;
|
|
||||||
import com.github.houbb.lock.redis.core.LockSpinRe;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author binbin.hou
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
public class LockSpinReThread implements Runnable {
|
|
||||||
|
|
||||||
private final ILock lock = new LockSpinRe();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
System.out.println("first-lock: " + Thread.currentThread().getId());
|
|
||||||
lock.lock();
|
|
||||||
|
|
||||||
System.out.println("second-lock: " + Thread.currentThread().getId());
|
|
||||||
lock.lock();
|
|
||||||
lock.unlock();
|
|
||||||
System.out.println("second-unlock: " + Thread.currentThread().getId());
|
|
||||||
|
|
||||||
lock.unlock();
|
|
||||||
System.out.println("first-unlock: " + Thread.currentThread().getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
final Runnable runnable = new LockSpinReThread();
|
|
||||||
new Thread(runnable).start();
|
|
||||||
new Thread(runnable).start();
|
|
||||||
new Thread(runnable).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package com.github.houbb.lock.test.core;
|
|
||||||
|
|
||||||
import com.github.houbb.lock.api.core.ILock;
|
|
||||||
import com.github.houbb.lock.redis.core.LockSpin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author binbin.hou
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
public class LockSpinThread implements Runnable {
|
|
||||||
|
|
||||||
private final ILock lock = new LockSpin();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
System.out.println("first-lock: " + Thread.currentThread().getId());
|
|
||||||
lock.lock();
|
|
||||||
|
|
||||||
System.out.println("second-lock: " + Thread.currentThread().getId());
|
|
||||||
lock.lock();
|
|
||||||
lock.unlock();
|
|
||||||
System.out.println("second-unlock: " + Thread.currentThread().getId());
|
|
||||||
|
|
||||||
lock.unlock();
|
|
||||||
System.out.println("first-unlock: " + Thread.currentThread().getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
final Runnable runnable = new LockSpinThread();
|
|
||||||
new Thread(runnable).start();
|
|
||||||
new Thread(runnable).start();
|
|
||||||
new Thread(runnable).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
package com.github.houbb.lock.test.core;
|
|
||||||
|
|
||||||
import com.github.houbb.heaven.util.util.DateUtil;
|
|
||||||
import com.github.houbb.lock.api.core.ILock;
|
|
||||||
import com.github.houbb.lock.redis.core.LockWaitNotify;
|
|
||||||
import com.github.houbb.log.integration.core.Log;
|
|
||||||
import com.github.houbb.log.integration.core.LogFactory;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author binbin.hou
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
public class LockWaitNotifyThread implements Runnable {
|
|
||||||
|
|
||||||
private static final Log log = LogFactory.getLog(LockWaitNotifyThread.class);
|
|
||||||
|
|
||||||
private final ILock lock = new LockWaitNotify();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
log.debug("first lock");
|
|
||||||
|
|
||||||
lock.lock();
|
|
||||||
log.info("执行业务逻辑。");
|
|
||||||
DateUtil.sleep(TimeUnit.SECONDS, 5);
|
|
||||||
lock.unlock();
|
|
||||||
log.debug("first unlock");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
final Runnable runnable = new LockWaitNotifyThread();
|
|
||||||
new Thread(runnable).start();
|
|
||||||
new Thread(runnable).start();
|
|
||||||
new Thread(runnable).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
package com.github.houbb.lock.test.core;
|
|
||||||
|
|
||||||
import com.github.houbb.heaven.util.util.DateUtil;
|
|
||||||
import com.github.houbb.lock.api.core.ILock;
|
|
||||||
import com.github.houbb.lock.redis.core.LockWaitNotify;
|
|
||||||
import com.github.houbb.log.integration.core.Log;
|
|
||||||
import com.github.houbb.log.integration.core.LogFactory;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author binbin.hou
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
public class LockWaitNotifyThread2 implements Runnable {
|
|
||||||
|
|
||||||
private static final Log log = LogFactory.getLog(LockWaitNotifyThread2.class);
|
|
||||||
|
|
||||||
private final ILock lock = new LockWaitNotify();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
log.debug("first lock");
|
|
||||||
|
|
||||||
lock.lock();
|
|
||||||
|
|
||||||
log.debug("second lock");
|
|
||||||
lock.lock();
|
|
||||||
log.info("执行业务逻辑。");
|
|
||||||
DateUtil.sleep(TimeUnit.SECONDS, 5);
|
|
||||||
log.debug("second unlock");
|
|
||||||
lock.unlock();
|
|
||||||
|
|
||||||
lock.unlock();
|
|
||||||
log.debug("first unlock");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
final Runnable runnable = new LockWaitNotifyThread2();
|
|
||||||
new Thread(runnable).start();
|
|
||||||
new Thread(runnable).start();
|
|
||||||
new Thread(runnable).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package com.github.houbb.lock.test.core;
|
|
||||||
|
|
||||||
import com.github.houbb.heaven.util.util.DateUtil;
|
|
||||||
import com.github.houbb.lock.api.core.ILock;
|
|
||||||
import com.github.houbb.lock.redis.core.LockWaitNotify;
|
|
||||||
import com.github.houbb.lock.redis.core.LockWaitNotifyRe;
|
|
||||||
import com.github.houbb.log.integration.core.Log;
|
|
||||||
import com.github.houbb.log.integration.core.LogFactory;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author binbin.hou
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
public class LockWaitNotifyThreadRe implements Runnable {
|
|
||||||
|
|
||||||
private static final Log log = LogFactory.getLog(LockWaitNotifyThreadRe.class);
|
|
||||||
|
|
||||||
private final ILock lock = new LockWaitNotifyRe();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
log.debug("first lock");
|
|
||||||
lock.lock();
|
|
||||||
|
|
||||||
log.debug("second lock");
|
|
||||||
lock.lock();
|
|
||||||
log.debug("second unlock");
|
|
||||||
lock.unlock();
|
|
||||||
|
|
||||||
log.debug("first unlock");
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
final Runnable runnable = new LockWaitNotifyThreadRe();
|
|
||||||
new Thread(runnable).start();
|
|
||||||
new Thread(runnable).start();
|
|
||||||
new Thread(runnable).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.github.houbb.lock.test.model;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "User{" +
|
||||||
|
"name='" + name + '\'' +
|
||||||
|
", id='" + id + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.github.houbb.lock.test.service;
|
||||||
|
|
||||||
|
import com.github.houbb.lock.spring.annotation.Lock;
|
||||||
|
import com.github.houbb.lock.test.model.User;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author binbin.hou
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class UserService {
|
||||||
|
|
||||||
|
public String rawUserName(Long userId) {
|
||||||
|
return userId+"-name";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Lock
|
||||||
|
public String queryUserName(Long userId) {
|
||||||
|
return userId+"-name";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Lock(value = "#user.name")
|
||||||
|
public void queryUserName2(User user) {
|
||||||
|
System.out.println("user: " + user.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package com.github.houbb.lock.test;
|
|
||||||
|
|
||||||
import com.github.houbb.lock.api.core.ILock;
|
|
||||||
import com.github.houbb.lock.redis.bs.LockRedisBs;
|
|
||||||
import com.github.houbb.lock.redis.support.operator.IOperator;
|
|
||||||
import com.github.houbb.lock.redis.support.operator.impl.JedisOperator;
|
|
||||||
import org.junit.Test;
|
|
||||||
import redis.clients.jedis.Jedis;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author binbin.hou
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
public class LockRedisTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void helloTest() {
|
|
||||||
Jedis jedis = new Jedis("127.0.0.1", 6379);
|
|
||||||
IOperator operator = new JedisOperator(jedis);
|
|
||||||
|
|
||||||
// 获取锁
|
|
||||||
ILock lock = LockRedisBs.newInstance().operator(operator).lock();
|
|
||||||
|
|
||||||
try {
|
|
||||||
boolean lockResult = lock.tryLock();
|
|
||||||
System.out.println(lockResult);
|
|
||||||
// 业务处理
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
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();
|
||||||
|
String key = "ddd";
|
||||||
|
try {
|
||||||
|
// 加锁
|
||||||
|
lock.tryLock(key);
|
||||||
|
System.out.println("业务处理");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
// 释放锁
|
||||||
|
lock.unlock(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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()) //释放锁失败处理
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.github.houbb.lock.test;
|
package com.github.houbb.lock.test.redis;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import redis.clients.jedis.Jedis;
|
import redis.clients.jedis.Jedis;
|
||||||
|
|
||||||
@@ -8,6 +9,7 @@ import redis.clients.jedis.Jedis;
|
|||||||
* @author binbin.hou
|
* @author binbin.hou
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
|
@Ignore
|
||||||
public class JedisTest {
|
public class JedisTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
//package com.github.houbb.lock.test.redis;
|
||||||
|
//
|
||||||
|
//import com.github.houbb.lock.api.core.ILock;
|
||||||
|
//import com.github.houbb.lock.api.support.IOperator;
|
||||||
|
//import com.github.houbb.lock.core.bs.LockBs;
|
||||||
|
//import com.github.houbb.lock.redis.support.operator.JedisOperator;
|
||||||
|
//import org.junit.Ignore;
|
||||||
|
//import org.junit.Test;
|
||||||
|
//import redis.clients.jedis.Jedis;
|
||||||
|
//
|
||||||
|
///**
|
||||||
|
// * @author binbin.hou
|
||||||
|
// * @since 0.0.1
|
||||||
|
// */
|
||||||
|
//@Ignore
|
||||||
|
//public class LockRedisTest {
|
||||||
|
//
|
||||||
|
// @Test
|
||||||
|
// public void helloTest() {
|
||||||
|
// Jedis jedis = new Jedis("127.0.0.1", 6379);
|
||||||
|
// IOperator operator = new JedisOperator(jedis);
|
||||||
|
//
|
||||||
|
// // 获取锁
|
||||||
|
// ILock lock = LockBs.newInstance(operator).lock();
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// boolean lockResult = lock.tryLock();
|
||||||
|
// System.out.println(lockResult);
|
||||||
|
// // 业务处理
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// } finally {
|
||||||
|
// lock.unlock();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.github.houbb.lock.test.spring;
|
||||||
|
|
||||||
|
|
||||||
|
import com.github.houbb.lock.core.bs.LockBs;
|
||||||
|
import com.github.houbb.lock.test.config.SpringConfig;
|
||||||
|
import com.github.houbb.lock.test.model.User;
|
||||||
|
import com.github.houbb.lock.test.service.UserService;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author binbin.hou
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
@ContextConfiguration(classes = SpringConfig.class)
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
public class SpringServiceRawTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private LockBs lockBs;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void queryLogTest() {
|
||||||
|
final String key = "name";
|
||||||
|
try {
|
||||||
|
lockBs.tryLock(key);
|
||||||
|
final String value = userService.rawUserName(1L);
|
||||||
|
} catch (Exception exception) {
|
||||||
|
throw new RuntimeException(exception);
|
||||||
|
} finally {
|
||||||
|
lockBs.unlock(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.github.houbb.lock.test.spring;
|
||||||
|
|
||||||
|
|
||||||
|
import com.github.houbb.lock.test.config.SpringConfig;
|
||||||
|
import com.github.houbb.lock.test.model.User;
|
||||||
|
import com.github.houbb.lock.test.service.UserService;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author binbin.hou
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
@ContextConfiguration(classes = SpringConfig.class)
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
public class SpringServiceTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void queryLogTest() {
|
||||||
|
final String key = "name";
|
||||||
|
final String value = userService.queryUserName(1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void queryUserNameTest() {
|
||||||
|
User user = new User();
|
||||||
|
user.setId("001");
|
||||||
|
user.setName("u-001");
|
||||||
|
userService.queryUserName2(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
23
lock-test/src/test/resources/logback.xml
Normal file
23
lock-test/src/test/resources/logback.xml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration debug="false">
|
||||||
|
|
||||||
|
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径 /app/sv/logs -->
|
||||||
|
<!-- 控制台输出 -->
|
||||||
|
<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} [%thread] %-5level %logger{50} - %msg%n</pattern>
|
||||||
|
<charset>utf-8</charset>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!--com.github.houbb 包路径 additivity: 表示是否 想上层传递)-->
|
||||||
|
<logger name="com.github.houbb.lock" level="INFO" additivity="true">
|
||||||
|
</logger>
|
||||||
|
|
||||||
|
<!-- 日志输出级别-->
|
||||||
|
<root level="INFO">
|
||||||
|
<appender-ref ref="STDOUT"/>
|
||||||
|
</root>
|
||||||
|
|
||||||
|
</configuration>
|
||||||
47
lock-test2/pom.xml
Normal file
47
lock-test2/pom.xml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>lock</artifactId>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<version>1.3.0</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>lock-test2</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>lock-springboot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>test-spring</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>slf4j-common</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-core</artifactId>
|
||||||
|
<version>1.1.11</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
<version>1.1.11</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
/**
|
||||||
|
* @author d
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
package com.github.houbb.lock.test2;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.github.houbb.lock.test2.service;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author binbin.hou
|
||||||
|
* @since 0.0.3
|
||||||
|
*/
|
||||||
|
@SpringBootApplication
|
||||||
|
public class MyApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(MyApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.github.houbb.lock.test2.service;
|
||||||
|
|
||||||
|
import com.github.houbb.lock.spring.annotation.Lock;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author binbin.hou
|
||||||
|
* @since 0.0.1
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class UserService {
|
||||||
|
|
||||||
|
@Lock
|
||||||
|
public void queryInfo(final String id) {
|
||||||
|
System.out.println("query info: " + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.github.houbb.lock.test2.service;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author binbin.hou
|
||||||
|
* @since 0.0.2
|
||||||
|
*/
|
||||||
|
@ContextConfiguration(classes = MyApplication.class)
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
public class UserServiceTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserService service;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void queryTest() {
|
||||||
|
service.queryInfo("1");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
/**
|
||||||
|
* @author d
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
package com.github.houbb.lock.test2.service;
|
||||||
80
pom.xml
80
pom.xml
@@ -5,11 +5,14 @@
|
|||||||
<groupId>com.github.houbb</groupId>
|
<groupId>com.github.houbb</groupId>
|
||||||
<artifactId>lock</artifactId>
|
<artifactId>lock</artifactId>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<version>0.0.2</version>
|
<version>1.3.0</version>
|
||||||
<modules>
|
<modules>
|
||||||
<module>lock-api</module>
|
<module>lock-api</module>
|
||||||
<module>lock-core</module>
|
<module>lock-core</module>
|
||||||
<module>lock-test</module>
|
<module>lock-test</module>
|
||||||
|
<module>lock-spring</module>
|
||||||
|
<module>lock-springboot-starter</module>
|
||||||
|
<module>lock-test2</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
@@ -33,14 +36,19 @@
|
|||||||
|
|
||||||
<!--============================== INTER ==============================-->
|
<!--============================== INTER ==============================-->
|
||||||
<plugin.gen.version>1.0.6</plugin.gen.version>
|
<plugin.gen.version>1.0.6</plugin.gen.version>
|
||||||
<heaven.version>0.1.114</heaven.version>
|
|
||||||
|
<heaven.version>0.1.167</heaven.version>
|
||||||
<id.version>0.0.6</id.version>
|
<id.version>0.0.6</id.version>
|
||||||
<wait.version>0.0.1</wait.version>
|
<wait.version>0.0.1</wait.version>
|
||||||
<log-integration.version>1.1.8</log-integration.version>
|
<log-integration.version>1.1.8</log-integration.version>
|
||||||
|
<common-cache.version>0.0.5</common-cache.version>
|
||||||
|
<redis-config.version>1.4.0</redis-config.version>
|
||||||
|
<aop.version>0.0.2</aop.version>
|
||||||
|
<test.version>0.0.1</test.version>
|
||||||
|
|
||||||
<!--============================== OTHER ==============================-->
|
<!--============================== OTHER ==============================-->
|
||||||
<junit.version>4.12</junit.version>
|
<junit.version>4.12</junit.version>
|
||||||
<jedis.version>2.8.1</jedis.version>
|
<spring-boot.version>1.5.22.RELEASE</spring-boot.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
@@ -56,6 +64,16 @@
|
|||||||
<artifactId>lock-core</artifactId>
|
<artifactId>lock-core</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>lock-spring</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>lock-springboot-starter</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!--============================== INTER ==============================-->
|
<!--============================== INTER ==============================-->
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -63,6 +81,11 @@
|
|||||||
<artifactId>heaven</artifactId>
|
<artifactId>heaven</artifactId>
|
||||||
<version>${heaven.version}</version>
|
<version>${heaven.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>id-api</artifactId>
|
||||||
|
<version>${id.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.houbb</groupId>
|
<groupId>com.github.houbb</groupId>
|
||||||
<artifactId>id-core</artifactId>
|
<artifactId>id-core</artifactId>
|
||||||
@@ -79,6 +102,33 @@
|
|||||||
<version>${log-integration.version}</version>
|
<version>${log-integration.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>common-cache-api</artifactId>
|
||||||
|
<version>${common-cache.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>common-cache-core</artifactId>
|
||||||
|
<version>${common-cache.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>redis-config-core</artifactId>
|
||||||
|
<version>${redis-config.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>redis-config-spring</artifactId>
|
||||||
|
<version>${redis-config.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>redis-config-springboot-starter</artifactId>
|
||||||
|
<version>${redis-config.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!--============================== OTHER ==============================-->
|
<!--============================== OTHER ==============================-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
@@ -89,10 +139,28 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>redis.clients</groupId>
|
<groupId>com.github.houbb</groupId>
|
||||||
<artifactId>jedis</artifactId>
|
<artifactId>aop-core</artifactId>
|
||||||
<version>${jedis.version}</version>
|
<version>${aop.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>aop-spring</artifactId>
|
||||||
|
<version>${aop.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.houbb</groupId>
|
||||||
|
<artifactId>test-spring</artifactId>
|
||||||
|
<version>${test.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
<version>${spring-boot.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ ECHO "============================= RELEASE START..."
|
|||||||
|
|
||||||
:: 版本号信息(需要手动指定)
|
:: 版本号信息(需要手动指定)
|
||||||
:::: 旧版本名称
|
:::: 旧版本名称
|
||||||
SET version=0.0.2
|
SET version=1.3.0
|
||||||
:::: 新版本名称
|
:::: 新版本名称
|
||||||
SET newVersion=0.0.3
|
SET newVersion=1.4.0
|
||||||
:::: 组织名称
|
:::: 组织名称
|
||||||
SET groupName=com.github.houbb
|
SET groupName=com.github.houbb
|
||||||
:::: 项目名称
|
:::: 项目名称
|
||||||
|
|||||||
Reference in New Issue
Block a user