更新时间:2022-11-11 10:44:53 来源:极悦 浏览881次
简单来说幂等保证了只要调用接口方法成功,外部多次调用对系统的影响是一致的,也就是一个请求多次重试的问题。
客户端存在多次提交或者超时重试的情况;
分布式架构中因网络波动采用重试机制,如Dubbo的重试机制;
消息推送重试,如MQ重试;
不幂等带来的影响:比如在支付场景下,消费者消费扣款消息,对一笔订单进行扣款操作,该扣款操作需要扣除100元,在不幂等的情况下,如果消费者多次发起请求,就会造成多次扣款。
幂等问题演示:
执行如下SQL,初始化金额为100;
create table order_pay
(
id int default 0 not null
primary key,
amt decimal(9, 2) default 0.00 null comment '金额',
status int null comment '状态:0已完成/1处理中'
)comment '订单支付表';
INSERT INTO test.order_pak (id, amt, status) VALUES (1, 100.00, 0);
pay接口:
@RestController
@RequestMapping("/test/order-pay")
public class OrderPayController {
@Autowired
IOrderPayService orderPayService;
@ApiOperation(httpMethod = "POST", value = "幂等性测试")
@PostMapping("pay")
public Resp pay(@RequestBody Req req) {
PayReq payReq = req.getData();
OrderPay order = orderPayService.getOne(new LambdaQueryWrapper().eq(OrderPay::getId, payReq.getBizId()));
order.setAmt(order.getAmt().subtract(payReq.getAmt()));
orderPayService.updateById(order);
return Resp.success("剩余金额更新为"+order.getAmt());
}
}
在不控制幂等的情况下,对pay接口连续发起5次请求,每一次扣减金额为10.00:
可以看到最后剩余金额更新为50 .00,我们发起一次请求,应该只扣除10.00,当遇到网络重复或系统bug在不控制幂等的情况下会导致系统进行了多次扣款。那如何进行幂等控制呢?有什么方法呢?
保证幂等性的措施包括但不限于:
1.表单提交后按钮置灰
限制客户端请求
2.添加唯一索引
把唯一标识作为唯一索引,在重复创建时会抛出唯一约束异常
3.全局唯一ID
针对业务操作和内容生产全局唯一ID,在执行操作时判断ID是否存在来判断是否已执行
4.一锁二查三更新
如果在流程处理过程中,业务要求不能并发执行,可以在流程执行之前根据业务ID获取锁,其他流程执行时获取锁就会失败,也就是同一时间该流程只能有一个能执行成功,执行完成后,释放锁,同时也需要在入口处增加业务状态的判断,以避免对请求的多次处理。这种方式不止可用于幂等的控制,也可以防止并发操作带来的异常。
@ApiOperation(httpMethod = "POST", value = "幂等性测试")
@PostMapping("pay")
public Resp pay(@RequestBody Req req) throws Exception {
PayReq payReq = req.getData();
// 获取分布式锁
redisTools.lock(payReq.getBizId().toString());
OrderPay order = orderPayService.getOne(new LambdaQueryWrapper().eq(OrderPay::getId, payReq.getBizId()));
if (order.getStatus()==1){
order.setAmt(order.getAmt().subtract(payReq.getAmt()));
order.setStatus(0);
orderPayService.updateById(order);
}
// 释防锁
redisTools.unlock(payReq.getBizId().toString());
return Resp.success("剩余金额为"+order.getAmt());
}
以下是对pay接口进行并发请求5次的结果:
可以看到,只有1个请求可以请求成功,另外4个请求在 redisTools.lock(payReq.getBizId().toString())获取分布式锁的步骤中抛出异常。
如果把分布式锁的步骤去掉会发生什么样的情况呢?
@ApiOperation(httpMethod = "POST", value = "幂等性测试")
@PostMapping("pay")
public Resp pay(@RequestBody Req req) throws Exception {
PayReq payReq = req.getData();
OrderPay order = orderPayService.getOne(new LambdaQueryWrapper().eq(OrderPay::getId, payReq.getBizId()));
if (order.getStatus()==1){
order.setAmt(order.getAmt().subtract(payReq.getAmt()));
order.setStatus(0);
orderPayService.updateById(order);
}
return Resp.success("剩余金额为"+order.getAmt());
}
依旧对pay接口进行并发请求5次,查看日志:
5次请求都成功了,但是逻辑执行了5遍,在复杂的业务流程下可能会引发其他不必要问题。
以上就是关于“Java幂等控制的实现”介绍,大家如果对此比较感兴趣,想了解更多相关知识,不妨来关注一下本站的Java视频教程,里面的课程内容从入门到精通,细致全面,通俗易懂,很适合小白学习,希望对大家能够有所帮助哦。
0基础 0学费 15天面授
Java就业班有基础 直达就业
业余时间 高薪转行
Java在职加薪班工作1~3年,加薪神器
工作3~5年,晋升架构
提交申请后,顾问老师会电话与您沟通安排学习