25 Commits

Author SHA1 Message Date
老马啸西风
9ef657d0ba Update README.md 2022-12-08 14:19:12 +08:00
binbin.hou
0f9ce0441e [Feature] add for new 2022-12-08 14:02:18 +08:00
binbin.hou
bc1fd8fc71 [Feature] add for new 2022-12-08 13:53:21 +08:00
binbin.hou
4ce823e161 release branch 1.3.0 2022-12-08 13:30:01 +08:00
binbin.hou
62d2a60edc release branch 1.2.0 2022-12-08 11:16:22 +08:00
binbin.hou
0bae77fc8a release branch 1.1.0 2022-12-08 11:15:03 +08:00
binbin.hou
c45f6b53a0 release branch 1.1.0 2022-12-07 21:00:45 +08:00
binbin.hou
27a931ae77 [Feature] add for new 2022-12-07 18:39:00 +08:00
binbin.hou
8bf1df19c2 [Feature] add for new 2022-12-07 16:54:37 +08:00
binbin.hou
c0e3cc4bf2 release branch 1.0.0 2022-12-07 16:43:21 +08:00
binbin.hou
402653393d release branch 0.0.4 2022-12-07 16:40:24 +08:00
binbin.hou
7fd29cd1f2 release branch 0.0.4 2022-04-17 19:16:44 +08:00
binbin.hou
ecd879763a release branch 0.0.3 2021-12-08 10:19:58 +08:00
binbin.hou
d06effa306 [Feature] add for new 2020-11-08 13:07:13 +08:00
houbb
bce83c6d1e [Feature] add for new 2020-10-31 12:53:31 +08:00
houbb
2016605dfd Merge remote-tracking branch 'origin/master' 2020-10-30 23:45:49 +08:00
houbb
d273441a72 [Feature] add for new 2020-10-30 23:45:03 +08:00
binbin.hou
2cc9eccdee [Feature] add for new 2020-10-24 20:21:11 +08:00
binbin.hou
4beac5f889 [Feature] add for new 2020-10-20 13:21:17 +08:00
binbin.hou
629c6b8fce release branch 0.0.2 2020-10-20 12:44:35 +08:00
houbb
56e25d67e3 [Feature] add for new 2020-10-19 23:38:19 +08:00
binbin.hou
67f8b1e2a7 [Feature] add for new 2020-10-19 18:23:20 +08:00
houbb
acbd8f4022 [Feature] add for new 2020-10-17 15:38:19 +08:00
binbin.hou
422ec999ac [Feature] add for new 2020-09-10 16:20:38 +08:00
binbin.hou
71d554ba3b [Feature] add for new 2020-09-10 14:13:29 +08:00
63 changed files with 2073 additions and 486 deletions

View File

@@ -14,3 +14,48 @@
| 序号 | 变更类型 | 说明 | 时间 | 备注 |
|:---|:---|:---|:---|:--|
| 1 | A | 基本 api 定义 | 2020-9-2 14:45:40 | |
# release_0.0.2
| 序号 | 变更类型 | 说明 | 时间 | 备注 |
|:---|:---|:---|:---|:--|
| 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 | |

251
README.md
View File

