私有云network侧接口幂等实现

需求背景

私有云碧桂园生产环境用脚本调用创建接口,有重复调用请求,重复请求落到业务层并发执行,导致在门户会有数据被重复创建的现象。对此情况,需要有接口对接口实现幂等。

CMP项目现状

1.接口均符合restful接口规范,且post类请求的api的入参只有一个,且类型为DTO。
2.springBoot版本为2.0.4.RELEASE,对应spring-data-redis为2.0.9.RELEASE。redisson版本为3.10.1。
2.1 spring-data-redis版本过低,暂不支持setnx操作中携带ttl,如需确保原子操作得自行写lua脚本,或者升级版本至2.1以上。
2.2 redisson已支持分布式锁,且底层已尽可能确保了分布式锁的高可用。

业务功能设计

设计切面,在切面中获取方法/接口的入参,对入参进行重复校验。

功能实现问题以及处理方案

Q1.并不是所有接口都需要进行幂等,且需要方法幂等的接口难以用一个标准统一。
-- 自定义方法型注解,对注解进行aop。提供具有幂等功能的注解。

Q2.不同接口的幂等限制或时长不一。
-- 注解提供限制时长参数,供开发自定义。

Q3.参数体过于旁大,在与redis交互中占用太多网络资源和内存资
源。
-- 对参数体字符串进行信息摘要MD5处理,得到32位固定长度唯一标识。

Q4. Spring-data-redis 与 redisson的选择。
-- Spring-data-redis 需要版本升级或自行实现,对于支持java服务操作redis的setxn+ttl操作的高可用性有更多不确定的风险;
redisson已提供分布式锁的功能,底层原理就是对setnx+ttl的封装,底层已经尽可能的规避了此操作的高可用风险。只需要把tryLock()的等待时间设置为0,便可复用此功能来实现setnx+ttl操作。
所以此处选择用redisson来实现。

Q5. 单纯的根据报文体来做,只能实现基本的幂等,未跟业务结合,关键参数不变其他参数变了的情况还是需要业务层做处理。
-- 因为CMP的服务对外接口都是DTO统一入参,所以定义变量注解,只对有这个注解的字段做MD5。(此处刚好可以复用swagger的注解,达到减少注解复杂度的情况。尽可能少的改动原有代码)

核心代码实现

1.注解(Idempotence)

image.png
  1. aop(Aspect)
public void doBefore(JoinPoint joinPoint,Idempotence idempotence) throws CmpCheckException{
        log.debug(">>>>>>>>>>>>>>>> begin idempotence >>>>>>>>>>>>>>>>>>");
         Object[] datas =  joinPoint.getArgs();
         //  需要实现幂等的接口在cmp都是以dto的形式作为入参,所以此处可进行约定限制
         if(datas==null || datas.length!=1){
             log.error("幂等功能仅对单个入参的方法开放!");
             return;
         }
        Field[] fields = datas[0].getClass().getDeclaredFields();
        String stringKey="";
        for(Field field:fields){
            field.setAccessible(true);
            try{
                ApiModelProperty p =field.getAnnotation(ApiModelProperty.class);
                if(p!=null && p.required()){
                    stringKey+=field.get(datas[0])+":";
                }
            }catch (Exception e){
                log.error("反射异常! /n {}",e.getMessage());
            }
        }
         //对入参进行md5之后作为key,用以缩减key的长度,减小网络传输和redis内存的压力
         String md5s =MD5Utils.encodeByMD5(stringKey);
         String key ="idempotence:"+md5s;
        RLock lock = redissonClient.getLock(key);
        Boolean b=true;
        try {
             b = lock.tryLock(0L,idempotence.idempotenceTime(),idempotence.timeUnit());
        }catch (Exception e){
            log.error(">>>>>>>>>>  tryLock()处理异常 >>>>>>>>> /n {}",e.getMessage());
        }
        if(!b){
          throw  new CmpCheckException("请勿重复提交!","");
        }
    }

3.信息摘要算法 MD5 处理

image.png

功能测试

image.png

注:此处没有code,是因为次测试代码暂未结合cmp的异常码机制(涉及国际化),等之后正式下需求确定开发版本分支再在其上进行处理。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。