AOP + 责任链 == 幂等性校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
1. 定义一个注解:包含:
1. 返回信息
2. 校验类型(枚举,基于什么场景:入参、Token、SPEL)
3. 校验场景(MQ、RestAPI)
4. 过期时间

2. 创建一个切面类:
1. 通过JoinPoint获取具体注解:从连接点中获取@Idempotent注解(包含幂等配置:type、scene、message等)
Method targetMethod = joinPoint.getTarget()
.getClass()
.getDeclaredMethod(
methodSignature.getName(), // 方法名
methodSignature.getMethod().getParameterTypes() // 方法参数类型
);


2. 配置类将各种处理器注册为bean:
@Bean
@ConditionalOnMissingBean
public IdempotentSpELService idempotentSpELByRestAPIExecuteHandler(RedissonClient redissonClient) {
return new IdempotentSpELByRestAPIExecuteHandler(redissonClient);
}


3. 根据场景、类型获取具体拦截类:
1)切面类中:
IdempotentExecuteHandler instance = IdempotentExecuteHandlerFactory
.getInstance(idempotent.scene(), idempotent.type());
2)工厂获取:
public final class IdempotentExecuteHandlerFactory {
/**
* 获取幂等执行处理器
* @param scene 指定幂等验证场景类型
* @param type 指定幂等处理类型
* @return 幂等执行处理器
*/
public static IdempotentExecuteHandler getInstance(IdempotentSceneEnum scene, IdempotentTypeEnum type) {
IdempotentExecuteHandler result = null;
switch (scene) {
case RESTAPI -> {
switch (type) {
case PARAM -> result = ApplicationContextHolder.getBean(IdempotentParamService.class);
case TOKEN -> result = ApplicationContextHolder.getBean(IdempotentTokenService.class);
case SPEL -> result = ApplicationContextHolder.getBean(IdempotentSpELByRestAPIExecuteHandler.class);
default -> {
}
}
}
case MQ -> result = ApplicationContextHolder.getBean(IdempotentSpELByMQExecuteHandler.class);
default -> {
}
}
return result;
}
}


4. 执行处理器对应的excute

5. 通过joinPoint.proceed();执行业务

6. 执行处理器后置处理

7. 清理上下文


3. 幂等处理器:(SPEL幂等为列子)
1. interface IdempotentExecuteHandler 核心处理器:定义主要方法
1). handel: 具体的幂等处理
2). excute: 执行幂等处理逻辑(组合handel和其他)
3). exceptionProcessing: 幂等处理异常处理
4). postProcessing: 幂等处理后置处理

2. abstract class AbstractIdempotentExecuteHandler implements IdempotentExecuteHandler 抽象幂等处理器:定义抽象方法
1). protected abstract IdempotentParamWrapper buildWrapper(ProceedingJoinPoint joinPoint); 构建幂等参数包装器

2). public void execute(ProceedingJoinPoint joinPoint, Idempotent idempotent) {
// 1:调用抽象方法构建参数包装器,由子类实现具体逻辑
// 构建后通过setIdempotent方法将注解配置存入包装器
IdempotentParamWrapper idempotentParamWrapper = buildWrapper(joinPoint).setIdempotent(idempoten
// 2:执行具体的幂等处理逻辑(由子类实现handler方法)
handler(idempotentParamWrapper);
}

3. public final class IdempotentSpELByRestAPIExecuteHandler extends AbstractIdempotentExecuteHandler 实现类(基于REST API和SPEL)
1). 构建锁:通过切点获取传入的参数,构建锁名称
2). 获取锁:通过Redission获取锁,失败则表明已有一次,直接报错(返回@Idempotent中的message)
3). 获取锁成功:将锁放入threadLocal中供后续释放使用
4). 在切面中执行后置处理:从上下文threadLocal获取Redission锁,并释放锁
5). 异常处理:从threadLocal中获取Redission锁,并释放锁


4. 描述:
幂等性注解与切面:
1).先定义一个注解,注解包含:返回信息,场景,类型
2).定义一个切面,现有@Around注解环绕通知所有注解的方法
3).从joinPoint中获取具体注解并且获取类型和场景
4).根据类型和场景从幂等性校验工厂中获取对应的拦截器,
5).拦截器执行拦截逻辑(切面将连接点传入拦截器)
6).执行业务流程
7).执行后置处理
8).业务方法执行异常就会执行拦截器的异常处理方法

具体校验拦截器:
1).拦截器接口定义主要方法:
handel: 具体的幂等处理
excute: 执行幂等处理逻辑(组合handel和其他)
exceptionProcessing: 幂等处理异常处理
postProcessing: 幂等处理后置处理
2).抽象拦截器:
模板方法组合handel和新建的buildWrapper方法
3).具体拦截器:
BuidWarapper: 通过参数来构建Key方便后续加锁
handel : 通过BuidWarapper返回的Key进行Redission加锁,失败则说明已有请求,报错;成功则将锁放入IdempotentContext(ThreadLocal)供后续释放使用
4).后置处理和异常处理:
不管是否获取到锁,释放锁

责任链模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
描述:
责任链选择器:
1. 在配置类中将其注册为Bean且@ConditionalOnMIssingBean
2. 实现CommandLineRunner接口,在应用启动时加载所有拦截器实现类并存入Map中
3. 提供choose方法根据标识(枚举)选择具体拦截器
4. 根据拦截器具体类执行对应的handel方法