@@ -1,22 +1,30 @@
# 项目简介
为 java 设计的锁
[lock](https://github.com/houbb/lock) 为 java 设计的分布式锁,开箱即用,纵享丝滑
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.houbb/lock/badge.svg)](http://mvnrepository.com/artifact/com.github.houbb/lock)
[![Build Status](https://www.travis-ci.org/houbb/lock.svg?branch=master)](https://www.travis-ci.org/houbb/lock?branch=master)
[![Coverage Status](https://coveralls.io/repos/github/houbb/lock/badge.svg?branch=master)](https://coveralls.io/github/houbb/lock?branch=master)
开源地址:[https://github.com/houbb/lock](https://github.com/houbb/lock)
## 目的
- 开箱即用,支持注解式和过程式调用
- 基于 redis 的分布式锁
- 基于 oracle 的分布式锁
- 内置支持多种 redis 的整合方式
- 基于 mysql 的分布式锁
- 渐进式设计,可独立于 spring 使用
- 整合 spring
- 整合 spring-boot
# 变更日志
> [变更日志](doc/CHANGELOG.md)
> [变更日志](CHANGELOG.md)
# 快速开始
@@ -32,7 +40,7 @@ maven 3.x+
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>lock-core</artifactId>
<version>${最新版本}</version>
<version>1.3.0</version>
</dependency>
```
@@ -41,36 +49,237 @@ maven 3.x+
基于本地 redis 的测试案例。
```java
Jedis jedis = new Jedis("127.0.0.1", 6379);
IOperator operator = new JedisOperator(jedis);
// 获取锁
ILock lock = LockRedisBs.newInstance().operator(operator).lock();
public void helloTest() {
ILock lock = LockBs.newInstance();
String key = "ddd";
try {
boolean lockResult = lock.tryLock();
System.out.println(lockResult);
// 业务处理
// 加锁
lock.tryLock(key);
System.out.println("业务处理");
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally {
lock.unlock();
// 释放锁
lock.unlock(key);
}
}
```
### 配置化
为了便于拓展LockBs 的配置支持自定义:
```java
LockBs.newInstance()
.id(Ids.uuid32()) //id 生成策略
.cache(JedisRedisServiceFactory.pooled("127.0.0.1", 6379)) //缓存策略
.lockSupport(new RedisLockSupport()) // 锁实现策略
.lockKeyFormat(new LockKeyFormat()) // 针对 key 的格式化处理策略
.lockReleaseFailHandler(new LockReleaseFailHandler()) //释放锁失败处理
;
```
# 整合 spring
## maven 引入
```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
- [ ] 支持锁的可重入
持有锁的线程可以多次获取锁
- [ ] 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/)

View File

@@ -5,11 +5,21 @@
<parent>
<artifactId>lock</artifactId>
<groupId>com.github.houbb</groupId>
<version>0.0.1</version>
<version>1.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<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>

View File

@@ -1,29 +1,47 @@
package com.github.houbb.lock.api.core;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
/**
* 定义
* 分布式锁接口定义
* @author binbin.hou
* @since 0.0.1
*/
public interface ILock extends Lock {
public interface ILock {
/**
* 尝试加锁
* @param time 时间
* @param unit 当为
* 尝试加锁,如果失败,会一直尝试。
*
* @param key key
* @param timeUnit 时间单位
* @param waitLockTime 等待锁时间
* @param lockTime 加锁时间
* @return 返回
* @throws InterruptedException 异常
* @since 0.0.1
*/
boolean tryLock(long time, TimeUnit unit,
String key) throws InterruptedException;
boolean tryLock(String key, TimeUnit timeUnit, long lockTime, long waitLockTime);
/**
* 尝试加锁
* 尝试加锁,只加锁一次
* @param key key
* @param timeUnit 时间单位
* @param lockTime 加锁时间
* @return 返回
* @since 0.0.1
*/
boolean tryLock(String key, TimeUnit timeUnit, long lockTime);
/**
* 尝试加锁,只加锁一次
* @param key key
* @param lockTime 加锁时间
* @return 返回
* @since 0.0.1
*/
boolean tryLock(String key, long lockTime);
/**
* 尝试加锁,只加锁一次
* @param key key
* @return 返回
* @since 0.0.1
@@ -34,7 +52,8 @@ public interface ILock extends Lock {
* 解锁
* @param key key
* @since 0.0.1
* @return 是否释放锁成功
*/
void unlock(String key);
boolean unlock(String key);
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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);
}
}

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>lock</artifactId>
<groupId>com.github.houbb</groupId>
<version>0.0.1</version>
<version>1.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -32,12 +32,20 @@
<groupId>com.github.houbb</groupId>
<artifactId>heaven</artifactId>
</dependency>
<!--============================== OTHER ==============================-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<groupId>com.github.houbb</groupId>
<artifactId>log-integration</artifactId>
</dependency>
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>common-cache-core</artifactId>
</dependency>
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>redis-config-core</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -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;
}
}

View File

@@ -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";
}

View File

@@ -0,0 +1 @@
package com.github.houbb.lock.core;

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,5 @@
/**
* @author binbin.hou
* @since 1.0.0
*/
package com.github.houbb.lock.core.support;

View File

@@ -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);
}
}

View File

@@ -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";
}

View File

@@ -1,87 +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 java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
/**
* 抽象实现
* @author binbin.hou
* @since 0.0.1
*/
public abstract class AbstractLockRedis implements ILock {
/**
* 锁等待
* @since 0.0.1
*/
private final IWait wait;
protected AbstractLockRedis(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;
}
// 等待 10ms
wait.wait(TimeUnit.MILLISECONDS, 10);
}
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();
}
}

View File

