3 Commits

Author SHA1 Message Date
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
33 changed files with 718 additions and 171 deletions

View File

@@ -45,3 +45,17 @@
| 序号 | 变更类型 | 说明 | 时间 | 备注 |
|:---|:---|:---|:---|:--|
| 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 | |

147
README.md
View File

@@ -1,6 +1,6 @@
# 项目简介
为 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)
@@ -8,16 +8,16 @@
## 目的
- 开箱即用,支持注解式和过程式调用
- 基于 redis 的分布式锁
- 内置支持多种 redis 的整合方式
- 整合 spring
- 整合 spring-boot
- 开箱即用,支持注解。
- 支持多种 redis 的声明方式
# 变更日志
> [变更日志](CHANGELOG.md)
@@ -36,7 +36,7 @@ maven 3.x+
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>lock-core</artifactId>
<version>1.1.0</version>
<version>1.3.0</version>
</dependency>
```
@@ -45,22 +45,36 @@ maven 3.x+
基于本地 redis 的测试案例。
```java
ILock lock = LockBs.newInstance()
.init();
String key = "ddd";
try {
// 加锁
lock.tryLock(key);
System.out.println("业务处理");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// 释放锁
lock.unlock(key);
public void helloTest() {
ILock lock = LockBs.newInstance();
String key = "ddd";
try {
// 加锁
lock.tryLock(key);
System.out.println("业务处理");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// 释放锁
lock.unlock(key);
}
}
```
### 配置化
为了便于拓展LockBs 的配置支持自定义:
```java
LockBs.newInstance()
.id(Ids.uuid32()) //id 生成策略
.cache(JedisRedisServiceFactory.pooled("127.0.0.1", 6379)) //缓存策略
.lockSupport(new RedisLockSupport()) // 锁实现策略
.lockKeyFormat(new LockKeyFormat()) // 针对 key 的格式化处理策略
.lockReleaseFailHandler(new LockReleaseFailHandler()) //释放锁失败处理
;
```
# 整合 spring
## maven 引入
@@ -69,7 +83,7 @@ try {
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>lock-spring</artifactId>
<version>1.1.0</version>
<version>1.3.0</version>
</dependency>
```
@@ -79,17 +93,65 @@ try {
`@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` 的引导类。
我们可以直接 `LockBs` 的引导类,这种适合一些更加灵活的场景
```java
@ContextConfiguration(classes = SpringConfig.class)
@@ -124,7 +186,7 @@ public class SpringServiceRawTest {
当然,我们可以在方法上直接指定注解 `@Lock`,使用更加方便。
支持 SPEL 表达式
直接使用AOP 切面生效即可
```java
@Service
@@ -140,9 +202,41 @@ public class UserService {
}
```
直接使用AOP 切面生效即可
`@Lock` 属性说明value 用于指定 key支持 SPEL 表达式
# springboot 整合
其他属性,和引导类的方法参数一一对应。
```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 引入
@@ -150,7 +244,7 @@ public class UserService {
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>lock-springboot-starter</artifactId>
<version>1.1.0</version>
<version>1.3.0</version>
</dependency>
```
@@ -164,5 +258,4 @@ public class UserService {
持有锁的线程可以多次获取锁
- [x] 分布式锁注解支持
- [x] 分布式锁注解支持

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>lock</artifactId>
<groupId>com.github.houbb</groupId>
<version>1.1.0</version>
<version>1.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -12,13 +12,33 @@ public interface ILock {
/**
* 尝试加锁,如果失败,会一直尝试。
*
* @param time 时间
* @param unit 当为
* @param key key
* @param timeUnit 时间单位
* @param waitLockTime 等待锁时间
* @param lockTime 加锁时间
* @return 返回
* @since 0.0.1
*/
boolean tryLock(long time, TimeUnit unit, String key);
boolean tryLock(String key, TimeUnit timeUnit, long lockTime, long waitLockTime);
/**
* 尝试加锁,只加锁一次
* @param key key
* @param timeUnit 时间单位
* @param lockTime 加锁时间
* @return 返回
* @since 0.0.1
*/
boolean tryLock(String key, TimeUnit timeUnit, long lockTime);
/**
* 尝试加锁,只加锁一次
* @param key key
* @param lockTime 加锁时间
* @return 返回
* @since 0.0.1
*/
boolean tryLock(String key, long lockTime);
/**
* 尝试加锁,只加锁一次
@@ -32,6 +52,7 @@ public interface ILock {
* 解锁
* @param key key
* @since 0.0.1
* @return 是否释放锁成功
*/
boolean unlock(String key);

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

@@ -12,30 +12,21 @@ public interface ILockSupport {
/**
* 尝试加锁,如果失败,会一直尝试。
*
* @param time 时间
* @param unit 单位
* @param key key
* 1. 如果等待时间小于等于0则直接执行加锁
* 2. 如果等待时间大于等于0则尝试循环等待。
*
* @param context 上下文
* @return 返回
* @since 0.0.1
*/
boolean tryLock(long time, TimeUnit unit, String key, final ILockSupportContext context);
/**
* 尝试加锁,只加锁一次
* @param key key
* @param context 上下文
* @return 返回
* @since 0.0.1
*/
boolean tryLock(String key, final ILockSupportContext context);
boolean tryLock(final ILockSupportContext context);
/**
* 解锁
* @param key key
* @param context 上下文
* @since 0.0.1
* @return 结果
*/
boolean unlock(String key, final ILockSupportContext context);
boolean unlock(final ILockSupportContext context);
}

View File

@@ -3,6 +3,8 @@ package com.github.houbb.lock.api.core;
import com.github.houbb.common.cache.api.service.ICommonCacheService;
import com.github.houbb.id.api.Id;
import java.util.concurrent.TimeUnit;
/**
* 分布式锁接口定义
* @author binbin.hou
@@ -11,21 +13,60 @@ import com.github.houbb.id.api.Id;
public interface ILockSupportContext {
/**
* 标识
*
* @return 标识策略
* @since 0.0.4
*/
Id id();
/**
* 缓存
* @return 缓存策略
* @since 0.0.4
*/
ICommonCacheService cache();
/**
* 锁的过期时间
* @return 结果
* 时间单位
* @return 时间单位
*/
int lockExpireMills();
TimeUnit timeUnit();
/**
* 加锁时间
* @return 时间
* @since 1.2.0
*/
long lockTime();
/**
* 等待加锁时间
* @return 等待时间
* @since 1.2.0
*/
long waitLockTime();
/**
* 缓存对应的 key
* @return 结果
* @since 1.2.0
*/
String key();
/**
* 锁 key 格式化
* @since 1.2.0
* @return 格式化
*/
ILockKeyFormat lockKeyFormat();
/**
* 锁释放失败处理类
* @since 1.2.0
* @return 失败处理
*/
ILockReleaseFailHandler lockReleaseFailHandler();
}

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>lock</artifactId>
<groupId>com.github.houbb</groupId>
<version>1.1.0</version>
<version>1.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -4,10 +4,9 @@ import com.github.houbb.common.cache.api.service.ICommonCacheService;
import com.github.houbb.heaven.util.common.ArgUtil;
import com.github.houbb.id.api.Id;
import com.github.houbb.id.core.core.Ids;
import com.github.houbb.lock.api.core.ILock;
import com.github.houbb.lock.api.core.ILockSupport;
import com.github.houbb.lock.api.core.ILockSupportContext;
import com.github.houbb.lock.core.constant.LockConst;
import com.github.houbb.lock.api.core.*;
import com.github.houbb.lock.core.support.format.LockKeyFormat;
import com.github.houbb.lock.core.support.handler.LockReleaseFailHandler;
import com.github.houbb.lock.core.support.lock.LockSupportContext;
import com.github.houbb.lock.core.support.lock.RedisLockSupport;
import com.github.houbb.redis.config.core.factory.JedisRedisServiceFactory;
@@ -20,48 +19,49 @@ import java.util.concurrent.TimeUnit;
* @author binbin.hou
* @since 0.0.4
*/
public final class LockBs implements ILock{
public final class LockBs implements ILock {
private LockBs(){}
private LockBs() {
}
public static LockBs newInstance() {
return new LockBs();
}
/**
* 加锁锁定时间
* @since 0.0.4
*/
private int lockExpireMills = LockConst.DEFAULT_EXPIRE_MILLS;
/**
* 标识策略
*
* @since 0.0.4
*/
private Id id = Ids.uuid32();
/**
* 缓存策略
*
* @since 0.0.4
*/
private ICommonCacheService cache = JedisRedisServiceFactory.pooled("127.0.0.1", 6379);
/**
* 锁支持策略
*
* @since 1.0.0
*/
private ILockSupport lockSupport = new RedisLockSupport();
/**
* 锁上下文
* @since 1.0.0
* 锁 key 格式化
*
* @since 1.2.0
*/
private ILockSupportContext lockSupportContext = null;
private ILockKeyFormat lockKeyFormat = new LockKeyFormat();
public LockBs lockExpireMills(int lockExpireMills) {
this.lockExpireMills = lockExpireMills;
return this;
}
/**
* 锁释放失败处理类
*
* @since 1.2.0
*/
private ILockReleaseFailHandler lockReleaseFailHandler = new LockReleaseFailHandler();
public LockBs id(Id id) {
ArgUtil.notNull(id, "id");
@@ -84,48 +84,67 @@ public final class LockBs implements ILock{
return this;
}
public LockBs lockKeyFormat(ILockKeyFormat lockKeyFormat) {
ArgUtil.notNull(lockKeyFormat, "lockKeyFormat");
/**
* 初始化
*/
public LockBs init() {
this.lockSupportContext = LockSupportContext.newInstance()
.id(id)
.cache(cache)
.lockExpireMills(lockExpireMills);
this.lockKeyFormat = lockKeyFormat;
return this;
}
public LockBs lockReleaseFailHandler(ILockReleaseFailHandler lockReleaseFailHandler) {
ArgUtil.notNull(lockReleaseFailHandler, "lockReleaseFailHandler");
this.lockReleaseFailHandler = lockReleaseFailHandler;
return this;
}
public ILockReleaseFailHandler lockReleaseFailHandler() {
return lockReleaseFailHandler;
}
@Override
public boolean tryLock(long time, TimeUnit unit, String key) {
ArgUtil.notEmpty(key, "key");
this.checkInitStatus();
public boolean tryLock(String key, TimeUnit timeUnit, long lockTime, long waitLockTime) {
ILockSupportContext supportContext = buildLockSupportContext(key, timeUnit, lockTime, waitLockTime);
return this.lockSupport.tryLock(supportContext);
}
@Override
public boolean tryLock(String key, TimeUnit timeUnit, long lockTime) {
return this.tryLock(key, timeUnit, lockTime, 0);
}
return this.lockSupport.tryLock(time, unit, key, lockSupportContext);
@Override
public boolean tryLock(String key, long lockTime) {
return this.tryLock(key, TimeUnit.SECONDS, lockTime);
}
@Override
public boolean tryLock(String key) {
ArgUtil.notEmpty(key, "key");
this.checkInitStatus();
return this.lockSupport.tryLock(key, lockSupportContext);
return this.tryLock(key, 60);
}
@Override
public boolean unlock(String key) {
ILockSupportContext supportContext = buildLockSupportContext(key, TimeUnit.SECONDS, 0, 0);
return this.lockSupport.unlock(supportContext);
}
/**
* 构建上下文
*
* @param key key
* @param timeUnit 时间
* @param lockTime 加锁时间
* @param waitLockTime 等待加锁时间
* @return 结果
* @since 1.0.0
*/
private ILockSupportContext buildLockSupportContext(String key, TimeUnit timeUnit, long lockTime, long waitLockTime) {
ArgUtil.notEmpty(key, "key");
this.checkInitStatus();
ArgUtil.notNull(timeUnit, "timeUnit");
return this.lockSupport.unlock(key, lockSupportContext);
ILockSupportContext context = LockSupportContext.newInstance().id(id).cache(cache).lockKeyFormat(lockKeyFormat).lockReleaseFailHandler(lockReleaseFailHandler).key(key).timeUnit(timeUnit).lockTime(lockTime).waitLockTime(waitLockTime);
return context;
}
private void checkInitStatus() {
ArgUtil.notNull(lockSupportContext, "please init() first!");
}
}

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

@@ -2,7 +2,13 @@ package com.github.houbb.lock.core.support.lock;
import com.github.houbb.common.cache.api.service.ICommonCacheService;
import com.github.houbb.id.api.Id;
import com.github.houbb.lock.api.core.ILockKeyFormat;
import com.github.houbb.lock.api.core.ILockReleaseFailHandler;
import com.github.houbb.lock.api.core.ILockSupportContext;
import com.github.houbb.lock.core.support.format.LockKeyFormat;
import com.github.houbb.lock.core.support.handler.LockReleaseFailHandler;
import java.util.concurrent.TimeUnit;
/**
* 分布式锁接口定义
@@ -25,13 +31,43 @@ public class LockSupportContext implements ILockSupportContext {
* 缓存策略
* @since 0.0.4
*/
private ICommonCacheService commonCacheService;
private ICommonCacheService cache;
/**
* 锁的过期时间
* @since 1.0.0
* 时间单位
*/
private int lockExpireMills;
private TimeUnit timeUnit;
/**
* 加锁时间
* @since 1.2.0
*/
private long lockTime;
/**
* 等待加锁时间
* @since 1.2.0
*/
private long waitLockTime;
/**
* 缓存对应的 key
* @since 1.2.0
*/
private String key;
/**
* 锁 key 格式化
* @since 1.2.0
*/
private ILockKeyFormat lockKeyFormat;
/**
* 锁释放失败处理类
* @since 1.2.0
*/
private ILockReleaseFailHandler lockReleaseFailHandler;
@Override
public Id id() {
@@ -45,21 +81,72 @@ public class LockSupportContext implements ILockSupportContext {
@Override
public ICommonCacheService cache() {
return commonCacheService;
return cache;
}
public LockSupportContext cache(ICommonCacheService commonCacheService) {
this.commonCacheService = commonCacheService;
public LockSupportContext cache(ICommonCacheService cache) {
this.cache = cache;
return this;
}
@Override
public int lockExpireMills() {
return lockExpireMills;
public TimeUnit timeUnit() {
return timeUnit;
}
public LockSupportContext lockExpireMills(int lockExpireMills) {
this.lockExpireMills = lockExpireMills;
public LockSupportContext timeUnit(TimeUnit timeUnit) {
this.timeUnit = timeUnit;
return this;
}
@Override
public long lockTime() {
return lockTime;
}
public LockSupportContext lockTime(long lockTime) {
this.lockTime = lockTime;
return this;
}
@Override
public long waitLockTime() {
return waitLockTime;
}
public LockSupportContext waitLockTime(long waitLockTime) {
this.waitLockTime = waitLockTime;
return this;
}
@Override
public String key() {
return key;
}
public LockSupportContext key(String key) {
this.key = key;
return this;
}
@Override
public ILockKeyFormat lockKeyFormat() {
return lockKeyFormat;
}
public LockSupportContext lockKeyFormat(ILockKeyFormat lockKeyFormat) {
this.lockKeyFormat = lockKeyFormat;
return this;
}
@Override
public ILockReleaseFailHandler lockReleaseFailHandler() {
return lockReleaseFailHandler;
}
public LockSupportContext lockReleaseFailHandler(ILockReleaseFailHandler lockReleaseFailHandler) {
this.lockReleaseFailHandler = lockReleaseFailHandler;
return this;
}
}

View File

@@ -1,10 +1,13 @@
package com.github.houbb.lock.core.support.lock;
import com.github.houbb.common.cache.api.service.ICommonCacheService;
import com.github.houbb.heaven.util.lang.StringUtil;
import com.github.houbb.id.api.Id;
import com.github.houbb.id.core.util.IdThreadLocalHelper;
import com.github.houbb.lock.api.core.ILockKeyFormat;
import com.github.houbb.lock.api.core.ILockSupport;
import com.github.houbb.lock.api.core.ILockSupportContext;
import com.github.houbb.lock.core.support.format.LockKeyFormatContext;
import com.github.houbb.log.integration.core.Log;
import com.github.houbb.log.integration.core.LogFactory;
import com.github.houbb.redis.config.core.constant.JedisConst;
@@ -22,25 +25,27 @@ public class RedisLockSupport implements ILockSupport {
private final Log log = LogFactory.getLog(RedisLockSupport.class);
@Override
public boolean tryLock(long time, TimeUnit unit, String key, ILockSupportContext context) {
public boolean tryLock(ILockSupportContext context) {
long startTimeMills = System.currentTimeMillis();
// 一次获取,直接成功
boolean result = this.tryLock(key, context);
boolean result = this.doLock(context);
if(result) {
return true;
}
// 时间判断
if(time <= 0) {
final TimeUnit timeUnit = context.timeUnit();
final long waitLockTime = context.waitLockTime();
if(waitLockTime <= 0) {
return false;
}
long durationMills = unit.toMillis(time);
long durationMills = timeUnit.toMillis(waitLockTime);
long endMills = startTimeMills + durationMills;
// 循环等待
while (System.currentTimeMillis() < endMills) {
result = tryLock(key, context);
result = this.doLock(context);
if(result) {
return true;
}
@@ -49,33 +54,48 @@ public class RedisLockSupport implements ILockSupport {
try {
TimeUnit.MILLISECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
log.debug("[LOCK] try lock wait 1 mills.");
}
}
return false;
}
@Override
public boolean tryLock(String key, ILockSupportContext context) {
/**
* 执行加锁
* @param context 上下文
* @return 结果
*/
protected boolean doLock(ILockSupportContext context) {
final String key = this.getActualKey(context);
// 生成当前线程的唯一标识
Id id = context.id();
final Id id = context.id();
final String requestId = id.id();
IdThreadLocalHelper.put(requestId);
log.info("[LOCK] BEGIN TRY LOCK key: {} requestId: {}", key, requestId);
final ICommonCacheService commonCacheService = context.cache();
final int lockExpireMills = context.lockExpireMills();
final TimeUnit timeUnit = context.timeUnit();
final long lockTime = context.lockTime();
final int lockExpireMills = (int) timeUnit.toMillis(lockTime);
String result = commonCacheService.set(key, requestId, JedisConst.SET_IF_NOT_EXIST, JedisConst.SET_WITH_EXPIRE_TIME, lockExpireMills);
log.info("[LOCK] END TRY LOCK key: {}, requestId: {}, result: {}", key, requestId, result);
log.info("[LOCK] END TRY LOCK key: {}, requestId: {}, lockExpireMills: {}, result: {}", key, requestId, lockExpireMills, result);
return JedisConst.OK.equalsIgnoreCase(result);
}
@Override
public boolean unlock(String key, ILockSupportContext context) {
public boolean unlock(ILockSupportContext context) {
final String key = this.getActualKey(context);
String requestId = IdThreadLocalHelper.get();
log.info("[LOCK] BEGIN UN LOCK key: {} requestId: {}", key, requestId);
if(StringUtil.isEmpty(requestId)) {
log.warn("[LOCK] UNLOCK requestId not found, ignore");
return false;
}
final ICommonCacheService commonCacheService = context.cache();
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = commonCacheService.eval(script, Collections.singletonList(key), Collections.singletonList(requestId));
@@ -83,4 +103,21 @@ public class RedisLockSupport implements ILockSupport {
return JedisConst.RELEASE_SUCCESS.equals(result);
}
/**
* 构建真正的 key
* @param context 上下文
* @return 结果
* @since 1.2.0
*/
private String getActualKey(ILockSupportContext context) {
final String rawKey = context.key();
final ILockKeyFormat keyFormat = context.lockKeyFormat();
LockKeyFormatContext formatContext = LockKeyFormatContext.newInstance()
.rawKey(rawKey);
String key = keyFormat.format(formatContext);
log.info("[LOCK] format rawKey: {} to key: {}", rawKey, key);
return key;
}
}

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>lock</artifactId>
<groupId>com.github.houbb</groupId>
<version>1.1.0</version>
<version>1.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -18,13 +18,6 @@ import java.lang.annotation.*;
@EnableAspectJAutoProxy
public @interface EnableLock {
/**
* 加锁过期时间
* @return 时间
* @since 1.1.0
*/
int lockExpireMills() default 60 * 1000;
/**
* 唯一标识生成策略
* @return 结果
@@ -42,5 +35,18 @@ public @interface EnableLock {
*/
String cache() default "springRedisService";
/**
* 加锁 key 格式化策略
* @return 策略
* @since 1.2.0
*/
String lockKeyFormat() default "lockKeyFormat";
/**
* 锁释放失败处理类
* @return 结果
* @since 1.2.0
*/
String lockReleaseFailHandler() default "lockReleaseFailHandler";
}

View File

@@ -1,6 +1,7 @@
package com.github.houbb.lock.spring.annotation;
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
/**
* 分布式加锁注解
@@ -20,9 +21,24 @@ public @interface Lock {
String value() default "";
/**
* 尝试获取锁等待时间
* @return 结果
* 时间单位
* @return 单位
* @since 1.2.0
*/
long tryLockMills() default 10 * 1000;
TimeUnit timeUnit() default TimeUnit.SECONDS;
/**
* 等待锁时间
* @return 等待锁时间
* @since 1.2.0
*/
long waitLockTime() default 10;
/**
* 业务加锁时间
* @return 加锁时间
* @since 1.2.0
*/
long lockTime() default 60;
}

View File

@@ -6,6 +6,7 @@ import com.github.houbb.heaven.util.lang.StringUtil;
import com.github.houbb.heaven.util.util.ArrayUtil;
import com.github.houbb.lock.api.exception.LockException;
import com.github.houbb.lock.core.bs.LockBs;
import com.github.houbb.lock.core.support.handler.LockReleaseFailHandlerContext;
import com.github.houbb.lock.spring.annotation.Lock;
import com.github.houbb.log.integration.core.Log;
import com.github.houbb.log.integration.core.LogFactory;
@@ -24,9 +25,7 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
/**
* @author binbin.hou
@@ -61,16 +60,14 @@ public class LockAspect {
boolean isLockAnnotation = method.isAnnotationPresent(Lock.class);
if(isLockAnnotation) {
Lock lock = method.getAnnotation(Lock.class);
Lock lockAnnotation = method.getAnnotation(Lock.class);
// 如果构建 key
String lockKey = buildLockKey(lock, point);
String lockKey = buildLockKey(lockAnnotation, point);
boolean tryLockFlag = false;
try {
long tryLockMills = lock.tryLockMills();
tryLockFlag = lockBs.tryLock(tryLockMills, TimeUnit.MILLISECONDS, lockKey);
tryLockFlag = lockBs.tryLock(lockKey, lockAnnotation.timeUnit(), lockAnnotation.lockTime(), lockAnnotation.waitLockTime());
if(!tryLockFlag) {
log.warn("[LOCK] TRY LOCK FAILED {}", lockKey);
throw new LockException("[LOCK] TRY LOCK FAILED " + lockKey);
@@ -84,7 +81,9 @@ public class LockAspect {
// 只有获取锁的情况下,才尝试释放锁
if(tryLockFlag) {
boolean unLockFlag = lockBs.unlock(lockKey);
// 异常处理等
// 释放锁结果
this.lockReleaseFailHandle(unLockFlag, lockKey);
}
}
} else {
@@ -92,6 +91,24 @@ public class LockAspect {
}
}
/**
* 锁释放失败
* @param unLockFlag 释放结果
* @param lockKey 加锁信息
*/
private void lockReleaseFailHandle(boolean unLockFlag,
String lockKey) {
if(unLockFlag) {
return;
}
// 触发通知,便于用户自定义处理。
// 比如报警
LockReleaseFailHandlerContext handlerContext = LockReleaseFailHandlerContext.newInstance()
.key(lockKey);
this.lockBs.lockReleaseFailHandler().handle(handlerContext);
}
/**
* 构建加锁的 key
*

View File

@@ -1,16 +0,0 @@
package com.github.houbb.lock.spring.config;
import com.github.houbb.redis.config.spring.annotation.EnableRedisConfig;
import org.springframework.context.annotation.Configuration;
/**
* bean 配置
*
* @author binbin.hou
* @since 0.0.2
*/
@Configuration
@EnableRedisConfig
public class CommonCacheConfig {
}

View File

@@ -1,14 +1,12 @@
package com.github.houbb.lock.spring.config;
import com.github.houbb.common.cache.api.service.ICommonCacheService;
import com.github.houbb.heaven.util.lang.StringUtil;
import com.github.houbb.id.api.Id;
import com.github.houbb.id.core.core.Ids;
import com.github.houbb.lock.api.core.ILockKeyFormat;
import com.github.houbb.lock.api.core.ILockReleaseFailHandler;
import com.github.houbb.lock.core.bs.LockBs;
import com.github.houbb.lock.spring.annotation.EnableLock;
import com.github.houbb.redis.config.core.factory.JedisRedisServiceFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.*;
@@ -23,20 +21,21 @@ import org.springframework.core.type.AnnotationMetadata;
*/
@Configuration
@ComponentScan(basePackages = "com.github.houbb.lock.spring")
@Import({LockBeanConfig.class, CommonCacheConfig.class})
@Import({LockBeanConfig.class})
public class LockAopConfig implements ImportAware, BeanFactoryPostProcessor {
@Bean("lockBs")
public LockBs lockBs() {
int lockExpireMills = (int) enableLockAttributes.get("lockExpireMills");
ICommonCacheService commonCacheService = beanFactory.getBean(enableLockAttributes.getString("cache"), ICommonCacheService.class);
Id id = beanFactory.getBean(enableLockAttributes.getString("id"), Id.class);
ILockKeyFormat lockKeyFormat = beanFactory.getBean(enableLockAttributes.getString("lockKeyFormat"), ILockKeyFormat.class);
ILockReleaseFailHandler lockReleaseFailHandler = beanFactory.getBean(enableLockAttributes.getString("lockReleaseFailHandler"), ILockReleaseFailHandler.class);
return LockBs.newInstance()
.cache(commonCacheService)
.id(id)
.lockExpireMills(lockExpireMills)
.init();
.lockKeyFormat(lockKeyFormat)
.lockReleaseFailHandler(lockReleaseFailHandler);
}
/**

View File

@@ -2,6 +2,10 @@ package com.github.houbb.lock.spring.config;
import com.github.houbb.id.api.Id;
import com.github.houbb.id.core.core.Ids;
import com.github.houbb.lock.api.core.ILockKeyFormat;
import com.github.houbb.lock.api.core.ILockReleaseFailHandler;
import com.github.houbb.lock.core.support.format.LockKeyFormat;
import com.github.houbb.lock.core.support.handler.LockReleaseFailHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@@ -21,4 +25,14 @@ public class LockBeanConfig {
return Ids.uuid32();
}
@Bean("lockKeyFormat")
public ILockKeyFormat lockKeyFormat() {
return new LockKeyFormat();
}
@Bean("lockReleaseFailHandler")
public ILockReleaseFailHandler lockReleaseFailHandler() {
return new LockReleaseFailHandler();
}
}

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>lock</artifactId>
<groupId>com.github.houbb</groupId>
<version>1.1.0</version>
<version>1.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -17,9 +17,15 @@
<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

@@ -1,8 +1,6 @@
package com.github.houbb.lock.springboot.starter.config;
import com.github.houbb.lock.spring.annotation.EnableLock;
import com.github.houbb.lock.spring.config.LockAopConfig;
import com.github.houbb.redis.config.spring.annotation.EnableRedisConfig;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>lock</artifactId>
<groupId>com.github.houbb</groupId>
<version>1.1.0</version>
<version>1.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -2,6 +2,7 @@ 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;
@@ -12,5 +13,6 @@ import org.springframework.context.annotation.ComponentScan;
@Configurable
@ComponentScan(basePackages = "com.github.houbb.lock.test.service")
@EnableLock
@EnableRedisConfig
public class SpringConfig {
}

View File

@@ -1,17 +1,20 @@
package com.github.houbb.lock.test.core;
import com.github.houbb.id.core.core.Ids;
import com.github.houbb.lock.api.core.ILock;
import com.github.houbb.lock.core.bs.LockBs;
import com.github.houbb.lock.core.support.format.LockKeyFormat;
import com.github.houbb.lock.core.support.handler.LockReleaseFailHandler;
import com.github.houbb.lock.core.support.lock.RedisLockSupport;
import com.github.houbb.redis.config.core.factory.JedisRedisServiceFactory;
import org.junit.Test;
public class LockBsTest {
@Test
public void helloTest() {
ILock lock = LockBs.newInstance()
.init();
ILock lock = LockBs.newInstance();
String key = "ddd";
try {
// 加锁
@@ -25,4 +28,15 @@ public class LockBsTest {
}
}
@Test
public void configTest() {
LockBs.newInstance()
.id(Ids.uuid32()) //id 生成策略
.cache(JedisRedisServiceFactory.pooled("127.0.0.1", 6379)) //缓存策略
.lockSupport(new RedisLockSupport()) // 锁实现策略
.lockKeyFormat(new LockKeyFormat()) // 针对 key 的格式化处理策略
.lockReleaseFailHandler(new LockReleaseFailHandler()) //释放锁失败处理
;
}
}

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>lock</artifactId>
<groupId>com.github.houbb</groupId>
<version>1.1.0</version>
<version>1.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<groupId>com.github.houbb</groupId>
<artifactId>lock</artifactId>
<packaging>pom</packaging>
<version>1.1.0</version>
<version>1.3.0</version>
<modules>
<module>lock-api</module>
<module>lock-core</module>
@@ -123,6 +123,11 @@
<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>

View File

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