release branch 1.1.0

This commit is contained in:
binbin.hou
2022-12-07 21:00:45 +08:00
parent 27a931ae77
commit c45f6b53a0
27 changed files with 444 additions and 78 deletions

View File

@@ -39,3 +39,9 @@
| 序号 | 变更类型 | 说明 | 时间 | 备注 |
|:---|:---|:---|:---|:--|
| 1 | A | 基于 redis 实现的分布式锁策略 | 2022-12-7 14:45:40 | |
# release_1.1.0
| 序号 | 变更类型 | 说明 | 时间 | 备注 |
|:---|:---|:---|:---|:--|
| 1 | A | 整合 spring | 2022-12-7 14:45:40 | |

119
README.md
View File

@@ -10,9 +10,13 @@
- 基于 redis 的分布式锁
- 基于 oracle 的分布式锁
- 整合 spring
- 基于 mysql 的分布式锁
- 整合 spring-boot
- 开箱即用,支持注解。
- 支持多种 redis 的声明方式
# 变更日志
@@ -32,7 +36,7 @@ maven 3.x+
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>lock-core</artifactId>
<version>1.0.0</version>
<version>1.1.0</version>
</dependency>
```
@@ -57,21 +61,108 @@ try {
}
```
# 整合 spring
## maven 引入
```xml
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>lock-spring</artifactId>
<version>1.1.0</version>
</dependency>
```
## 指定 bean 使用
### 启用分布式锁
`@EnableLock` 启用分布式锁。
```xml
@Configurable
@ComponentScan(basePackages = "com.github.houbb.lock.test.service")
@EnableLock
public class SpringConfig {
}
```
### 使用 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`,使用更加方便。
支持 SPEL 表达式。
```java
@Service
public class UserService {
@Lock
public String queryUserName(Long userId) {
}
@Lock(value = "#user.name")
public void queryUserName2(User user) {
}
}
```
直接使用AOP 切面生效即可。
# springboot 整合
## maven 引入
```xml
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>lock-springboot-starter</artifactId>
<version>1.1.0</version>
</dependency>
```
## 使用
同 spring
# 后期 Road-MAP
- [ ] 支持锁的可重入
持有锁的线程可以多次获取锁
- [ ] redis 实现的支持
cluster 支持
redis 支持
aliyun-redis 支持
各种各样的声明方式的默认支持
- [ ] 分布式锁注解支持
- [x] 分布式锁注解支持

View File

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

View File

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

View File

@@ -57,31 +57,29 @@ public class RedisLockSupport implements ILockSupport {
@Override
public boolean tryLock(String key, ILockSupportContext context) {
log.info("开始尝试获取锁 {}", key);
// 生成当前线程的唯一标识
Id id = context.id();
final String requestId = id.id();
IdThreadLocalHelper.put(requestId);
log.info("开始尝试获取锁 requestId: {}", requestId);
log.info("[LOCK] BEGIN TRY LOCK key: {} requestId: {}", key, requestId);
final ICommonCacheService commonCacheService = context.cache();
final int lockExpireMills = context.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);
return JedisConst.OK.equalsIgnoreCase(result);
}
@Override
public boolean unlock(String key, ILockSupportContext context) {
log.info("开始尝试释放锁 {}", key);
String requestId = IdThreadLocalHelper.get();
log.info("开始尝试释放锁 requestId: {}", requestId);
log.info("[LOCK] BEGIN UN LOCK key: {} requestId: {}", key, requestId);
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);
}

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>lock</artifactId>
<groupId>com.github.houbb</groupId>
<version>1.0.0</version>
<version>1.1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -25,6 +25,11 @@
<groupId>com.github.houbb</groupId>
<artifactId>aop-spring</artifactId>
</dependency>
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>redis-config-spring</artifactId>
</dependency>
</dependencies>
</project>
</project>

View File

@@ -34,10 +34,13 @@ public @interface EnableLock {
/**
* 缓存实现策略 bean 名称
*
* 默认引入 redis-config 中的配置
*
* @return 实现
* @since 1.1.0
*/
String cache() default "lockCache";
String cache() default "springRedisService";
}

View File

