一、幂等的概念
幂等:多次调用方法或者接口不会改变业务状态,可以保证重复调用的结果和单次调用的结果一致。
需要幂等的场景:
- 用户重复点击(网络波动)
- MQ消息重复
- 应用使用失败或超时重试机制
二、接口幂等的场景
基于RESTful API的角度对部分常见类型请求的幂等性特点进行分析。
| 请求方式 | 说明 |
|---|---|
| GET | 查询操作,天然幂等 |
| POST | 新增操作,请求一次与请求多次造成的结果不同,不是幂等的 |
| PUT | 更新操作,如果是以绝对值更新,则是幂等的。如果是通过增量的方式更新,则不是幂等的 |
| DELETE | 删除操作,根据唯一值删除,是幂等的 |
二、接口幂等的解决方案
2.1 数据库唯一索引
可以保证新增操作时是幂等的。
2.2 token+redis
可以保证新增操作和修改操作时是幂等的,而且性能是比较高的。
这种方案可以解决大多数场景的幂等要求,比如创建商品、提交订单、转账、支付等操作。
如上图所示,是使用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的分布式锁的方法。
这里有两个要点:
- 快速失败(抢不到锁的线程)。
- 控制锁的粒度,粒度越细越好。