幂等性非侵入式实现

幂等性
今天我们来谈谈什么是幂等性
引用百度百科的解析如下:
幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中。
在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。例如,“setTrue()”函数就是一个幂等函数,无论多次执行,其结果都是一样的.更复杂的操作幂等保证是利用唯一交易号(流水号)实现。

这解析,确实有点了,大家话看看就行了!!!(●'◡'●)
那对于我们程序员来说,我们关心的更多是下面这些问题:
什么地方,什么场景下需要用到幂等?
幂等,我们需要怎么做,如何实现幂等呢?

什么场景下需要用到幂等

  • 前端表单重复提交问题
  • 用户订单支付问题
  • 银行业务办理取号问题
  • 用户恶意进行调接口问题
  • 接口超时重复提交问题
  • MQ消息进行重复消费
  • ...

当然了,还有很多场景会用到幂等,这里咱们就不一一列举出来了。
那我们要如何设计一个幂等功能呢,而且还是代码非侵入式
代码非侵入式的意思,就是,我们的业务逻辑代码,不需要处理幂等校验的逻辑。
业务功能不处理?那交给谁处理呢?别着急,听哥们一一道来。^_^
这里,要实现代码非侵入式的幂等校验,我们就要使用到切面编程了(@Aspect
幂等的实现原理
在系统中一些接口需要增加幂等处理,幂等的概念是一个业务请求只能执行一次。类似银行业务办理,首先需要取一个号,然后用户使用这个号去柜台办理业务。这个号只能使用一次,如果过期或者已办理这个号就无效了。
我们的幂等也是使用这种原理。

  • 1.首先客户端调用通过我们的系统获取一个号,我们称之为幂等号,这个号已经存在我们的系统中。
  • 2.客户端使用这个号,调用我们的接口。
  • 3.我们系统判断这个号在我们的系统中已经存在,如果存在则允许业务办理,如果不存在,则表示这是一个非法的号,我们直接抛出异常。
  • 4.当业务处理完成,我们会将这个号从我们的系统中删除掉。


好了,这实现步骤,也是十分清晰了呀!!!^_^
那么我们下面就来看代码如何实现了
幂等的代码实现

  • 定义一个幂等处理接口
public interface Idempotence {
    /**
     * 检查是否存在幂等号
     * @param idempotenceId 幂等号
     * @return 是否存在
     */
    boolean check(String idempotenceId);

    /**
     * 记录幂等号
     * @param idempotenceId 幂等号
     */
    void record(String idempotenceId);

    /**
     * 记录幂等号
     * @param idempotenceId 幂等号
     * @param time 过期时间
     */
    void record(String idempotenceId, Integer time);

    /**
     * 删除幂等号
     * @param idempotenceId 幂等号
     */
    void delete(String idempotenceId);

}
  • 定义一个幂等处理接口实现类
  • @Component
    public class RedisIdempotence implements Idempotence {
        @Autowired
        private RedisRepository redisRepository;
    
        @Override
        public boolean check(String idempotenceId) {
            return redisRepository.exists(idempotenceId);
        }
    
        @Override
        public void record(String idempotenceId) {
            redisRepository.set(idempotenceId,"1");
        }
    
        @Override
        public void record(String idempotenceId,Integer time) {
            redisRepository.setExpire(idempotenceId,"1",time);
        }
    
        @Override
        public void delete(String idempotenceId) {
            redisRepository.del(idempotenceId);
        }
    }

    这个实现类,咱们就用redis存储这个幂等号 实现4个方法:
    检查是否存在幂等号
    记录幂等号
    记录幂等号(带过期时间)
    删除幂等号

    幂等工具类

    @Component
    public class IdempotenceUtil {
        @Autowired
        private RedisRepository redisRepository;
        /**
         * 生成幂等号
         * @return
         */
        public String generateId() {
            String uuid = UUID.randomUUID().toString();
            String uId=Base64Util.encode(uuid).toLowerCase();
            redisRepository.setExpire(uId,"1",1800);
            return uId;
        }
    
        /**
         * 从Header里面获取幂等号
         * @return
         */
        public String getHeaderIdempotenceId(){
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            String idempotenceId=request.getHeader("idempotenceId");
            return idempotenceId;
        }
    }
这个工具类,提供两个方法。
1.生成一个幂等号,咱们就用uuid
2.从Header里面获取幂等号
  • 定义一个注解
/**
 * 接口增加幂等性
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IdempotenceRequired {

}

 

  • 切面
@Aspect
@Slf4j
@Component
public class IdempotenceSupportAdvice {
    @Autowired
    private Idempotence idempotence;
    @Autowired
    IdempotenceUtil idempotenceUtil;

    /**
     * 拦截有@IdempotenceRequired 注解 的方法。
     */
    @Pointcut("@annotation(xxx.xxx.IdempotenceRequired)")
    public void idempotenceMethod(){}

    @AfterThrowing(value = "idempotenceMethod()()",throwing = "e")
    public void afterThrowing(Throwable e){
        if(!(e instanceof IdempotencyException)) {
            // 从HTTP header中获取幂等号idempotenceId
            String idempotenceId = idempotenceUtil.getHeaderIdempotenceId();
            idempotence.record(idempotenceId, 1800);
        }
    }

    @Around(value = "idempotenceMethod()")
    public Object around(ProceedingJoinPoint  joinPoint) throws Throwable {
        // 从HTTP header中获取幂等号idempotenceId
        String idempotenceId = idempotenceUtil.getHeaderIdempotenceId();
        if(StringUtils.isEmpty(idempotenceId)){
            //不存在幂等号则不进行额外操作
            return joinPoint.proceed();
        }
        // 前置操作 幂等号是否存在
        boolean existed = idempotence.check(idempotenceId);
        if (!existed) {
            throw new IdempotencyException("{success:false,message:"操作重复,请重新输入幂等号重试!",data:-2}");
        }
        //删除幂等号
        idempotence.delete(idempotenceId);
        Object result = joinPoint.proceed();

        return result;
    }
}
  • 定义个controller
  • @RequestMapping("/idempotence")
    public class IdempotenceController {
        /**
         * 生成幂等号
         * @return
         */
        @GetMapping("/generateId")
        public JsonResult generateId(){
            IdempotenceUtil idempotenceUtil=SpringUtil.getBean(IdempotenceUtil.class);
            String uId=idempotenceUtil.generateId();
            return JsonResult.success("成功生成!").setData(uId);
        }
    }

