幂等是分布式系统中保证数据一致性和安全性的重要保障之一,尤其是在金融、支付领域,其作为资损防控的硬性指标体现在系统架构设计中。今天我们就来浅谈一下幂等相关的设计。
创新互联建站专业为企业提供德清网站建设、德清做网站、德清网站设计、德清网站制作等企业网站建设、网页设计与制作、德清企业网站模板建站服务,十年德清做网站经验,不只是建网站,更提供有价值的思路和整体网络服务。
幂等( idempotent、idempotence )的概念来源于数学,并被广泛应用于计算机科学。在数学中,其语意是 f ( x ) = f ( f ( x )),比如求取绝对值,abs ( x ) = abs ( abs ( x )),就是幂等的。
在计算机科学中,幂等即相同的请求调用一次和调用多次,服务端处理的的结果相同,并且最多受理一次。
我们就拿支付公司的资金调拨举个例子。一般的,第三方支付公司需要借助清算公司(如网联)提供的支付通道进行备付金账户资金调拨,以保证资金池充足可用。当第三方支付公司发起资金调拨请求时,如果清算公司的返回结果丢失,这时,支付公司是否可以重试?如果重试,是否会发生资金的重复调拨?
互联网公司的应用间存在物理边界,请求和响应信息会通过网络进行传递。我们说远程调用的结果会有三个状态:成功,失败,未知。前两者都是明确的状态,而未知具有不确定性,一般都是由网络超时、丢包引起的。如上例中,如果出现了超时,其实有两种方案,我们可以建立查询补偿机制,来研判是否要重新发起资金调拨。或者,清算公司做好幂等控制,支付公司可以无脑重试,既可以保证资金调拨业务的正常,又能保证不会发生多次调拨。
在架构设计中,幂等的应用面非常广泛,比如 MQ 规避重复消费、表单规避重复提交等。
幂等包含两大要素,幂等标记和关键请求参数。
幂等号:它对应服务端的唯一约束,在设计上,它一般由上游的幂等单号和来源组成。服务端的接口文档中,需要明确指出幂等号的信息组成,它的作用是对请求信息进行身份标识,相同幂等号的请求将被服务端识别为同一请求。
关键请求信息:接收的核心业务信息,常见的如收款账户、打款账户,打款金额、币种、商品数量等等。相同的请求中,调用方需要保证关键请求信息不变,一旦信息发生变动,则需要替换幂等号。
调用方需要保证幂等号不重复,且对同一业务单据的同一次操作,无论请求多少次,都要保证幂等号不变。
幂等号重复,原因基本如下
幂等号变化,原因基本如下
当服务端没有返回结果时,调用方关键业务请求参数不允许变更。
初次请求,由于网络异常导致 timeout 调用方没有拿到结果,而服务端受理成功。客户端修改单据金额,请求信息发生变化,调用方与服务端处理出错。
img
幂等号不持久化,对于异步回执处理,上下游数据稽核带来困难,所以幂等号持久化是一个基本要求。
RPC 调用,调用方的幂等号,是内存中根据业务映射拼接得来,不做持久化。
//内存中拼接幂等号
request.setRequestId(BizTypeEnum.getPrefix(×xxDO.getBizType()) + xxxDO.getId()):
transactionTemplate.execute (status ->
//生成流水号 xxx
SerialDO serialDO = buildSerialDO();
//播入 aaa 表
serialDAO.insert(serialDO);
someDAO.update (someDO) ;
// dubbo 调用 rpc,流水号 xxxId 作为幂等号
invokeRpc(request);
return true,
));
transactionTemplate.execute (status ->
//生成流水号 xxx
SerialDO serialDO = buildSerialDO();
//播入 aaa 表
serialDAO.insert(serialDO);
someDAO.update (someDO) ;
return true;
));
// dubbo 调用 rpc,流水号 xxxId 作为幂等号
invokeRpc(request);
/**
* 外层已开启事务
*/
public static void execute (){
//更新单据状态
Runnable runnable = () -> {
response = dubboService.call(request);
};
register(runnable);
}
public static void register (Runnable runnable) {
if (TransactionSynchronizationManager.isActualTrangactionActive()) {
TransactionSynchronizationManager.registersynchronization(
new TransactionSynchronizationAdapter() {
@Override
public void afterCommit () {
runnable.run();
}
}
);
} else {
LOGGER.debug( "No active transaction.");
runnable.run();
}
}
分布式下并发场景,并不能单纯的依赖查询做到插入 幂等。常见唯一性保障方式:
RPC 调用超时,本地事务回滚。下次重试,会生成新的幂等号,导致资损。
针对相同请求,不论调用方请求多少次,服务端仅受理一次,且受理结果相同。
售中退款的场景中,第一次服务端正常受理调用方请求,但调用方因为超时丢弃响应;当第二次调用方重试,服务端发现退款金额不足,返回受理失败,导致故障。
//1、基本校验
//2、悲观锁内,可退款金额判断;
Assert.isTrue(refundable(xxx), "cannot refund");
//3、逻辑处理
try {
process(xxx);
} catch (Exception e) {
//幂等判断处理
}
客户端收到服务端结果后,本着不信任的原则,针对关键业务请求参数如账户、 金额同服务端受理内容对比。
服务端做幂等判断时,只看幂等号,虽然第二次请求幂等号不变,但是金额又可能被篡改,如果服务端直接返回成功,将导致资金损失。
以上规则是借鉴历史项目和互联网经验总结而成,主要侧重于幂等设计的原则,幂等的落地方案有很多,比如幂等表、乐观锁、悲观锁等,这里就不赘述。
文章名称:浅谈幂等,大家都看明白了吗?
文章链接:http://www.csdahua.cn/qtweb/news38/80288.html
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网