idemopent-幂等-工具设计说明.md 5.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
# 幂等工具设计说明

> Author : kongxiang
>
> Date Time : 2023-08-27


## 1. 概述
希望spring环境中,快速支持接口幂等能力。简单引入,自动配置。通过注解就可以直接使用。
无需复杂配置

功能:
13 14 15
- [x] 基于spring框架
- [x] 直接进入jar,即可直接使用
- [x] 通过`@Idempotent`注解一键赋能类或方法幂等性
16
- [ ] 支持指定token生成算法
17
- [x] 可支持自定义实现token缓冲存储
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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164


## 2. 如何引入
```xml
     <dependency>
                <groupId>com.kongxiang</groupId>
                <artifactId>kongxiang-spring</artifactId>
                <version>{version}</version>
            </dependency>
```

## 3. 如何使用

### 3.1 设计思路
![](./idempotent-class.png)

#### 3.1.1 使用设计
要求:开发人员通过配置一个注解`@Idempotent`即可使注解的方法调用时,实现幂等性。

幂等性方案:
- [x] 防重token令牌

设计:
1. 基于环境Spring 框架。所以采用Spring Aop 拦截注解方法和类。自定义 幂等 逻辑,实现幂等要求。

2. 梳理流程: client请求【携带token和userid】-> 服务端Aop拦截【配置@Idempotent 】-> 获取注解信息 -> 认证token和userId -> 通过,执行业务逻辑,失败返回错误信息

3. 抽象出存储接口`TokenCache`和请求体传输token方式`TokenHolder`

4. @Idempontent注解在类和方法上的优先级设计:方法上注解配置信息优先于类上注解信息。注意:只是覆盖重复配置。

5. TokenCache 存储接口实现,是抽象出的一个接口。支持存储自定义配置方案。该框架:已经实现的方式:1. InMemory 2. Reids 
   > 根据接入环境的配置。如果已经接入`spring-boot-starter-redis`和连接redis服务成功。那么将会使用`RedisTokenCache`,否则默认使用`InMemoryTokenCache`.如果是redis,可以使用application.yml配置相关属性【为实现】

6. 设计请求体提供token接口的目的,是兼容在不同spring环境下,直接调用注解@Idempotent方法时,提供给Aop防重Token处理逻辑校验中去获取token和value的能力。如:在正常spring web环境中,采用的是HttpSerlvetRequest中去获取token和value。而在非spring环境中,也需要自动提供获取token的方式。根据web中底层实现原理。采用ThreadLocal绑定线程的特点,实现aop在内部线程获取token。

#### 3.1.2 具体接入

#### 3.1.2.1 引入包

```xml
<dependency>
                <groupId>com.kongxiang</groupId>
                <artifactId>kongxiang-spring</artifactId>
                <version>{version}</version>
            </dependency>
```



#### 3.1.2.2 注解方法@Idempotent

支持 注解类 和注解方法

- 注解类:类下面所有的方法被调用时,执行幂等逻辑
- 注解方法: 该方法被调用时,执行幂等逻辑

```java
@Idempotent
public class HelloWorld{
    
    public void a(){
        
    }
    @Idempotent
    public void b(){
        
    }
}
```

#### 3.1.2.3 开发代码

- 在普通方法中调用

  ```java
  @Compoment
  public class Client {
      
       /**TODO 这个目前是个具体类,后续需要改造 幂等实现方式接口IdempotentCaseHanlder,该类只是接口的一个实现类而已 */
      @Autowire
      private AntiDuplicationTokenHanlder tokenHanlder;
   
      @Autowire
      private HelloWorld helloWorld;// 自己开发的幂等注解服务
      
      private Stirng token;
      // 用户信息最开始就提供了
      private String userInfo = getUseInfo();
      
      // 1. 客户端先获取token
      public void requestGetToken(String userInfo){
          this.token = tokenHandler.getToken(userInfo);
      }
      // 2. 执行幂等函数
      public void execute(){
          requestGetToken(userInfo );
          // 传递参数 
          // 不加这行,该线程传递的参数是之前的。userInfo验证不通过。也不会执行业务逻辑
          TokenHolder.setToken(this.token,this.userInfo);
          // 执行幂等操作
          helloWorld.b();
          //第二次和之后操作会报错
          helloWorld.b(); 
          
         // -------
          requestGetToken(userInfo+"1");
          TokenHolder.setToken(this.token,this.userInfo);
          
          helloWorld.b();// 报错 userInfo 验证失败
          TokenHolder.setToken(this.token,this.userInfo+"1"); 
  		helloWorld.b();// 成功
          helloWorld.b();// 报错 userInfo 验证失败
      }
  }
  ```

- 在spring web中接口中使用

  1. 实现 getToken接口【必须提供给客户端获取token的接口】
  2. 在注解使用

```java
@RestController
@RequestMapping
// 注解类:类中所有方法都幂等
@Idempotent
public class Service{
    /**TODO 这个目前是个具体类,后续需要改造 幂等实现方式接口IdempotentCaseHanlder,该类只是接口的一个实现类而已 */
    @Autowire
    private AntiDuplicationTokenHanlder tokenHanlder;
    // [防重token令牌方案必须提供给客户端,其他自定义方案不定]
    @RequestMapping("/getToken")
    public String getToken(){
        return tokenHanlder.getToken();
    }
    
    @RequestMapping("/login")
    // 只有该方法幂等
    @Idempotent
    public Object login(){
        ...
    }
    
}
```