好了,实现的代码,就是这些了,理解起来也是比较简单,没有过多复杂的逻辑。

接下来,就是如何使用的问题了,

幂等的使用

服务端:

不是所有的方法都需要切面拦截 ,只有 IdempotenceRequired 注解的方法才会被拦截。

例如下面接口:

    @IdempotenceRequired
    @PostMapping("/getUsers")
    public JsonResult getUsers(){
  
        //执行正常业务逻辑
        ...
    }

在开发幂等接口时,只需要在方法上简单增加一个 IdempotenceRequired 注解即可。

 

这基本上就是代码非侵入式了呀!!!

客户端:

服务端处理好后,在客户端访问接口的时候需要执行以下步骤:

  • 需要先获取幂等号
  • 然后将幂等号添加到请求头中

  • 1.获取幂等号
  • http://服务地址/idempotence/generateId

幂等性非侵入式实现_第1张图片

  • 2.请求调用

 幂等性非侵入式实现_第2张图片

好了,到这里幂等的实现,就已经完成了!!!

最后:

最近有一些小伙伴粉丝让我帮忙找一些 面试题 资料。为帮助开发者们提升面试技能、有机会入职BATJ等大厂公司,于是我翻遍了收藏的 5T 资料后特别制作了一个专辑一次整体放出。

幂等性非侵入式实现_第3张图片

说明一下:所有的面试题目都不是一成不变的,特别是像一线大厂,下面的面试题只是给大家一个借鉴作用,最主要的是给自己增加知识的储备,有备无患。大致内容包括了: 各类大小厂面经真题、Java 集合、JVM、多线程、并发编程、设计模式、Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、MongoDB、Redis、MySQL、RabbitMQ、Kafka、Linux、Netty、Tomcat、spring面试题、spring cloud面试题、spring boot面试题、spring教程 笔记、spring boot教程笔记、最新阿里巴巴开发手册(63页PDF总结)、2022年Java面试手册一共整理了1184页PDF文档。

如需获取——点赞关注后私信(555)即可

幂等性非侵入式实现_第4张图片 

 

 

你可能感兴趣的:(面试,mysql,java,后端,数据库)