接口幂等性的理解
通俗来讲,除了交易型业务、安全型业务或者非常严谨的计算型业务,一般业务对于幂等的要求并不是很高;但是从安全性的角度来看,幂等就显得愈发重要,另外对于服务器资源也是非常重要的;
接口幂等,运用百度百科的描述:“在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同”。
综上所述,即以相同的请求调用某个接口一次和多次,对系统或是系统资源产生的影响是相同的;
从操作系统或者系统资源的角度来看,在整个流程中,用户–>前端–>服务端–>数据库,用户是不可控因素,那么可以从三方面来控制整个链路的幂等性:
1、前端(防止重复提交)
1、当用户点击提交之后,将按钮置为不可点击,以防止重复提交
2、浏览器的每次前进后退都做刷新动作,以防止重复提交
3、当用户提交之后,跳转或者重定向页面(类似电商提交订单业务,直接跳转到支付页面)
这些操作能有效的阻止在前端进行的重复提交,但是这并不代表我们服务端接口不需要做幂等性设计,例如当接口超时重试或消息重复消费或者请求重发等问题,那么服务端一般都进行了retry设计,如果前一个正在操作资源的请求失败进行了retry请求,那么新的请求会再次操作资源,这就造成了资源的流失,因此在服务端做幂等操作同样重要;
2、服务端幂等设计
对于数据库的增删改查:
天然幂等操作:查询、删除,在资源不变的情况下,对一些和一个资源进行查询和删除,结果都是一样的,因此查询和删除都是天然的幂等操作
增加和更新:非幂等操作
1、对于并发不高的业务系统,可以做select + update/insert 在操作资源之前,先查询比较,判断是否需要对资源进行操作
2、唯一标识/令牌/token:当进入页面时,客户端向服务端请求一个唯一标识,服务端将唯一标识存在redis中,后面客户端提交都带上唯一标识;当请求到达服务端,服务端先去redis判断标识是否存在,存在则代表第一次请求,删除redis标识,操作资源;如果标识不存在,则代表资源已经处理过了,做友好返回;
3、页面跳转,后端也能做页面跳转,原理和前端跳转相同;Post-Redirect-Get(PRG)模式
3、数据库控制
1、唯一索引
对于一些重要的资源,在表中对应字段做唯一索引,这样当数据重复时,会抛出异常,保证不会有脏数据;
2、悲观锁
jvm的悲观锁:lock、synchornized
数据库的悲观锁:
select * from table_name where 唯一字段=#{唯一字段} for update;
这种方案很少使用,特别消耗性能,当并发稍高时,请求就会处理不过来,for update会把整张表锁住
3、乐观锁/状态机/时间戳
在表中添加version或者status字段,此方案适合更新操作,当更新时判断字段的值
boolean updatetable(int id,String newName,int version);
update table_ame set name=#{newName},version=#{version} where id=#{id} and version<${version};
update table_name sale = sale + 1, version = version + 1, WHERE id = #{id} and version = #{version};
update table_name set status = 下一个状态 where id = #{id} and status = #{status};
4、消费防重表
此方案和唯一索引是同样的道理,将消费记录存到另一张表中,以资源的唯一标识作为防重表的唯一索引,在插入重复数据时抛出异常;
4、分布式系统
后续更新…