@@ -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 AbstractLockRedis {
/**
* 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!");
}
}
}

View File

@@ -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);
}
}

View File

@@ -1 +0,0 @@
package com.github.houbb.lock.redis;

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -1 +0,0 @@
package com.github.houbb.lock.redis.support;

35
lock-spring/pom.xml Normal file
View 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>

View File

@@ -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";
}

View File

@@ -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;
}

View File

@@ -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");
}
}

View File

@@ -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());
}
}
}

View File

@@ -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();
}
}

View File

@@ -0,0 +1 @@
package com.github.houbb.lock.spring;

View 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>

View File

@@ -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 {
}

View File

@@ -0,0 +1,5 @@
/**
* @author d
* @since 1.0.0
*/
package com.github.houbb.lock.springboot.starter;

View File

@@ -0,0 +1 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.github.houbb.lock.springboot.starter.config.LockAutoConfig

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>lock</artifactId>
<groupId>com.github.houbb</groupId>
<version>0.0.1</version>
<version>1.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -16,11 +16,37 @@
<groupId>com.github.houbb</groupId>
<artifactId>lock-core</artifactId>
</dependency>
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>lock-spring</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>

View File

@@ -0,0 +1,18 @@
package com.github.houbb.lock.test;
/**
* @author binbin.hou
* @since 0.0.1
*/
public class SyncTest {
public synchronized void syncOne() {
System.out.println("方法1执行");
syncTwo();
}
public synchronized void syncTwo() {
System.out.println("方法2执行");
}
}

View File

@@ -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 {
}

View File

@@ -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 + '\'' +
'}';
}
}

View File

@@ -0,0 +1 @@
package com.github.houbb.lock.test;

View File

@@ -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());
}
}

View File

@@ -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 RedisLockTest {
@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();
}
}
}

View File

@@ -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()) //释放锁失败处理
;
}
}

View File

@@ -1,6 +1,7 @@
package com.github.houbb.lock.test;
package com.github.houbb.lock.test.redis;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import redis.clients.jedis.Jedis;
@@ -8,6 +9,7 @@ import redis.clients.jedis.Jedis;
* @author binbin.hou
* @since 0.0.1
*/
@Ignore
public class JedisTest {
@Test

View File

@@ -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();
// }
// }
//
//}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View 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
View 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>

View File

@@ -0,0 +1,5 @@
/**
* @author d
* @since 1.0.0
*/
package com.github.houbb.lock.test2;

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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");
}
}

View File

@@ -0,0 +1,5 @@
/**
* @author d
* @since 1.0.0
*/
package com.github.houbb.lock.test2.service;

86
pom.xml
View File

@@ -5,11 +5,14 @@
<groupId>com.github.houbb</groupId>
<artifactId>lock</artifactId>
<packaging>pom</packaging>
<version>0.0.1</version>
<version>1.3.0</version>
<modules>
<module>lock-api</module>
<module>lock-core</module>
<module>lock-test</module>
<module>lock-spring</module>
<module>lock-springboot-starter</module>
<module>lock-test2</module>
</modules>
<properties>
@@ -33,13 +36,19 @@
<!--============================== INTER ==============================-->
<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>
<wait.version>0.0.1</wait.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 ==============================-->
<junit.version>4.12</junit.version>
<jedis.version>2.8.1</jedis.version>
<spring-boot.version>1.5.22.RELEASE</spring-boot.version>
</properties>
<dependencyManagement>
@@ -55,6 +64,16 @@
<artifactId>lock-core</artifactId>
<version>${project.version}</version>
</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 ==============================-->
<dependency>
@@ -62,6 +81,11 @@
<artifactId>heaven</artifactId>
<version>${heaven.version}</version>
</dependency>
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>id-api</artifactId>
<version>${id.version}</version>
</dependency>
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>id-core</artifactId>
@@ -72,6 +96,38 @@
<artifactId>wait</artifactId>
<version>${wait.version}</version>
</dependency>
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>log-integration</artifactId>
<version>${log-integration.version}</version>
</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 ==============================-->
<dependency>
@@ -83,10 +139,28 @@
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${jedis.version}</version>
<groupId>com.github.houbb</groupId>
<artifactId>aop-core</artifactId>
<version>${aop.version}</version>
</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>
</dependencyManagement>

View File

@@ -10,9 +10,9 @@ ECHO "============================= RELEASE START..."
:: 版本号信息(需要手动指定)
:::: 旧版本名称
SET version=0.0.1
SET version=1.3.0
:::: 新版本名称
SET newVersion=0.0.2
SET newVersion=1.4.0
:::: 组织名称
SET groupName=com.github.houbb
:::: 项目名称