日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

什么是接口的冪等性,如何實現接口冪等性?

(一)冪等性概念

冪等性原本是數學上的概念,用在接口上就可以理解為:同一個接口,多次發出同一個請求,必須保證操作只執行一次。 調用接口發生異常并且重復嘗試時,總是會造成系統所無法承受的損失,所以必須阻止這種現象的發生。 比如下面這些情況,如果沒有實現接口冪等性會有很嚴重的后果: 支付接口,重復支付會導致多次扣錢 訂單接口,同一個訂單可能會多次創建。

(二)冪等性的解決方案

唯一索引 使用唯一索引可以避免臟數據的添加,當插入重復數據時數據庫會拋異常,保證了數據的唯一性。

樂觀鎖 這里的樂觀鎖指的是用樂觀鎖的原理去實現,為數據字段增加一個version字段,當數據需要更新時,先去數據庫里獲取此時的version版本號

select version from tablename where xxx

更新數據時首先和版本號作對比,如果不相等說明已經有其他的請求去更新數據了,提示更新失敗。

update tablename set count=count+1,version=version+1 where version=#{version}

悲觀鎖 樂觀鎖可以實現的往往用悲觀鎖也能實現,在獲取數據時進行加鎖,當同時有多個重復請求時其他請求都無法進行操作

分布式鎖 冪等的本質是分布式鎖的問題,分布式鎖正常可以通過redis或zookeeper實現;在分布式環境下,鎖定全局唯一資源,使請求串行化,實際表現為互斥鎖,防止重復,解決冪等。

token機制 token機制的核心思想是為每一次操作生成一個唯一性的憑證,也就是token。一個token在操作的每一個階段只有一次執行權,一旦執行成功則保存執行結果。對重復的請求,返回同一個結果。token機制的應用十分廣泛。

(三)token機制的實現

這里展示通過token機制實現接口冪等性的案例:github文末自取 首先引入需要的依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.Apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.4</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

3.1、配置請求的方法體和枚舉類

首先配置一下通用的請求返回體

public class Response {
    private int status;
    private String msg;
    private Object data;
    //省略get、set、toString、無參有參構造方法
}

以及返回code

public enum ResponseCode {
    // 通用模塊 1xxxx
    ILLEGAL_ARGUMENT(10000, "參數不合法"),
    REPETITIVE_OPERATION(10001, "請勿重復操作"),
    ;
    ResponseCode(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }
    private Integer code;
    private String msg;
    public Integer getCode() {
        return code;
    }
    public void setCode(Integer code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
}

3.2 自定義異常以及配置全局異常類

public class ServiceException extends RuntimeException{
    private String code;
    private String msg;
    //省略get、set、toString以及構造方法
}

配置全局異常捕獲器

@ControllerAdvice
public class MyControllerAdvice {
    @ResponseBody
    @ExceptionHandler(ServiceException.class)
    public Response serviceExceptionHandler(ServiceException exception){
        Response response=new Response(Integer.valueOf(exception.getCode()),exception.getMsg(),null);
        return response;
    }
}

3.3 編寫創建Token和驗證Token的接口以及實現類

@Service
public interface TokenService {
    public Response createToken();
    public Response checkToken(HttpServletRequest request);
}

具體實現類,核心的業務邏輯都寫在注釋中了

@Service
public class TokenServiceImpl implements TokenService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public Response createToken() {
        //生成uuid當作token
        String token = UUID.randomUUID().toString().replaceAll("-","");
        //將生成的token存入redis中
        redisTemplate.opsForValue().set(token,token);
        //返回正確的結果信息
        Response response=new Response(0,token.toString(),null);
        return response;
    }

    @Override
    public Response checkToken(HttpServletRequest request) {
        //從請求頭中獲取token
        String token=request.getHeader("token");
        if (StringUtils.isBlank(token)){
            //如果請求頭token為空就從參數中獲取
            token=request.getParameter("token");
            //如果都為空拋出參數異常的錯誤
            if (StringUtils.isBlank(token)){
                throw new ServiceException(ResponseCode.ILLEGAL_ARGUMENT.getCode().toString(),ResponseCode.ILLEGAL_ARGUMENT.getMsg());
            }
        }
        //如果redis中不包含該token,說明token已經被刪除了,拋出請求重復異常
        if (!redisTemplate.hasKey(token)){
            throw new ServiceException(ResponseCode.REPETITIVE_OPERATION.getCode().toString(),ResponseCode.REPETITIVE_OPERATION.getMsg());
        }
        //刪除token
        Boolean del=redisTemplate.delete(token);
        //如果刪除不成功(已經被其他請求刪除),拋出請求重復異常
        if (!del){
            throw new ServiceException(ResponseCode.REPETITIVE_OPERATION.getCode().toString(),ResponseCode.REPETITIVE_OPERATION.getMsg());
        }
        return new Response(0,"校驗成功",null);
    }
}

3.4 配置自定義注解

這是比較重要的一步,通過自定義注解在需要實現接口冪等性的方法上添加此注解,實現token驗證

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiIdempotent {
}

接口攔截器

public class ApiIdempotentInterceptor implements HandlerInterceptor {
    @Autowired
    private TokenService tokenService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod= (HandlerMethod) handler;
        Method method=handlerMethod.getMethod();
        ApiIdempotent methodAnnotation=method.getAnnotation(ApiIdempotent.class);
        if (methodAnnotation != null){
            // 校驗通過放行,校驗不通過全局異常捕獲后輸出返回結果
            tokenService.checkToken(request);
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }
}

3.5 配置攔截器以及redis

配置webConfig,添加攔截器

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(apiIdempotentInterceptor());
    }

    @Bean
    public ApiIdempotentInterceptor apiIdempotentInterceptor() {
        return new ApiIdempotentInterceptor();
    }
}

配置redis,使得中文可以正常傳輸

@Configuration
public class RedisConfig {
    //自定義的redistemplate
    @Bean(name = "redisTemplate")
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
        //創建一個RedisTemplate對象,為了方便返回key為string,value為Object
        RedisTemplate<String,Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        //設置json序列化配置
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer=new
                Jackson2JsonRedisSerializer(Object.class);
        ObjectMApper objectMapper=new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance);
        //string的序列化
        StringRedisSerializer stringRedisSerializer=new StringRedisSerializer();
        //key采用string的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        //value采用jackson的序列化方式
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //hashkey采用string的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        //hashvalue采用jackson的序列化方式
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

最后是controller

@RestController
@RequestMapping("/token")
public class TokenController {
    @Autowired
    private TokenService tokenService;

    @GetMapping
    public Response token(){
        return tokenService.createToken();
    }

    @PostMapping("checktoken")
    public Response checktoken(HttpServletRequest request){
        return tokenService.checkToken(request);
    }
}

(四)結果驗證

首先通過token接口創建一個token出來,此時redis中也存在了該token

什么是接口的冪等性,如何實現接口冪等性?

 

在jmeter中同時運行50個請求,我們可以觀察到,只有第一個請求校驗成功,后續的請求均提示請勿重復操作。

什么是接口的冪等性,如何實現接口冪等性?

 


什么是接口的冪等性,如何實現接口冪等性?

 

分享到:
標簽:接口
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定