一、什么是幂等?
在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同即对接口的多次操作所造成的影响和一次操作造成的影响是一样的
二、什么场景下会涉及到幂等?
1.新增数据
代码如下(示例):
@Data
public class Student {
private int id;
private String name;
private Integer age;
private String sex;
}
@Autowired
private StudentMapper studentMapper;
public void testAdd() {
for (int i = 0; i < 10; i++) {
Student student = new Student();
student.setId(1);
student.setName("小花");
student.setAge(18);
student.setSex("女");
studentMapper.insert(student);
}
}
如上图所示,我们假设一次进行10次請求,按照正常的流程来说,对相同的数据进行新增,再数据库中只存在一条唯一的数据才是合理的,但是上图的操作,再相同时间内,数据库存在10条相同的数据,是存在问题的,这个接口就不是 幂等 接口
2.删除数据
我们假设一个场景,再电商系统中,我们进行购买商品时,下单之后,我们商品库存是需要减少的,但是由于网络问题,没看看到减库存,我们有尝试了一次,最后可能会导致我们的库存数量异常,这时候我们需要对这个操作进行幂等设计
代码如下(示例):
@Data
public class Inventory {
private int id;
private int num;
}
@Autowired
private InventoryMapper inventoryMapper;
public void test() {
for (int i = 0; i < 2; i++) {
subtract();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void subtract() {
Inventory inventory = inventoryMapper.selectById(1);
inventory.setNum(inventory.getNum() - 1);
inventoryMapper.updateById(inventory);
}
通过上面的代码我们可以看到,本来是需要对ID=1的库存进行减少的,倒是由于网络问题,延迟了,最后可能倒是库存减的数量不对,这类的接口 也是需要进行 幂等 设计的
3.修改数据
假设一个场景,我们需要给一个用户进行转账,第一个我们先进行转了50万,这个时候还没通知我们成功了,我们有进行操作了一次,导致我们的数据少了100万,这个问题就很大了
@Data
public class Transfer {
private int id;
private BigDecimal money;
}
@Autowired
private TransferMapper transferMapper;
public void testTransfer() {
try {
//首次发起
transfer();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
//再次发起
transfer();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void transfer() throws InterruptedException {
Transfer transfer = transferMapper.selectById(1);
BigDecimal decimal = transfer.getMoney().subtract(new BigDecimal("50"));
transfer.setMoney(decimal);
transferMapper.updateById(transfer);
Thread.sleep(1000);
}
我们通过上面的代码可以看到,再进行减金额的时候结果返回延迟了,前端再次发起请求,导致金额不对
三、解决办法
通过上面的例子,我们可以看到再一些重要的场景下接口的幂等很重要,那么怎样解决上面的问题呢?
A.后端处理
1.令牌
所谓的令牌就是我们常说的token,在前端进行请求我们的一些需要进行幂等的接口时,可以先给前端一个token标识,前端拿着这个标识来进行请求我们的接口,接口请求完成之后立刻删除
流程图如下:
通过流程图我们可以看到,请求必须携带我们给的令牌才可以成功,否则就会返回异常信息
2.唯一索引
唯一索引是实现最简单的方式,比如我们新增用户信息时,将用户的身份证号当作唯一的索引,这就会导致在数据库中,只可以存在唯一的身份证号
3.第三方组件
第三方组件,比如使用redis,我们将操作的唯一标识存入redis,下次请求的时候从redis中进行获取,从而进行请求操作
4.乐观锁
乐观锁是通过数据方面进行操作,最常用就是版本号
select version from demo where id = 1 version = 1 这时我们查询出来的版本号是1 update demo set version = version +1 where id =1 and version = 1 version = 2 我们将版本号更新为2,这是其他的操作再来的时候,使用version=1进行比对,就无法成功
乐观锁主要用于数据的修改而且效率很高是基于数据库层面的
B.前端处理
1.重定向
即用户点击了按钮之后页面重定向到别的页面,防止误操作
2.按钮置灰
用户再点击按钮之后,按钮置灰,用户无法再次选择点击
总结
接口的幂等性是接口设计的重要环节,有些接口天生就是幂等的比如查询操作,对于不同的业务场景我们应该选择合适的幂等设计。 1、插入操作:使用数据库的唯一索引 2、修改操作:使用乐观锁或者全局令牌都可以,推荐乐观锁 3、删除操作:使用乐观锁或者全局令牌都可以,推荐全局令牌
推荐
最后推荐一个朋友的公众号,可以更好的方便大家沟通和学习