Browse Source

实现分布式锁接口、AOP拦截、自动装配模块,默认为虚拟加锁,具体实现参考其他模块

3.x
Yangkai.Shen 2 years ago
parent
commit
86fc5e27f2
15 changed files with 503 additions and 0 deletions
  1. +30
    -0
      demo-distributed-lock/demo-distributed-lock-api/pom.xml
  2. +28
    -0
      demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/annotation/DLock.java
  3. +68
    -0
      demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/aop/DistributedLockAspect.java
  4. +44
    -0
      demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/api/DistributedLock.java
  5. +27
    -0
      demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/api/DistributedLockService.java
  6. +23
    -0
      demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/api/LockClient.java
  7. +47
    -0
      demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/api/impl/DistributedLockServiceImpl.java
  8. +41
    -0
      demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/api/impl/DummyDistributedLock.java
  9. +29
    -0
      demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/api/impl/DummyDistributedLockClient.java
  10. +31
    -0
      demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/autoconfigure/DistributedLockConfiguration.java
  11. +15
    -0
      demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/mapper/StockMapper.java
  12. +29
    -0
      demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/model/Stock.java
  13. +58
    -0
      demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/service/StockService.java
  14. +31
    -0
      demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/task/InitStockTask.java
  15. +2
    -0
      demo-distributed-lock/demo-distributed-lock-api/src/main/resources/META-INF/spring.factories

+ 30
- 0
demo-distributed-lock/demo-distributed-lock-api/pom.xml View File

@@ -14,6 +14,36 @@

<properties>
<java.version>17</java.version>
<mybatis-plus.version>3.5.2</mybatis-plus.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

</project>

+ 28
- 0
demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/annotation/DLock.java View File

@@ -0,0 +1,28 @@
package com.xkcoding.distributed.lock.annotation;

import java.util.concurrent.TimeUnit;

/**
* <p>
* 分布式锁注解
* </p>
*
* @author yangkai.shen
* @date 2022-09-02 15:47
*/
public @interface DLock {
/**
* @return 锁的标识,支持 spel 表达式
*/
String lockKey() default "lock";

/**
* @return 锁的时间
*/
long lockTime() default 3000;

/**
* @return 锁的时间单位
*/
TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
}

+ 68
- 0
demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/aop/DistributedLockAspect.java View File

@@ -0,0 +1,68 @@
package com.xkcoding.distributed.lock.aop;

import com.xkcoding.distributed.lock.annotation.DLock;
import com.xkcoding.distributed.lock.api.DistributedLockService;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
* <p>
* 分布式锁切面
* </p>
*
* @author yangkai.shen
* @date 2022-09-02 21:02
*/
@Aspect
@RequiredArgsConstructor
public class DistributedLockAspect {
private final DistributedLockService distributedLockService;

@Around("@annotation(lock)")
public Object around(ProceedingJoinPoint pjp, DLock lock) throws Throwable {
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
Object[] args = pjp.getArgs();
String lockKey = lock.lockKey();
lockKey = parseExpression(lockKey, method, args);

long timeout = lock.lockTime();
TimeUnit timeUnit = lock.timeUnit();
return distributedLockService.lock(lockKey, timeout, timeUnit, () -> {
try {
return pjp.proceed();
} catch (Throwable e) {
throw new RuntimeException(e);
}
});
}

/**
* 解析 spel 表达式
*/
private String parseExpression(String expression, Method method, Object[] args) {
LocalVariableTableParameterNameDiscoverer nameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
String[] params = nameDiscoverer.getParameterNames(method);

ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
for (int i = 0; i < params.length; i++) {
context.setVariable(params[i], args[i]);
}
try {
return parser.parseExpression(expression).getValue(context, String.class);
} catch (SpelEvaluationException e) {
throw new RuntimeException("spel 表达式解析错误", e);
}
}
}

+ 44
- 0
demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/api/DistributedLock.java View File

@@ -0,0 +1,44 @@
package com.xkcoding.distributed.lock.api;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
* <p>
* 分布式锁接口
* </p>
*
* @author yangkai.shen
* @date 2022-09-02 21:30
*/
public abstract class DistributedLock implements Lock {
/**
* 锁的标识
*/
private final String lockKey;
/**
* 锁的时间
*/
private final long lockTime;
/**
* 锁的时间单位
*/
private final TimeUnit timeUnit;

protected DistributedLock(String lockKey, long lockTime, TimeUnit timeUnit) {
this.lockKey = lockKey;
this.lockTime = lockTime;
this.timeUnit = timeUnit;
}

@Override
public void lockInterruptibly() throws InterruptedException {
throw new UnsupportedOperationException("DistributedLock`s lockInterruptibly method is unsupported");
}

@Override
public Condition newCondition() {
throw new UnsupportedOperationException("DistributedLock`s newCondition method is unsupported");
}
}

