基础篇(一)幂等性
来源:互联网 发布:搜狐自媒体 seo 编辑:程序博客网 时间:2024/06/05 14:08
1、导语
我认为我是个懒惰的人,很少去写点什么东西,哪怕是看书,我也从来没有看完过一本书。我买过不少书籍,但是几乎每本书籍都没有看完三分之一,一个是因为我懒惰,其次是一本书对于我来说有效信息量可能不足20%甚至更低,我需要去筛选一些我感兴趣的或者说对我来说有用的片段,这使得我失去去翻它的兴趣。我几乎所有的能力都学习于互联网,感谢那些乐于分享自己心得的同仁。终于下定决心,自己去写一些什么东西回报一下,力求有效信息量超过60%,写一些对读者有用,读者愿意看下去的东西。
罗里吧嗦了半天,开始进入正题吧,这是我将写下的第一篇博客,幂等性。之所以开篇就写幂等性,是因为我认为幂等性是架构设计中的基础中的基础。,没法保证幂等性,咱们接下来没法谈数据一致性与事物完整性了。
2、什么是幂等性
抄用一段数学上的定义:f(f(x)) = f(x)。x被函数f作用一次和作用无限次的结果是一样的。幂等性应用在软件系统中,我把它简单定义为:某个函数或者某个接口使用相同参数调用一次或者无限次,其造成的后果是一样的,在实际应用中一般针对于接口进行幂等性设计。举个栗子,在系统中,调用方A调用系统B的接口进行用户的扣费操作时,由于网络不稳定,A重试了N次该请求,那么不管B是否接收到多少次请求,都应该保证只会扣除该用户一次费用。
3、幂等性设计
幂等性一般应用于协议设计,TCP协议支持幂等吗?答案是肯定的,在网络不稳定时,操作系统可以肆无忌惮的重发TCP报文片段。TCP协议能够保证幂等的核心在于sequence number字段,一个序列号的在较长的一段时间内均不会出现重复。对于应用层的协议设计,原理和TCP是类似的,我们需要一个不重复的序列号。再简单一点说,在一个业务流程的处理中,我们需要一个不重复的业务流水号,以保证幂等性。
举个实际应用场景:用户A在网页上发起一笔游戏充值请求,浏览器引导用户去银行支付,支付成功后系统给用户进行充值。
协议设计上,我们通过全局唯一的充值订单号贯穿整个业务流程,使该业务支持幂等。
应用实现上,我们列举在银行支付成功后回调系统,进行充值的步骤进行说明。
func pay_notify(orderid,value,state){//有问题的实现 order = db.query("select * from payorder where orderid=$orderid"); check(order,orderid,value,state);//判断支付金额是否与订单金额一致,判断是否是支付成功回调。 if(order.state=='未支付'){ db.update("update payorder set state='已支付' where orderid=$orderid"); charge(order.username,value);//执行充值 }else{ return result("订单已处理")//返回订单已处理,或者返回处理成功 } }
上述实现的问题在于,当回调出现并发时,order.state已经是脏读了,有可能重复充值,该实现并不能100%保证幂等。
列举三种改进方式:
1、悲观锁,select for update,整个执行过程中锁定该订单对应的记录。
2、乐观锁,affectrows = db.update("update payorder set state='已支付' where orderid=$orderid and state='未支付' "),如果affectrows=1,执行充值,否则返回已处理。
3、定义notifylog表,orderid为unique key或者primary key,执行前,先insert,若insert成功则执行充值,否则返回已处理。
以上简单例子用以说明幂等性常用应用实现,在SOA化系统中,可能很多原子功能都被拆分到不同的进程里,如charge充值这个函数,可能在另一个进程中,那么整个业务的链路就会更长,可能回调成功了,但是充值失败。同理,只要充值接口保证幂等性,对于已经回调过但是充值结果未返回的请求,回调接收程序,应当重复发起充值请求。更深入更复杂的应用场景,在数据一致性中再细讲。
4、总结
业务层设计协议时,要求请求方定义不重复的业务流水号。应用实现时,利用数据库乐观锁、插入unique key的日志等方式保证并发时的幂等。
幂等性把关环节,在协议设计评审中,评审重要业务RPC或者http接口是否支持幂等,代码评审中,重点把关请求并发时,是否仍旧能够保证幂等性。设计人员和具体实现人员在实现过程中,也应该时刻自审幂等性的实现是否过关。
2 0
- 基础篇(一)幂等性
- Android常用代码片段收藏---基础篇一(Button、BootBroadcastReceiver 、thread 、Handler 、wakelock、BroadcastReceiver 等)
- ActiveMQ远程监控(一):JMX等基础配置
- MATLAB 学习(一)指令窗等基础准备知识
- swift基础(一):常量和变量、数据类型、数据转换基础等
- Android基础(一):Activity的任务栈(启动模式等)
- Java基础(一)——JVM、JRE、JDK、Javac等
- 2016年末,Android岗位BAT等大厂面试题知识点小结(一)Android基础部分
- HTML学习---网页编程(一)HTML基础概述。列表、文字等
- ABP基础实践训练,一个简易的博客(增删改查)等功能 一:
- mysql基础篇(一)
- HTML5 基础篇(一)
- 多线程基础篇(一)
- JavaScript基础篇(一)
- JavaSE基础篇(一)
- JavaScript 基础篇(一)
- RabbitMQ基础篇(一)
- Linux基础篇(一)
- STAGE1能不能直接引导放在文件系统中的STAGE2.txt
- 怎样解决MySQL数据库主从复制延迟的问题(最后一个解决方案)
- 程序员的英语学习月末总结
- 统计学习方法 李航---第8章 提升方法
- 统计学习方法 李航---第9章 EM算法及其推广
- 基础篇(一)幂等性
- 统计学习方法 李航---第10章 隐马尔可夫模型
- 统计学习方法 李航---第11章 条件随机场
- 安装mplayer详细过程_OK
- 统计学习方法 李航---第12章 统计学习方法总结
- 循环左移字符串
- 用PXE引导调用kickstart安装CentOS 5.txt
- Delphi下的原生ADO使用方法
- JMM——volatile的内存语义