@@ -3,7 +3,6 @@ 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.lang.reflect.ReflectMethodUtil;
import com.github.houbb.heaven.util.util.ArrayUtil;
import com.github.houbb.lock.api.exception.LockException;
import com.github.houbb.lock.core.bs.LockBs;
@@ -11,9 +10,11 @@ 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;
@@ -23,8 +24,8 @@ 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.List;
import java.util.concurrent.TimeUnit;
/**
@@ -63,15 +64,16 @@ public class LockAspect {
Lock lock = method.getAnnotation(Lock.class);
// 如果构建 key
Object[] args = point.getArgs();
String lockKey = buildLockKey(lock, method, args);
String lockKey = buildLockKey(lock, point);
boolean tryLockFlag = false;
try {
long tryLockMills = lock.tryLockMills();
boolean tryLockFlag = lockBs.tryLock(tryLockMills, TimeUnit.MILLISECONDS, lockKey);
tryLockFlag = lockBs.tryLock(tryLockMills, TimeUnit.MILLISECONDS, lockKey);
if(!tryLockFlag) {
log.warn("尝试获取锁失败 {}", lockKey);
throw new LockException("尝试获取锁失败 " + lockKey);
log.warn("[LOCK] TRY LOCK FAILED {}", lockKey);
throw new LockException("[LOCK] TRY LOCK FAILED " + lockKey);
}
// 执行业务
@@ -79,10 +81,10 @@ public class LockAspect {
} catch (Throwable e) {
throw new RuntimeException(e);
} finally {
boolean unLockFlag = lockBs.unlock(lockKey);
if(!unLockFlag) {
log.warn("尝试释放锁失败 {}", lockKey);
// 这里释放异常,没有意义。
// 只有获取锁的情况下,才尝试释放锁
if(tryLockFlag) {
boolean unLockFlag = lockBs.unlock(lockKey);
// 异常处理等
}
}
} else {
@@ -97,19 +99,19 @@ public class LockAspect {
*
* https://www.cnblogs.com/best/p/5748105.html SpEL
* @param lock 注解信息
* @param args 参数
* @param joinPoint 参数
* @return 结果
*/
private String buildLockKey(Lock lock,
Method method,
Object[] args) {
ProceedingJoinPoint joinPoint) {
final Object[] args = joinPoint.getArgs();
final String lockValue = lock.value();
//创建SpEL表达式的解析器
ExpressionParser parser = new SpelExpressionParser();
//1. 如果没有入参怎么办?
if(ArrayUtil.isEmpty(args)) {
log.warn("对应的数组信息为空,直接返回 key 的值 {}", lockValue);
log.warn("[LOCK] method args is empty, return lock.value() {}", lockValue);
return lockValue;
}
@@ -123,10 +125,14 @@ public class LockAspect {
//解析表达式需要的上下文,解析时有一个默认的上下文
// jdk1.7 之前,直接使用 arg0, arg1...
EvaluationContext ctx = new StandardEvaluationContext();
List<String> paramNameList = ReflectMethodUtil.getParamNames(method);
for(int i = 0; i < paramNameList.size(); i++) {
String paramName = paramNameList.get(i);
// 利用 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对象

View File

@@ -0,0 +1,16 @@
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

@@ -23,7 +23,7 @@ import org.springframework.core.type.AnnotationMetadata;
*/
@Configuration
@ComponentScan(basePackages = "com.github.houbb.lock.spring")
@Import(LockBeanConfig.class)
@Import({LockBeanConfig.class, CommonCacheConfig.class})
public class LockAopConfig implements ImportAware, BeanFactoryPostProcessor {
@Bean("lockBs")

View File

@@ -1,11 +1,7 @@
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.redis.config.core.factory.JedisRedisServiceFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@@ -20,24 +16,6 @@ import org.springframework.context.annotation.Configuration;
@ComponentScan(basePackages = "com.github.houbb.lock.spring")
public class LockBeanConfig {
@Value("${redis.address:127.0.0.1}")
private String redisAddress;
@Value("${redis.port:6379}")
private int redisPort;
@Value("${redis.password:}")
private String redisPassword;
@Bean("lockCache")
public ICommonCacheService lockCache() {
if(StringUtil.isNotEmpty(redisPassword)) {
return JedisRedisServiceFactory.pooled(redisAddress, redisPort, redisPassword);
}
return JedisRedisServiceFactory.simple(redisAddress, redisPort);
}
@Bean("lockId")
public Id lockId() {
return Ids.uuid32();

View File

@@ -0,0 +1,25 @@
<?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.1.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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,18 @@
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;
/**
* 分布式锁自动配置
* @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>1.0.0</version>
<version>1.1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -30,6 +30,23 @@
<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

@@ -11,12 +11,16 @@ import org.springframework.stereotype.Service;
@Service
public class UserService {
public String rawUserName(Long userId) {
return userId+"-name";
}
@Lock
public String queryUserName(Long userId) {
return userId+"-name";
}
@Lock(value = "#arg0.name")
@Lock(value = "#user.name")
public void queryUserName2(User user) {
System.out.println("user: " + user.toString());
}

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,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.1.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;

28
pom.xml
View File

@@ -5,12 +5,14 @@
<groupId>com.github.houbb</groupId>
<artifactId>lock</artifactId>
<packaging>pom</packaging>
<version>1.0.0</version>
<version>1.1.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>
@@ -46,7 +48,7 @@
<!--============================== 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>
@@ -67,6 +69,11 @@
<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>
@@ -111,6 +118,11 @@
<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>
<!--============================== OTHER ==============================-->
<dependency>
@@ -121,12 +133,6 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${jedis.version}</version>
</dependency>
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>aop-core</artifactId>
@@ -144,6 +150,12 @@
<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=1.0.0
SET version=1.1.0
:::: 新版本名称
SET newVersion=1.1.0
SET newVersion=1.2.0
:::: 组织名称
SET groupName=com.github.houbb
:::: 项目名称