+ 27
- 0
demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/api/DistributedLockService.java View File

@@ -0,0 +1,27 @@
package com.xkcoding.distributed.lock.api;

import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
* <p>
* 分布式锁实现
* </p>
*
* @author yangkai.shen
* @date 2022-09-02 21:11
*/
public interface DistributedLockService {

/**
* 锁
*
* @param lockKey 锁的标识
* @param lockTime 锁的时间
* @param timeUnit 锁的时间单位
* @param execute 执行逻辑
* @param <T> 返回值类型
* @return 返回值
*/
<T> T lock(String lockKey, long lockTime, TimeUnit timeUnit, Supplier<T> execute);
}

+ 23
- 0
demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/api/LockClient.java View File

@@ -0,0 +1,23 @@
package com.xkcoding.distributed.lock.api;

import java.util.concurrent.TimeUnit;

/**
* <p>
* 锁客户端
* </p>
*
* @author yangkai.shen
* @date 2022-09-02 21:48
*/
public interface LockClient {
/**
* 获取一把锁
*
* @param lockKey 锁的标识
* @param lockTime 锁的时间
* @param timeUnit 锁的时间单位
* @return 锁
*/
DistributedLock getLock(String lockKey, long lockTime, TimeUnit timeUnit);
}

+ 47
- 0
demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/api/impl/DistributedLockServiceImpl.java View File

@@ -0,0 +1,47 @@
package com.xkcoding.distributed.lock.api.impl;

import com.xkcoding.distributed.lock.api.DistributedLock;
import com.xkcoding.distributed.lock.api.DistributedLockService;
import com.xkcoding.distributed.lock.api.LockClient;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
* <p>
* 分布式锁实现
* </p>
*
* @author yangkai.shen
* @date 2022-09-02 21:41
*/
@Slf4j
@AllArgsConstructor
public class DistributedLockServiceImpl implements DistributedLockService {
private final LockClient lockClient;

/**
* 锁
*
* @param lockKey 锁
* @param timeout 超时时间
* @param timeUnit 超时单位
* @param execute 执行逻辑
* @return 返回值
*/
@Override
public <T> T lock(String lockKey, long timeout, TimeUnit timeUnit, Supplier<T> execute) {
DistributedLock lock = lockClient.getLock(lockKey, timeout, timeUnit);
lock.lock();

try {
return execute.get();
} catch (Throwable t) {
throw new RuntimeException(t);
} finally {
lock.unlock();
}
}
}

+ 41
- 0
demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/api/impl/DummyDistributedLock.java View File

@@ -0,0 +1,41 @@
package com.xkcoding.distributed.lock.api.impl;

import com.xkcoding.distributed.lock.api.DistributedLock;
import org.jetbrains.annotations.NotNull;

import java.util.concurrent.TimeUnit;

/**
* <p>
* 无锁实现
* </p>
*
* @author yangkai.shen
* @date 2022-09-02 21:32
*/
public class DummyDistributedLock extends DistributedLock {

protected DummyDistributedLock(String lockKey, long lockTime, TimeUnit timeUnit) {
super(lockKey, lockTime, timeUnit);
}

@Override
public void lock() {
// Do nothing.
}

@Override
public boolean tryLock() {
return true;
}

@Override
public boolean tryLock(long time, @NotNull TimeUnit unit) throws InterruptedException {
return true;
}

@Override
public void unlock() {

}
}

+ 29
- 0
demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/api/impl/DummyDistributedLockClient.java View File

@@ -0,0 +1,29 @@
package com.xkcoding.distributed.lock.api.impl;

import com.xkcoding.distributed.lock.api.DistributedLock;
import com.xkcoding.distributed.lock.api.LockClient;

import java.util.concurrent.TimeUnit;

