release branch 1.1.0

This commit is contained in:
binbin.hou
2022-12-08 11:15:03 +08:00
parent c45f6b53a0
commit 0bae77fc8a
22 changed files with 692 additions and 138 deletions

View File

@@ -45,3 +45,11 @@
| 序号 | 变更类型 | 说明 | 时间 | 备注 | | 序号 | 变更类型 | 说明 | 时间 | 备注 |
|:---|:---|:---|:---|:--| |:---|:---|:---|:---|:--|
| 1 | A | 整合 spring | 2022-12-7 14:45:40 | | | 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 | |

149
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) [![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) [![Build Status](https://www.travis-ci.org/houbb/lock.svg?branch=master)](https://www.travis-ci.org/houbb/lock?branch=master)
@@ -8,15 +8,15 @@
## 目的 ## 目的
- 开箱即用,支持注解式和过程式调用
- 基于 redis 的分布式锁 - 基于 redis 的分布式锁
- 整合 spring - 整合 spring
- 整合 spring-boot - 整合 spring-boot
- 开箱即用,支持注解。 - 支持多种 redis 的整合方式
- 支持多种 redis 的声明方式
# 变更日志 # 变更日志
@@ -36,7 +36,7 @@ maven 3.x+
<dependency> <dependency>
<groupId>com.github.houbb</groupId> <groupId>com.github.houbb</groupId>
<artifactId>lock-core</artifactId> <artifactId>lock-core</artifactId>
<version>1.1.0</version> <version>1.2.0</version>
</dependency> </dependency>
``` ```
@@ -45,22 +45,36 @@ maven 3.x+
基于本地 redis 的测试案例。 基于本地 redis 的测试案例。
```java ```java
ILock lock = LockBs.newInstance() public void helloTest() {
.init(); ILock lock = LockBs.newInstance();
String key = "ddd";
String key = "ddd"; try {
try { // 加锁
// 加锁 lock.tryLock(key);
lock.tryLock(key); System.out.println("业务处理");
System.out.println("业务处理"); } catch (Exception e) {
} catch (Exception e) { throw new RuntimeException(e);
throw new RuntimeException(e); } finally {
} finally { // 释放锁
// 释放锁 lock.unlock(key);
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 # 整合 spring
## maven 引入 ## maven 引入
@@ -69,7 +83,7 @@ try {
<dependency> <dependency>
<groupId>com.github.houbb</groupId> <groupId>com.github.houbb</groupId>
<artifactId>lock-spring</artifactId> <artifactId>lock-spring</artifactId>
<version>1.1.0</version> <version>1.2.0</version>
</dependency> </dependency>
``` ```
@@ -87,9 +101,58 @@ public class SpringConfig {
} }
``` ```
`EnableLock` 注解说明,和引导类对应:
```java
public @interface EnableLock {
/**
* 唯一标识生成策略
* @return 结果
* @since 1.1.0
*/
String id() default "lockId";
/**
* 缓存实现策略 bean 名称
*
* 默认引入 redis-config 中的配置
*
* @return 实现
* @since 1.1.0
*/
String cache() default "springRedisService";
/**
* 加锁 key 格式化策略
* @return 策略
* @since 1.2.0
*/
String lockKeyFormat() default "lockKeyFormat";
/**
* 锁释放失败处理类
* @return 结果
* @since 1.2.0
*/
String lockReleaseFailHandler() default "lockReleaseFailHandler";
}
```
其中 `springRedisService` 使用的是 [redis-config](https://github.com/houbb/redis-config) 中的实现。
redis 的配置信息如下:
| 配置 | 说明 | 默认值
|:---|:---|:----|
| redis.address | redis 地址 | 127.0.0.1 |
| redis.port | redis 端口 | 6379 |
| redis.password | redis 密码 | |
### 使用 LockBs ### 使用 LockBs
我们可以直接 `LockBs` 的引导类。 我们可以直接 `LockBs` 的引导类,这种适合一些更加灵活的场景
```java ```java
@ContextConfiguration(classes = SpringConfig.class) @ContextConfiguration(classes = SpringConfig.class)
@@ -124,7 +187,7 @@ public class SpringServiceRawTest {
当然,我们可以在方法上直接指定注解 `@Lock`,使用更加方便。 当然,我们可以在方法上直接指定注解 `@Lock`,使用更加方便。
支持 SPEL 表达式 直接使用AOP 切面生效即可
```java ```java
@Service @Service
@@ -140,9 +203,44 @@ public class UserService {
} }
``` ```
直接使用AOP 切面生效即可 `@Lock` 属性说明value 用于指定 key支持 SPEL 表达式
# springboot 整合 其他属性,和引导类的方法参数一一对应。
```java
public @interface Lock {
/**
* 缓存的 key 策略,支持 SpEL
* @return 结果
*/
String value() default "";
/**
* 时间单位
* @return 单位
* @since 1.2.0
*/
TimeUnit timeUnit() default TimeUnit.SECONDS;
/**
* 等待锁时间
* @return 等待锁时间
* @since 1.2.0
*/
long waitLockTime() default 10;
/**
* 业务加锁时间
* @return 加锁时间
* @since 1.2.0
*/
long lockTime() default 60;
}
```
# spring boot 整合
## maven 引入 ## maven 引入
@@ -150,7 +248,7 @@ public class UserService {
<dependency> <dependency>
<groupId>com.github.houbb</groupId> <groupId>com.github.houbb</groupId>
<artifactId>lock-springboot-starter</artifactId> <artifactId>lock-springboot-starter</artifactId>
<version>1.1.0</version> <version>1.2.0</version>
</dependency> </dependency>
``` ```
@@ -164,5 +262,4 @@ public class UserService {
持有锁的线程可以多次获取锁 持有锁的线程可以多次获取锁
- [x] 分布式锁注解支持 - [x] 分布式锁注解支持

View File

@@ -12,13 +12,33 @@ public interface ILock {
/** /**
* 尝试加锁,如果失败,会一直尝试。 * 尝试加锁,如果失败,会一直尝试。
* *
* @param time 时间
* @param unit 当为
* @param key key * @param key key
* @param timeUnit 时间单位
* @param waitLockTime 等待锁时间
* @param lockTime 加锁时间
* @return 返回 * @return 返回
* @since 0.0.1 * @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 * @param key key
* @since 0.0.1 * @since 0.0.1
* @return 是否释放锁成功
*/ */
boolean 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

@@ -12,30 +12,21 @@ public interface ILockSupport {
/** /**
* 尝试加锁,如果失败,会一直尝试。 * 尝试加锁,如果失败,会一直尝试。
* *
* @param time 时间 * 1. 如果等待时间小于等于0则直接执行加锁
* @param unit 单位 * 2. 如果等待时间大于等于0则尝试循环等待。
* @param key key *
* @param context 上下文 * @param context 上下文
* @return 返回 * @return 返回
* @since 0.0.1 * @since 0.0.1
*/ */
boolean tryLock(long time, TimeUnit unit, String key, final ILockSupportContext context); boolean tryLock(final ILockSupportContext context);
/**
* 尝试加锁,只加锁一次
* @param key key
* @param context 上下文
* @return 返回
* @since 0.0.1
*/
boolean tryLock(String key, final ILockSupportContext context);
/** /**
* 解锁 * 解锁
* @param key key
* @param context 上下文 * @param context 上下文
* @since 0.0.1 * @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.common.cache.api.service.ICommonCacheService;
import com.github.houbb.id.api.Id; import com.github.houbb.id.api.Id;
import java.util.concurrent.TimeUnit;
/** /**
* 分布式锁接口定义 * 分布式锁接口定义
* @author binbin.hou * @author binbin.hou
@@ -11,21 +13,60 @@ import com.github.houbb.id.api.Id;
public interface ILockSupportContext { public interface ILockSupportContext {
/** /**
* 标识
*
* @return 标识策略 * @return 标识策略
* @since 0.0.4 * @since 0.0.4
*/ */
Id id(); Id id();
/** /**
* 缓存
* @return 缓存策略 * @return 缓存策略
* @since 0.0.4 * @since 0.0.4
*/ */
ICommonCacheService cache(); 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

@@ -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.heaven.util.common.ArgUtil;
import com.github.houbb.id.api.Id; import com.github.houbb.id.api.Id;
import com.github.houbb.id.core.core.Ids; import com.github.houbb.id.core.core.Ids;
import com.github.houbb.lock.api.core.ILock; import com.github.houbb.lock.api.core.*;
import com.github.houbb.lock.api.core.ILockSupport; import com.github.houbb.lock.core.support.format.LockKeyFormat;
import com.github.houbb.lock.api.core.ILockSupportContext; import com.github.houbb.lock.core.support.handler.LockReleaseFailHandler;
import com.github.houbb.lock.core.constant.LockConst;
import com.github.houbb.lock.core.support.lock.LockSupportContext; import com.github.houbb.lock.core.support.lock.LockSupportContext;
import com.github.houbb.lock.core.support.lock.RedisLockSupport; import com.github.houbb.lock.core.support.lock.RedisLockSupport;
import com.github.houbb.redis.config.core.factory.JedisRedisServiceFactory; import com.github.houbb.redis.config.core.factory.JedisRedisServiceFactory;
@@ -20,48 +19,49 @@ import java.util.concurrent.TimeUnit;
* @author binbin.hou * @author binbin.hou
* @since 0.0.4 * @since 0.0.4
*/ */
public final class LockBs implements ILock{ public final class LockBs implements ILock {
private LockBs(){} private LockBs() {
}
public static LockBs newInstance() { public static LockBs newInstance() {
return new LockBs(); return new LockBs();
} }
/**
* 加锁锁定时间
* @since 0.0.4
*/
private int lockExpireMills = LockConst.DEFAULT_EXPIRE_MILLS;
/** /**
* 标识策略 * 标识策略
*
* @since 0.0.4 * @since 0.0.4
*/ */
private Id id = Ids.uuid32(); private Id id = Ids.uuid32();
/** /**
* 缓存策略 * 缓存策略
*
* @since 0.0.4 * @since 0.0.4
*/ */
private ICommonCacheService cache = JedisRedisServiceFactory.pooled("127.0.0.1", 6379); private ICommonCacheService cache = JedisRedisServiceFactory.pooled("127.0.0.1", 6379);
/** /**
* 锁支持策略 * 锁支持策略
*
* @since 1.0.0 * @since 1.0.0
*/ */
private ILockSupport lockSupport = new RedisLockSupport(); private ILockSupport lockSupport = new RedisLockSupport();
/** /**
* 锁上下文 * 锁 key 格式化
* @since 1.0.0 *
* @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) { public LockBs id(Id id) {
ArgUtil.notNull(id, "id"); ArgUtil.notNull(id, "id");
@@ -84,48 +84,67 @@ public final class LockBs implements ILock{
return this; return this;
} }
public LockBs lockKeyFormat(ILockKeyFormat lockKeyFormat) {
ArgUtil.notNull(lockKeyFormat, "lockKeyFormat");
/** this.lockKeyFormat = lockKeyFormat;
* 初始化
*/
public LockBs init() {
this.lockSupportContext = LockSupportContext.newInstance()
.id(id)
.cache(cache)
.lockExpireMills(lockExpireMills);
return this; return this;
} }
public LockBs lockReleaseFailHandler(ILockReleaseFailHandler lockReleaseFailHandler) {
ArgUtil.notNull(lockReleaseFailHandler, "lockReleaseFailHandler");
this.lockReleaseFailHandler = lockReleaseFailHandler;
return this;
}
public ILockReleaseFailHandler lockReleaseFailHandler() {
return lockReleaseFailHandler;
}
@Override @Override
public boolean tryLock(long time, TimeUnit unit, String key) { public boolean tryLock(String key, TimeUnit timeUnit, long lockTime, long waitLockTime) {
ArgUtil.notEmpty(key, "key"); ILockSupportContext supportContext = buildLockSupportContext(key, timeUnit, lockTime, waitLockTime);
this.checkInitStatus(); 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 @Override
public boolean tryLock(String key) { public boolean tryLock(String key) {
ArgUtil.notEmpty(key, "key"); return this.tryLock(key, 60);
this.checkInitStatus();
return this.lockSupport.tryLock(key, lockSupportContext);
} }
@Override @Override
public boolean unlock(String key) { 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.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.common.cache.api.service.ICommonCacheService;
import com.github.houbb.id.api.Id; 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.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 * @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 @Override
public Id id() { public Id id() {
@@ -45,21 +81,72 @@ public class LockSupportContext implements ILockSupportContext {
@Override @Override
public ICommonCacheService cache() { public ICommonCacheService cache() {
return commonCacheService; return cache;
} }
public LockSupportContext cache(ICommonCacheService commonCacheService) { public LockSupportContext cache(ICommonCacheService cache) {
this.commonCacheService = commonCacheService; this.cache = cache;
return this; return this;
} }
@Override @Override
public int lockExpireMills() { public TimeUnit timeUnit() {
return lockExpireMills; return timeUnit;
} }
public LockSupportContext lockExpireMills(int lockExpireMills) { public LockSupportContext timeUnit(TimeUnit timeUnit) {
this.lockExpireMills = lockExpireMills; this.timeUnit = timeUnit;
return this; 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; package com.github.houbb.lock.core.support.lock;
import com.github.houbb.common.cache.api.service.ICommonCacheService; 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.api.Id;
import com.github.houbb.id.core.util.IdThreadLocalHelper; 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.ILockSupport;
import com.github.houbb.lock.api.core.ILockSupportContext; 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.Log;
import com.github.houbb.log.integration.core.LogFactory; import com.github.houbb.log.integration.core.LogFactory;
import com.github.houbb.redis.config.core.constant.JedisConst; 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); private final Log log = LogFactory.getLog(RedisLockSupport.class);
@Override @Override
public boolean tryLock(long time, TimeUnit unit, String key, ILockSupportContext context) { public boolean tryLock(ILockSupportContext context) {
long startTimeMills = System.currentTimeMillis(); long startTimeMills = System.currentTimeMillis();
// 一次获取,直接成功 // 一次获取,直接成功
boolean result = this.tryLock(key, context); boolean result = this.doLock(context);
if(result) { if(result) {
return true; return true;
} }
// 时间判断 // 时间判断
if(time <= 0) { final TimeUnit timeUnit = context.timeUnit();
final long waitLockTime = context.waitLockTime();
if(waitLockTime <= 0) {
return false; return false;
} }
long durationMills = unit.toMillis(time); long durationMills = timeUnit.toMillis(waitLockTime);
long endMills = startTimeMills + durationMills; long endMills = startTimeMills + durationMills;
// 循环等待 // 循环等待
while (System.currentTimeMillis() < endMills) { while (System.currentTimeMillis() < endMills) {
result = tryLock(key, context); result = this.doLock(context);
if(result) { if(result) {
return true; return true;
} }
@@ -49,33 +54,48 @@ public class RedisLockSupport implements ILockSupport {
try { try {
TimeUnit.MILLISECONDS.sleep(1); TimeUnit.MILLISECONDS.sleep(1);
} catch (InterruptedException e) { } catch (InterruptedException e) {
throw new RuntimeException(e); log.debug("[LOCK] try lock wait 1 mills.");
} }
} }
return false; 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(); final String requestId = id.id();
IdThreadLocalHelper.put(requestId); IdThreadLocalHelper.put(requestId);
log.info("[LOCK] BEGIN TRY LOCK key: {} requestId: {}", key, requestId); log.info("[LOCK] BEGIN TRY LOCK key: {} requestId: {}", key, requestId);
final ICommonCacheService commonCacheService = context.cache(); 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); 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); return JedisConst.OK.equalsIgnoreCase(result);
} }
@Override @Override
public boolean unlock(String key, ILockSupportContext context) { public boolean unlock(ILockSupportContext context) {
final String key = this.getActualKey(context);
String requestId = IdThreadLocalHelper.get(); String requestId = IdThreadLocalHelper.get();
log.info("[LOCK] BEGIN UN LOCK key: {} requestId: {}", key, requestId); 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(); 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"; 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)); 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); 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

@@ -18,13 +18,6 @@ import java.lang.annotation.*;
@EnableAspectJAutoProxy @EnableAspectJAutoProxy
public @interface EnableLock { public @interface EnableLock {
/**
* 加锁过期时间
* @return 时间
* @since 1.1.0
*/
int lockExpireMills() default 60 * 1000;
/** /**
* 唯一标识生成策略 * 唯一标识生成策略
* @return 结果 * @return 结果
@@ -42,5 +35,18 @@ public @interface EnableLock {
*/ */
String cache() default "springRedisService"; 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; package com.github.houbb.lock.spring.annotation;
import java.lang.annotation.*; import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
/** /**
* 分布式加锁注解 * 分布式加锁注解
@@ -20,9 +21,24 @@ public @interface Lock {
String value() default ""; 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.heaven.util.util.ArrayUtil;
import com.github.houbb.lock.api.exception.LockException; import com.github.houbb.lock.api.exception.LockException;
import com.github.houbb.lock.core.bs.LockBs; 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.lock.spring.annotation.Lock;
import com.github.houbb.log.integration.core.Log; import com.github.houbb.log.integration.core.Log;
import com.github.houbb.log.integration.core.LogFactory; 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 org.springframework.stereotype.Component;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.TimeUnit;
/** /**
* @author binbin.hou * @author binbin.hou
@@ -61,16 +60,14 @@ public class LockAspect {
boolean isLockAnnotation = method.isAnnotationPresent(Lock.class); boolean isLockAnnotation = method.isAnnotationPresent(Lock.class);
if(isLockAnnotation) { if(isLockAnnotation) {
Lock lock = method.getAnnotation(Lock.class); Lock lockAnnotation = method.getAnnotation(Lock.class);
// 如果构建 key // 如果构建 key
String lockKey = buildLockKey(lock, point); String lockKey = buildLockKey(lockAnnotation, point);
boolean tryLockFlag = false; boolean tryLockFlag = false;
try { try {
long tryLockMills = lock.tryLockMills(); tryLockFlag = lockBs.tryLock(lockKey, lockAnnotation.timeUnit(), lockAnnotation.lockTime(), lockAnnotation.waitLockTime());
tryLockFlag = lockBs.tryLock(tryLockMills, TimeUnit.MILLISECONDS, lockKey);
if(!tryLockFlag) { if(!tryLockFlag) {
log.warn("[LOCK] TRY LOCK FAILED {}", lockKey); log.warn("[LOCK] TRY LOCK FAILED {}", lockKey);
throw new LockException("[LOCK] TRY LOCK FAILED " + lockKey); throw new LockException("[LOCK] TRY LOCK FAILED " + lockKey);
@@ -84,7 +81,9 @@ public class LockAspect {
// 只有获取锁的情况下,才尝试释放锁 // 只有获取锁的情况下,才尝试释放锁
if(tryLockFlag) { if(tryLockFlag) {
boolean unLockFlag = lockBs.unlock(lockKey); boolean unLockFlag = lockBs.unlock(lockKey);
// 异常处理等
// 释放锁结果
this.lockReleaseFailHandle(unLockFlag, lockKey);
} }
} }
} else { } 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 * 构建加锁的 key
* *

View File

@@ -4,6 +4,8 @@ import com.github.houbb.common.cache.api.service.ICommonCacheService;
import com.github.houbb.heaven.util.lang.StringUtil; import com.github.houbb.heaven.util.lang.StringUtil;
import com.github.houbb.id.api.Id; import com.github.houbb.id.api.Id;
import com.github.houbb.id.core.core.Ids; 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.core.bs.LockBs;
import com.github.houbb.lock.spring.annotation.EnableLock; import com.github.houbb.lock.spring.annotation.EnableLock;
import com.github.houbb.redis.config.core.factory.JedisRedisServiceFactory; import com.github.houbb.redis.config.core.factory.JedisRedisServiceFactory;
@@ -28,15 +30,16 @@ public class LockAopConfig implements ImportAware, BeanFactoryPostProcessor {
@Bean("lockBs") @Bean("lockBs")
public LockBs lockBs() { public LockBs lockBs() {
int lockExpireMills = (int) enableLockAttributes.get("lockExpireMills");
ICommonCacheService commonCacheService = beanFactory.getBean(enableLockAttributes.getString("cache"), ICommonCacheService.class); ICommonCacheService commonCacheService = beanFactory.getBean(enableLockAttributes.getString("cache"), ICommonCacheService.class);
Id id = beanFactory.getBean(enableLockAttributes.getString("id"), Id.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() return LockBs.newInstance()
.cache(commonCacheService) .cache(commonCacheService)
.id(id) .id(id)
.lockExpireMills(lockExpireMills) .lockKeyFormat(lockKeyFormat)
.init(); .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.api.Id;
import com.github.houbb.id.core.core.Ids; 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.Bean;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@@ -21,4 +25,14 @@ public class LockBeanConfig {
return Ids.uuid32(); return Ids.uuid32();
} }
@Bean("lockKeyFormat")
public ILockKeyFormat lockKeyFormat() {
return new LockKeyFormat();
}
@Bean("lockReleaseFailHandler")
public ILockReleaseFailHandler lockReleaseFailHandler() {
return new LockReleaseFailHandler();
}
} }

View File

@@ -1,17 +1,20 @@
package com.github.houbb.lock.test.core; 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.api.core.ILock;
import com.github.houbb.lock.core.bs.LockBs; 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; import org.junit.Test;
public class LockBsTest { public class LockBsTest {
@Test @Test
public void helloTest() { public void helloTest() {
ILock lock = LockBs.newInstance() ILock lock = LockBs.newInstance();
.init();
String key = "ddd"; String key = "ddd";
try { 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()) //释放锁失败处理
;
}
} }