分布式服务的接口幂等性

101 阅读2分钟

一、幂等的概念

幂等:多次调用方法或者接口不会改变业务状态,可以保证重复调用的结果和单次调用的结果一致。

需要幂等的场景:

  • 用户重复点击(网络波动)
  • MQ消息重复
  • 应用使用失败或超时重试机制

二、接口幂等的场景

基于RESTful API的角度对部分常见类型请求的幂等性特点进行分析。

请求方式说明
GET查询操作,天然幂等
POST新增操作,请求一次与请求多次造成的结果不同,不是幂等的
PUT更新操作,如果是以绝对值更新,则是幂等的。如果是通过增量的方式更新,则不是幂等的
DELETE删除操作,根据唯一值删除,是幂等的

二、接口幂等的解决方案

2.1 数据库唯一索引

可以保证新增操作时是幂等的。

2.2 token+redis

可以保证新增操作和修改操作时是幂等的,而且性能是比较高的。

这种方案可以解决大多数场景的幂等要求,比如创建商品、提交订单、转账、支付等操作。

1692447043216.jpg

如上图所示,是使用token+redis保证接口幂等的过程。

  • 以提交订单操作为例:
    • 在第一次请求(创建订单)时,服务端生成一个token保存到redis中并返回给客户端。
    • 在第二次请求(提交订单)时,客户端将token传给服务端。验证token如果存在,则将token删除,并继续提交订单流程;如果token不存在,则认为是重复请求,终止请求并返回,不处理业务。

2.3 分布式锁

可以保证新增操作和修改操作时是幂等的,但是性能是比较低的。

public void saveOrder(Item item) {
    //获取锁(重入锁),执行锁的名称
    RLock lock = redissonClient.getLock("lockName");
    //尝试获取锁,参数分别是:获取锁的最大等待时间(期间会重试),锁自动释放时间,时间单位
    boolean isLock = lock.tryLock(10, TimeUnit.SECONDS);
    try {
        //判断是否获取锁成功
        if (!isLock) {
            log.info("下单操作获取锁失败");
            throw new BusinessException("新增或修改失败");
        }
        //下单操作

    }finally {
        //释放锁
        lock.unlock();
    }
}

上面代码片段展示了使用redisson的分布式锁的方法。

这里有两个要点

  • 快速失败(抢不到锁的线程)。
  • 控制锁的粒度,粒度越细越好。