久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

什么是接口的冪等性,如何實(shí)現(xiàn)接口冪等性?

 邸彥強(qiáng) 2020-11-22

什么是接口的冪等性,如何實(shí)現(xiàn)接口冪等性,?

(一)冪等性概念

冪等性原本是數(shù)學(xué)上的概念,用在接口上就可以理解為:同一個(gè)接口,,多次發(fā)出同一個(gè)請(qǐng)求,,必須保證操作只執(zhí)行一次。 調(diào)用接口發(fā)生異常并且重復(fù)嘗試時(shí),,總是會(huì)造成系統(tǒng)所無法承受的損失,,所以必須阻止這種現(xiàn)象的發(fā)生。 比如下面這些情況,,如果沒有實(shí)現(xiàn)接口冪等性會(huì)有很嚴(yán)重的后果: 支付接口,,重復(fù)支付會(huì)導(dǎo)致多次扣錢 訂單接口,同一個(gè)訂單可能會(huì)多次創(chuàng)建。

(二)冪等性的解決方案

唯一索引 使用唯一索引可以避免臟數(shù)據(jù)的添加,,當(dāng)插入重復(fù)數(shù)據(jù)時(shí)數(shù)據(jù)庫會(huì)拋異常,,保證了數(shù)據(jù)的唯一性。

樂觀鎖 這里的樂觀鎖指的是用樂觀鎖的原理去實(shí)現(xiàn),,為數(shù)據(jù)字段增加一個(gè)version字段,,當(dāng)數(shù)據(jù)需要更新時(shí),先去數(shù)據(jù)庫里獲取此時(shí)的version版本號(hào)

select version from tablename where xxx

更新數(shù)據(jù)時(shí)首先和版本號(hào)作對(duì)比,,如果不相等說明已經(jīng)有其他的請(qǐng)求去更新數(shù)據(jù)了,,提示更新失敗。

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

悲觀鎖 樂觀鎖可以實(shí)現(xiàn)的往往用悲觀鎖也能實(shí)現(xiàn),,在獲取數(shù)據(jù)時(shí)進(jìn)行加鎖,當(dāng)同時(shí)有多個(gè)重復(fù)請(qǐng)求時(shí)其他請(qǐng)求都無法進(jìn)行操作

分布式鎖 冪等的本質(zhì)是分布式鎖的問題,,分布式鎖正??梢酝ㄟ^redis或zookeeper實(shí)現(xiàn);在分布式環(huán)境下,,鎖定全局唯一資源,,使請(qǐng)求串行化,實(shí)際表現(xiàn)為互斥鎖,,防止重復(fù),,解決冪等。

token機(jī)制 token機(jī)制的核心思想是為每一次操作生成一個(gè)唯一性的憑證,,也就是token,。一個(gè)token在操作的每一個(gè)階段只有一次執(zhí)行權(quán),一旦執(zhí)行成功則保存執(zhí)行結(jié)果,。對(duì)重復(fù)的請(qǐng)求,,返回同一個(gè)結(jié)果。token機(jī)制的應(yīng)用十分廣泛,。

(三)token機(jī)制的實(shí)現(xiàn)

這里展示通過token機(jī)制實(shí)現(xiàn)接口冪等性的案例: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,、配置請(qǐng)求的方法體和枚舉類

首先配置一下通用的請(qǐng)求返回體

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

以及返回code

public enum ResponseCode {    // 通用模塊 1xxxx    ILLEGAL_ARGUMENT(10000'參數(shù)不合法'),    REPETITIVE_OPERATION(10001'請(qǐng)勿重復(fù)操作'),    ;    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以及構(gòu)造方法}

配置全局異常捕獲器

@ControllerAdvicepublic 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 編寫創(chuàng)建Token和驗(yàn)證Token的接口以及實(shí)現(xiàn)類

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

具體實(shí)現(xiàn)類,,核心的業(yè)務(wù)邏輯都寫在注釋中了

@Servicepublic class TokenServiceImpl implements TokenService {    @Autowired    private RedisTemplate redisTemplate;    @Override    public Response createToken() {        //生成uuid當(dāng)作token        String token = UUID.randomUUID().toString().replaceAll('-','');        //將生成的token存入redis中        redisTemplate.opsForValue().set(token,token);        //返回正確的結(jié)果信息        Response response=new Response(0,token.toString(),null);        return response;    }    @Override    public Response checkToken(HttpServletRequest request) {        //從請(qǐng)求頭中獲取token        String token=request.getHeader('token');        if (StringUtils.isBlank(token)){            //如果請(qǐng)求頭token為空就從參數(shù)中獲取            token=request.getParameter('token');            //如果都為空拋出參數(shù)異常的錯(cuò)誤            if (StringUtils.isBlank(token)){                throw new ServiceException(ResponseCode.ILLEGAL_ARGUMENT.getCode().toString(),ResponseCode.ILLEGAL_ARGUMENT.getMsg());            }        }        //如果redis中不包含該token,說明token已經(jīng)被刪除了,,拋出請(qǐng)求重復(fù)異常        if (!redisTemplate.hasKey(token)){            throw new ServiceException(ResponseCode.REPETITIVE_OPERATION.getCode().toString(),ResponseCode.REPETITIVE_OPERATION.getMsg());        }        //刪除token        Boolean del=redisTemplate.delete(token);        //如果刪除不成功(已經(jīng)被其他請(qǐng)求刪除),,拋出請(qǐng)求重復(fù)異常        if (!del){            throw new ServiceException(ResponseCode.REPETITIVE_OPERATION.getCode().toString(),ResponseCode.REPETITIVE_OPERATION.getMsg());        }        return new Response(0,'校驗(yàn)成功',null);    }}

3.4 配置自定義注解

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

@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){            // 校驗(yàn)通過放行,,校驗(yàn)不通過全局異常捕獲后輸出返回結(jié)果            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,,添加攔截器

@Configurationpublic class WebConfig implements WebMvcConfigurer {    @Override    public void addInterceptors(InterceptorRegistry registry) {        registry.addInterceptor(apiIdempotentInterceptor());    }    @Bean    public ApiIdempotentInterceptor apiIdempotentInterceptor() {        return new ApiIdempotentInterceptor();    }}

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

@Configurationpublic class RedisConfig {    //自定義的redistemplate    @Bean(name = 'redisTemplate')    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){        //創(chuàng)建一個(gè)RedisTemplate對(duì)象,,為了方便返回key為string,,value為Object        RedisTemplate<String,Object> template = new RedisTemplate<>();        template.setConnectionFactory(factory);        //設(shè)置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);    }}

(四)結(jié)果驗(yàn)證

首先通過token接口創(chuàng)建一個(gè)token出來,此時(shí)redis中也存在了該token

什么是接口的冪等性,,如何實(shí)現(xiàn)接口冪等性,?

在jmeter中同時(shí)運(yùn)行50個(gè)請(qǐng)求,我們可以觀察到,,只有第一個(gè)請(qǐng)求校驗(yàn)成功,,后續(xù)的請(qǐng)求均提示請(qǐng)勿重復(fù)操作。

什么是接口的冪等性,,如何實(shí)現(xiàn)接口冪等性,?
什么是接口的冪等性,如何實(shí)現(xiàn)接口冪等性,?

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多