最近开发提现相关的功能,需要考虑幂等性。向社区取了点经,把自己的实现方法写下来。不会高谈阔论什么是幂等,幂等种类等高深的问题。只是简单的写下步骤,题目来源于此。
为什么需要幂等
分布式环境里面,网络不可控,计算机不可控,都有失败风险。先给定两个角色,server,client,client是请求发起者,server是请求接收者并处理业务逻辑。第一次请求任何地方失败了就直接告诉客户处理失败,这没有问题,但是可用性不好,体验差。有什么办法提高?答案是有限次的重试,比如重试三次,第一次失败,还有第二次或者第三次机会。
问题来了,server接受到的请求可能是0个,1个,2个,或者3个。server收到3个重复的提现请求,当然只能最多真的提现一次。幂等解决的就是这个问题,最多处理一次,At Most Once。没有重复发请求的场景,不要考虑幂等问题。另外请求发出,server端只是查询,没有副作用,说明调用安全,也无需考虑幂等。
幂等解决思路
提供请求的身份证号码,或者叫请求的ID,不同的请求不同的ID,重复的请求共享相同的ID。做的第一件事情是要区别请求相同还是不同。区分开了请求的异同,接下来就是实施,实施的思路很简单,根据请求ID查看请求是否处理成功,成功就跳出处理流程,告知已处理,不成功就继续处理。
具体步骤
提供ID生成服务。client可以调用该服务,获取新的ID。
这一步可以跳过,client自己生成符合规范的唯一ID可行。String getTransactionId()
client端调用携带请求ID。
String transactionId = transactionService.getTransactionId(); while(int i< 3){ Response response = invokeServer(transactionId, request); if(response.isSuccess()){ break; } i++; }
server端实现
提现涉及金钱,比较敏感,数据库事务必不可少。所以server端的逻辑依赖数据库事务。
3.1 建ID池,用table表示
CREATE TABLE `transaction` (
`transaction_id` VARCHAR(64) NOT NULL COMMENT '事务ID',
PRIMARY KEY (`transaction_id`)
)
3.2 事务保证
@Transactional
public class BusinessServiceImpl inplements BusinessService{
@Override
public void doSomething(String transactionId, Request request){
// 向transaction表计入transactionId
transactionDao.insert(new Transaction(transactionId));
// 处理具体的业务逻辑
doBussiness(request);
}
}
- transaction表的插入与业务处理,放到同一个事务,要么都成功,要么都失败。
- 相同的transactionId,由于transaction用transactionId作为主键,所以transaction最多有一个插入成功。
所以业务处理也最多只有一次成功,搞定。
其它
谁发起请求,谁负责transactionId。
能够接受成功处理0次,但是不接受处理大于1次。