责任链具体实现:
1. 抽象责任链拦截器接口:
1):Mark()责任链组件标识
2):handel()具体处理方法
3):继承Ordered接口,实现getOrder()方法,定义责任链顺序
2. 各个具体的拦截器接口:
1):注册拦截器,车票查询拦截器,车票购买拦截器
2):具体的Mark(枚举)标识责任链
3. 具体的拦截器类(注册拦截器):
1):handel()具体处理方法,校验Service层传入的参数是否合法
2):getOrder()定义责任链顺序,在责任链选择器通过ApplicationContextHolder获取所有实现类并存入Map时会根据getOrder顺序存入

策略模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
1. 策略接口{
mark()​:返回策略的唯一标识符,用于策略路由和识别

​patternMatchMark()​:支持正则表达式匹配的策略标识,增强灵活性

​execute(REQUEST)​:执行策略的无返回值版本,适用于异步处理场景

​executeResp(REQUEST)​:执行策略的有返回值版本,适用于需要返回结果的场景
}

2. 在配置类中选择器添加为Bean

3. 策略模式选择器:{
1. 定义Map存储各种策略:
private final Map<String, AbstractExecuteStrategy> abstractExecuteStrategyMap = new HashMap<>();
// 按mark分组排序存入
public void onApplicationEvent(ApplicationInitializingEvent event) {
// 从Spring容器中获取所有AbstractExecuteStrategy类型的Bean(即所有策略实现类)
Map<String, AbstractExecuteStrategy> actual = ApplicationContextHolder.getBeansOfType(AbstractExecuteStrategy.class);
actual.forEach((beanName, bean) -> {
// 检查策略标识是否重复(同一mark只能对应一个策略)
AbstractExecuteStrategy beanExist = abstractExecuteStrategyMap.get(bean.mark());
if (beanExist != null) {
throw new ServiceException(String.format("[%s] 存在重复的执行策略", bean.mark()));
}
// 以策略标识为key,策略实例为value,存入缓存容器
abstractExecuteStrategyMap.put(bean.mark(), bean);
});
}

2. choose选择(1). 根据mark选择对应的策略
2). 根据正则匹配

3. chooseAndExecute:通过choose获取具体的策略,再通过具体的策略执行execute
}

4. 支付策略(支付宝){
1. 继承抽象支付类,实现抽象执行策略接口
抽象支付类:payResponse:支付并响应
抽象策略执行接口:mark excute executeResp

2. 根据aliPay具体文档执行
}

5. 选座策略:在购票后进入座位选择器,座位选择器通过座位类型进行分组并分别进入不同的分配策略模式
1. 商务策略:自定义选择
2. 一等座
3. 二等座
完善订单详情并远程调用


6. 描述:
策略选择器:
1. 抽象策略模式选择器类,实现ApplicationListener接口,重写onApplicationEvent方法利用ApplicationContext获取抽象策略执行类,遍历以Mark为key存入Map中
2. 通过Chose方法根据mark或者正则匹配获取具体的策略
3. 通过chooseAndExecute方法获取具体策略并执行

抽象策略执行接口:
1. mark()标识方法
2. patternMathMark()执行策略匹配标识符
3. excuteResp()执行策略,带返回值
4. excute()执行策略,无返回值

具体策略类(支付宝支付):
1. 继承抽象支付类(Pay方法),实现抽象策略执行接口
2. executeResp()中调用Pay()方法
3. Pay()方法中使用AliClient进行具体支付操作

布隆过滤器

1
2
3
4
1. 封装的缓存工具类中,put时添加key到布隆过滤器,get时判断key是否存在于布隆过滤器中。避免缓存穿透

2. 用户名注册:用户注册时添加用户名到布隆过滤器中,注册时先判断用户名是否存在于布隆过滤器中。

令牌桶

1
2
3
4
5
6
7
8
9
1. 列车购票时:根据列车id+前缀获取令牌桶
1. 若没有令牌桶:先上锁 + 双重检查
2. 获取当前车有的座位类型
3. 查询数据库获取当前车在各个站台间的余票数量
4. 加载lua脚本对传参的座位类型+列车号进行对应的缓存减少

2. service层加本地,分布式锁后进行数据库的扣减

3. 回滚事件发生触发,对缓存中的令牌桶进行增加

MQ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1. 支付返回MQ
1. 订单生成:(令牌桶)下单后调用选择座位并完善车票信息。
2. 完善订单信息,调用订单服务添加订单
3. 构建延迟信息详情,发送。时间到会判断订单支付情况。
4. 若支付

2. 订单支付回调
1. 支付成功后调用Controller层/api/pay-service/callback/alipay
2. 将参数转为订单信息
3. 将其转换为AliPayCallbackRequest对象,主要包含mark
4. 策略选择器选择具体的支付策略进行支付成功后的处理
5. aliCallBack调用payService的callBack方法
6. callBack方法更新订单状态,发送MQ
7. MQ监听者收到后更新支付时间,调用订单状态反转,

3. 取消订单:TicketService中开始
1. 调用OrderService的cancelOrder方法更新订单状态
2. 座位解锁
3. 缓存余票更新
4. 令牌桶回滚

4. 订单超时:
1. 订单生成后自动发送延迟信息
2. 座位解锁
3. 缓存余票更新
4. 令牌桶回滚