本文转载自 https://blog.csdn.net/liangcheng0523/article/details/107187894
温馨提示:本文无配图,纯文字。
一、引言
之前没有接触过分布式,redis是什么,消息队列(Message Queue)里的Kafka是什么,一改不知,听这些东西感觉好高级,菜鸡的我在屏幕前瑟瑟发抖。看别人的面试视频问“有没有用过kafka?”“kafka里面怎么保证幂等性?”,卧槽,听到这里带来了满满的焦虑,什么又是幂等性?还要用数学里面的次幂来实现什么超复杂算法吗?想到这里又不由得产生了畏难心理。这不,最近心态比较好,上网搜了个幂等性的相关概念。就有了下面的内容
二、先摆结论
幂等性,就是套个个高大上名字的普通概念,根本就不是新概念,甚至你开发时还经常会实现所谓的幂等性。
1、再详细点
幂等性的通俗概念:
调用方,对一个系统进行重复调用(参数全部相同),不论重复调用多少次,这些调用对系统的影响都是相同的效果。就是不论调用我多少次你对我的影响以及你的影响都是不变的,不会随着次数的变化而改变。
举个简单的例子:
天然幂等性:
假设对象Person中有个name属性,有个
setName(String name){
this.name = name
}
- 1
- 2
- 3
的方法,那这个方法就是天然幂等的哦,你输入相同的“小明”参数,不论你重复调用多少次都是将名字设置为“小明”,其对对象Person的影响都是一样的。这就是天然幂等性。
非幂等性:
还是拿对象Person举例子,假设对象中有个age属性,有个
increaseAge(){
this.age++;
}
- 1
- 2
- 3
方法,我们按正常的步骤一次一次调用是不会有问题的,如果调用者没有控制好逻辑,一次流程重复调用好几次,这时候影响效果和一次是有非常大区别,代码编写者以为它只会调用一次,结果出现了意外调用了很多次,恰好方法不具有幂等性,于是就会出现和预期不一样的效果。这个方法本身是不具备幂等性的,我们可以修改这个方法,让其传入一个标识符,每一次重复的请求会有相同的标识符,方法内部可以根据标识符查数据库是不是已经处理过,如果处理过就不重复处理。这样方法就具备了幂等性。
再举个实际点的例子:
客户在进行一笔转账交易,后端划分了两个系统来处理这个转账的流程:
①系统A负责收集转账人和接收人还有金额的信息然后传给系统B进行转账,将控制逻辑留在系统A。
②系统B读取系统A传过来的信息,负责更改数据库的金额。如果操作成功,就回复系统A成功,如果失败就回复系统A失败。
③系统A可以接受系统B操作成功或失败的回复,但是我们知道,系统A这个交易流程是有等待时间的,如果等待超时,它不确认是否是转账成功或失败,于是系统A会重试调用直到得到一个明确的回复。
这是系统大致的交易流程。这个流程是有问题的,系统B提供的操作接口不是幂等性的。
逻辑漏洞:
假设系统B操作成功了,但是由于系统B回复的时候遇到网络抖动、网络阻塞、网络风暴等,这个成功的消息没有及时被系统A接收,系统A再重试让系统B转账,结果会导致:用户点击一次转账,后台为他转了很多笔。这是非常危险的。
解决办法:
我们可以为每一笔用户发起的转账请求赋予一个全局唯一的ID,把这个ID在调用系统B的时候一起传输进去,第一次系统B处理的时候正常处理,处理完之后把转账成功还是失败的状态与ID一起存入数据库,下次重复请求会查找数据库,查出来数据为空则代表第一次请求,查出来状态为成功,则直接返回成功,查出来状态为失败,则再操作一次。这样能确保不会出现上述逻辑错误
三、总结:
很多方法具有天然的幂等性,有的方法不具备,我们需要业务逻辑层来实现幂等性。我们开发的过程中考虑全面了就能自然的实现幂等性,幂等性根本就不是什么新的知识。