ビジネス権利サービスのため、同じデータが複数のユーザーによって操作される可能性があるため、共有リソース操作の競争の問題があります。ビジネスに対して個別に制御される場合、コストは比較的高くなります。特定のコントロールはビジネスに結合する必要があります。標準化されていない場合、それは非常に高いです競合デッドロックの問題が発生しやすいため、AOPに基づいて分散ロックの開始を共有します。一緒に議論することを歓迎し、強化のための提案を歓迎します。
ここでは、aopのMethodInterceptorがメソッドレベルのインターセプトに使用され、プロセス全体がアノテーションを使用して完了します。
1.主な注意事項
1. Redission構成アノテーションを開始します。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({RedissonConfiguration.class, LockMethodHelper.class})
public @interface EnableRedissonLock {
}
2.メソッドがロックを開始するかどうか
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedLock {
String fieldKey();
}
3.Keyステッチノート:
@Documented
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface KeyParam {
/**
* 如果动态参数在command对象中,那么就需要设置columns的值为command对象中的属性名可以为多个,否则不需要设置该值
* <p>例1:public void test(@KeyParam({"id"}) MemberCommand member)
* <p>例2:public void test(@KeyParam({"id","loginName"}) MemberCommand member)
* <p>例3:public void test(@KeyParam String memberId)
*/
String[] columns() default {};
/**
* 获取锁等待时间
*
* @return 等待时间
*/
long waitTime() default 1L;
/**
* 锁释放时间
*
* @return 自动释放时间
*/
long leaseTime() default 5L;
}
第二に、Redissionの構成
1.基本的な構成パラメーターのカプセル化
@Setter
@Getter
@Configuration
@ConfigurationProperties(prefix = "spring.redisson")
public class RedissonProperties {
/**
* redis访问地址
*/
private String address;
/**
* 配置模式
*/
private String connectModel;
/**
* 密码
*/
private String password;
/**
* 超时时间
*/
private int timeout;
/**
* 连接池大小
*/
private int connectionPoolSize;
/**
* Minimum idle Redis connection amount.
*/
private int connectionMinimumIdleSize;
}
2.利用可能なRedissonClient(現在のスタンドアロンモード)を構成して実行します。
@Configuration
@EnableConfigurationProperties(RedissonProperties.class)
public class RedissonConfiguration {
@Bean
@ConditionalOnMissingBean(RedissonProperties.class)
@ConditionalOnProperty(prefix = "spring.redisson", value = {"address", "password"})
RedissonClient redissonSingleClient(RedissonProperties redissonProperties) {
Config config = new Config();
config.useSingleServer()
.setAddress(redissonProperties.getAddress())
.setTimeout(redissonProperties.getTimeout())
.setConnectionPoolSize(redissonProperties.getConnectionPoolSize())
.setConnectionMinimumIdleSize(redissonProperties.getConnectionMinimumIdleSize())
.setPassword(redissonProperties.getPassword());
return Redisson.create(config);
}
}
3.ターゲットメソッド:
@Slf4j
public class LockMethodHelper implements MethodInterceptor {
public static final String BEFORE_KEY = "LOCK:";
@Autowired(required = false)
private RedissonClient redissonClient;
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
Method method = methodInvocation.getMethod();
Object[] args = methodInvocation.getArguments();
//锁key
String key = builderKey(method, args);
// key不存在的情况下,不加锁处理,直接执行目标方法
if (StringUtils.isEmpty(key)) {
return methodInvocation.proceed();
}
RLock lock = redissonClient.getLock(key);
try {
if (lock.tryLock()) {
// 通过反射机制调用目标方法
return methodInvocation.proceed();
} else {
log.warn("资源竞争太大了,获取锁失败了,key为:{}", key);
throw new LockException("人太多了,系统小哥已经处理不过来了...");
}
} catch (Exception e) {
log.error("执行目标方法抛出异常,异常信息为:{}", e);
throw e;
} finally {
lock.unlock();
}
}
private String builderKey(Method method, Object[] args) throws NoSuchFieldException, IllegalAccessException {
NeedLock needLock = method.getAnnotation(NeedLock.class);
if (null == needLock) {
return null;
}
// 锁的前半部分key
String key = BEFORE_KEY + needLock.fieldKey();
// 迭代全部参数的注解,根据使用KeyParam的注解的参数所在的下标,来获取args中对应下标的参数值拼接到前半部分key上
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for (int i = 0; i < parameterAnnotations.length; i++) {
for (Annotation annotation : parameterAnnotations[i]) {
// 当前参数的注解不包含keyparam
if (!annotation.annotationType().getClass().isInstance(KeyParam.class)) {
continue;
}
// 当前参数的注解包含keyparam,获取注解配置的值
String[] columns = ((KeyParam) annotation).columns();
if (columns.length == 0) {
// 普通数据类型直接拼接
if (null == args[i]) {
log.error("动态参数不能为null!");
throw new RuntimeException("动态参数不能为null!");
}
key += args[i];
} else {
// keyparam的columns值不为null,所以当前参数应该是对象类型
for (int j = 0; j < columns.length; j++) {
Class<? extends Object> clasz = args[i].getClass();
Field declaredField = clasz.getDeclaredField(columns[j]);
declaredField.setAccessible(true);
Object value = declaredField.get(args[i]);
key += value;
}
}
}
}
return key;
}
}
4、ユースケースのデモ:
1.傍受範囲の構成。ユーザーが傍受パッケージの範囲を構成します。
@EnableRedissonLock
@Configuration
public class DisturiteLockConfig {
public static final String traceExecution = "execution(* com.workstation.biz.service..*.*(..))";
@Autowired
LockMethodHelper lockMethodHelper;
@Bean
public DefaultPointcutAdvisor defaultPointcutAdvisor() {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(traceExecution);
// 配置增强类advisor
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
advisor.setPointcut(pointcut);
advisor.setAdvice(lockMethodHelper);
return advisor;
}
}
2.方法:
// 基于对象的使用
@NeedLock(fieldKey = "user:uuid")
public void userChange(@KeyParam(columns = {"uuid"}) UserDTO user) {
}
// 基于字段的使用
@NeedLock(fieldKey = "user:uuid")
public void userChange(@KeyParam String uuid) {
}