/**
* <p>
* 获取一把虚拟锁
* </p>
*
* @author yangkai.shen
* @date 2022-09-02 21:53
*/
public class DummyDistributedLockClient implements LockClient {
/**
* 获取一把锁
*
* @param lockKey 锁的标识
* @param lockTime 锁的时间
* @param timeUnit 锁的时间单位
* @return 锁
*/
@Override
public DistributedLock getLock(String lockKey, long lockTime, TimeUnit timeUnit) {
return new DummyDistributedLock(lockKey, lockTime, timeUnit);
}
}

+ 31
- 0
demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/autoconfigure/DistributedLockConfiguration.java View File

@@ -0,0 +1,31 @@
package com.xkcoding.distributed.lock.autoconfigure;

import com.xkcoding.distributed.lock.api.DistributedLockService;
import com.xkcoding.distributed.lock.api.LockClient;
import com.xkcoding.distributed.lock.api.impl.DistributedLockServiceImpl;
import com.xkcoding.distributed.lock.api.impl.DummyDistributedLockClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* <p>
* 自动装配类
* </p>
*
* @author yangkai.shen
* @date 2022-09-02 21:57
*/
@Configuration(proxyBeanMethods = false)
public class DistributedLockConfiguration {
@Bean
@ConditionalOnMissingBean
public LockClient lockClient() {
return new DummyDistributedLockClient();
}

@Bean
public DistributedLockService distributedLockService(LockClient lockClient) {
return new DistributedLockServiceImpl(lockClient);
}
}

+ 15
- 0
demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/mapper/StockMapper.java View File

@@ -0,0 +1,15 @@
package com.xkcoding.distributed.lock.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xkcoding.distributed.lock.model.Stock;
import org.apache.ibatis.annotations.Mapper;

/**
* 货物 Mapper
*
* @author yangkai.shen
* @date 2022-09-02 14:09
*/
@Mapper
public interface StockMapper extends BaseMapper<Stock> {
}

+ 29
- 0
demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/model/Stock.java View File

@@ -0,0 +1,29 @@
package com.xkcoding.distributed.lock.model;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

/**
* 货物
*
* @author yangkai.shen
* @date 2022-09-02 14:07
*/
@Data
@TableName("db_stock")
public class Stock {
/**
* 主键
*/
@TableId
private Long id;
/**
* 货物名称
*/
private String name;
/**
* 货物总数
*/
private Long count;
}

+ 58
- 0
demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/service/StockService.java View File

@@ -0,0 +1,58 @@
package com.xkcoding.distributed.lock.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xkcoding.distributed.lock.annotation.DLock;
import com.xkcoding.distributed.lock.mapper.StockMapper;
import com.xkcoding.distributed.lock.model.Stock;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
* 模拟仓库 Service
*
* @author yangkai.shen
* @date 2022-09-02 14:05
*/
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class StockService {
private final StockMapper stockMapper;

/**
* 减货物
*/
@DLock(lockKey = "lock_stock_${stockId}", lockTime = 3000, timeUnit = TimeUnit.MICROSECONDS)
public void reduceStock(Long stockId) {
// 先查询库存是否充足
Stock stock = this.stockMapper.selectById(stockId);

// 再减库存
if (stock != null && stock.getCount() > 0) {
stock.setCount(stock.getCount() - 1);
this.stockMapper.updateById(stock);
}
}

/**
* 重置货物
*/
public void resetStock() {
log.info("start to init stock data...");

stockMapper.delete(new LambdaQueryWrapper<Stock>().gt(Stock::getId, 0));

Stock mockStock = new Stock();
mockStock.setId(1L);
mockStock.setName("测试商品");
mockStock.setCount(5000L);

stockMapper.insert(mockStock);

log.info("stock data has been initialized...");
}
}

+ 31
- 0
demo-distributed-lock/demo-distributed-lock-api/src/main/java/com/xkcoding/distributed/lock/task/InitStockTask.java View File

@@ -0,0 +1,31 @@
package com.xkcoding.distributed.lock.task;

import com.xkcoding.distributed.lock.service.StockService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

/**
* <p>
* 初始化数据
* </p>
*
* @author yangkai.shen
* @date 2022-09-02 15:35
*/
@Slf4j
@Component
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class InitStockTask implements ApplicationRunner {
private final StockService stockService;

@Override
public void run(ApplicationArguments args) throws Exception {
log.info("================================================");
stockService.resetStock();
log.info("================================================");
}
}

+ 2
- 0
demo-distributed-lock/demo-distributed-lock-api/src/main/resources/META-INF/spring.factories View File

@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.xkcoding.distributed.lock.autoconfigure.DistributedLockConfiguration

Loading…
Cancel
Save