接口的幂等性的多重考虑,你会了吗?
目录
[TOC]
前言
今天的主题:接口幂等性的解决方案。本来是想把对象的存储过程和内存布局肝出来的,但是临时产生了变化,哈哈,这部分内容我们留在下一期吧,有句话说的好,好事多磨,对吧。</br>
在实际项目开发中接口是我们在开发中经常接触到的,而且是经常经常要写,每一个项目可能都会伴随着大量的接口开发,在moon来涂鸦的这几个月,基本上就是在与接口作斗争了,新需求除了业务相关就是设计表和接口编写了。</br>
当然,在接口设计中我们要考虑很多问题,安全性,格式,设计等等,今天我们先来聊聊,在高并发环境下,接口幂等性的解决方案有哪些。
正文
1 接口幂等性
就是说在多次相同的操作下保证最终的结果是一致的。
其实这个概念还是比较简单的,很容易理解,那我们思考一个问题,如果不保证接口幂等性会有什么问题?
1.1 案例
我们简单的举个例子,现在有一个接口,提供了转账的功能,a要给b转账1000元,正常情况下我们接口一次性就调用成功了,但是却因为网络抖动等其它原因没有成功,于是就开始不停的重试,突然网络好了,但是这时却连续发出去了三个请求,但是这个接口没有保证幂等性,于是从结果上来看就是a给b转了3000元,这显然是程序业务逻辑上不能接受的(其实moon可以当b的)。
2 解决方案
2.1 token机制
token机制其实是比较简单的,我们先来简单的说一下流程。</br>
- 首先客户端先请求服务端,服务端生成token,每次请求生成的都是一个新的token(这个token一定要设置超时时间),将token存入redis当中,然后将token返回给客户端。
- 客户端携带刚刚返回的token请求服务端做业务请求。
- 服务端收到请求,做判断。
- 如果token在redis中,则直接删除该token,然后继续做业务请求。
- 如果token不在redis中,代表已经执行过当前业务了,则不执行业务。
图示如下:
token机制实现方式还是比较简单的,但是其实对于我们某些响应速度要求很高的业务不太友好,缺点就是需要多一次请求获取token的过程。
正常来说是每次请都会生成一个新的token,如果有极限情况下,有两个请求都带着相同的token进来,会存在都走入判断是否存在的过程,可能都会同时查到存在,这样也会有问题,针对这种情况,我们可以在删除前判断下是否存在,存在就删除,为了保证原子性,这部分逻辑建议使用lua脚本完成。
2.2 去重表
去重表的机制是根据mysql唯一索引的特性来的,我们先来说下它的流程:
- 首先客户端先请求服务端,服务端先将这次的请求信息存入一张mysql的去重表中,这张表要根据这次请求的其中某个特殊字段建立唯一索引,或者主键索引。
- 判断是否插入成功
- 如果插入成功,则继续做后续业务请求。
- 如果插入失败,则代表已经执行过当前请求。
图示如下:
去重表机制的问题有两点:
- 1.mysql容错性,也就是mysql本身如果不是高可用的那么业务可能会受到影响:
- 2.既然是唯一索引,自然在写表的时候就没有办法用到changbuffer,每次都要从磁盘查出来判断再写入,对于一个高并发的接口来说,这些都是需要考虑的因素。
2.3 redis 的 SETNX键值
过程如下:
- 首先客户端先请求服务端,服务端将能代表这次请求业务的唯一字段以 SETNX 的方式存入redis,并设置超时时间,超时时间可以根据业务权衡。
- 判断是否插入成功
- 如果插入成功,则继续做后续业务请求。
- 如果插入失败,则代表已经执行过当前请求。
这里我们是利用了redis setnx 的特性来完成的。</br>
setnx:只在键key不存在的情况下,将键key的值设置为value。若键key已经存在,则SETNX命令不做任何动作。命令在设置成功时返回1,设置失败时返回0。
图示如下:
这种方案可以说是针对上一个方案改进的,效率也会提高很多。
2.4 状态机幂
这种机制适用于有不同状态的业务,moon的上一家公司就是这样做的。
我们的订单系统,一条订单会有多个状态,如:待付款,锁定,已付款等状态,而这些状态都是有流程和逻辑的,我们可以根据这个状态判断是否执行后续业务操作。
2.5 乐观锁(更新操作)
就是数据库中增加版本号字段,每次更新根据版本号来判断
过程如下:
- 首先客户端先请求服务端,先查询出当前的version版本。
- select version from .. where ..
- 根据version版本来做sql操作
- UPDATE .. SET ... version=(version+1) WHERE .. AND version=version;
这个图示我就不再画了,还是比较简单的
2.6 悲观锁(更新操作)
假设每一次拿数据,都有认为会被修改,所以给数据库的行上锁,也是基于数据库特性来完成。
当数据库执行select for update时会获取被select中的数据行的行锁,因此其他并发执行的select for update如果试图选中同一行则会发生排斥(需要等待行锁被释放),因此达到锁的效果。
START TRANSACTION; # 开启事务 SELETE * FROM TABLE WHERE .. FOR UPDATE; UPDATE TABLE SET ... WHERE ..; COMMIT; # 提交事务
结语
关于接口幂等性这部分内容,解决方案其实大同小异,很多方式的原理都是一样的,更多的其实都是在业务链路中去过滤,也会有很多是有消息中间件去解决的,默认在中间件这一层就直接过滤掉了,当然每种方式都有各自的优点和缺点,需要结合当前的业务去选择,今天的文章内容,你get到了吗?
我是moon 文章首发于我的微信公众号:moon聊技术,欢迎大家关注 ! 关注后回复666 有一线大厂面试题赠送,助你成为offer收割机!下一篇,真的要讲jvm了~ 下次见~

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
我的服务器接连被黑客攻击,我好难
最近在几台测试服务器上跑一些业务数据,但是过了几天服务器突然变的奇慢无比,敲个命令就像卡壳一样,有时候甚至都连接不上,最开始我以为是网络问题,就强行kill掉进程,重新跑一下进程,最后实在受不了,就上阿里云后台说重启下服务器吧,结果看到CPU的占用率已经到达了100%。 这是CPU是恢复正常之后截图 看到这样我以为是因为我跑了大量的数据导致CPU飙升的,然后我就kill到了进程,并且重启了服务器,启动之后CPU正常,我以为就是我跑数据导致的,此后我就没用这台服务器跑数据了,我就单纯的以为这就算处理好了,没想到等我过几天部署测试包的时候发现,又是奇慢无比,看了下CPU占用率又是99.9%,事实证明我还是太年轻了。 终于忍无可忍,就深究下吧,先用linux命令(top)查看下,到底是什么占用了这么多CPU资源,结果如下图: 看到的瞬间第一感觉就是,这是啥玩意,这是谁部署的。问了下平时身旁的背锅侠,好像也不是他弄的,看来这次这锅是甩不了了,那就只能... 「What?中病毒了?」 根据过往的经验,这玩意不应该是点了网页上的小姐姐才会发生的事情吗?我这为什么也就中毒了。 这东西是啥 既然已经中...
- 下一篇
SpringMVC执行流程还不清楚?
MVC总结 1. 概述 还是之前的三个套路 1.1 是什么? Spring提供一套视图层的处理框架,他基于Servlet实现,可以通过XML或者注解进行我们需要的配置。 他提供了拦截器,文件上传,CORS等服务。 1.2 为什么用? 原生Servlet在大型项目中需要进过多重封装,来避免代码冗余,其次由于不同接口需要的参数不同,我们需要自己在Servlet层 封装我们需要的参数,这对于开发者来说是一种重复且枯燥的工作,于是出现了视图层框架,为我们进行参数封装等功能。让开发者的注意力全部放在逻辑架构中,不需要考虑参数封装等问题。 1.3 怎么用 再聊怎么用之前,我们需要了解一下MVC的工作原理。 他基于一个DispatcherServlet类实现对各种请求的转发,即前端的所有请求都会来到这个Servlet中,然后这个类进行参数封装和请求转发,执行具体的逻辑。(第二章我们细聊) 1.3.1 XML 根据上面的原理,我们需要一个DispatcherServlet来为我们提供基础的Servlet服务,我们可以通过servlet规范的web.xml文件,对该类进行初始化。并且声明该类处理所有的请...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS7设置SWAP分区,小内存服务器的救世主
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS7安装Docker,走上虚拟化容器引擎之路
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- CentOS关闭SELinux安全模块
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Docker安装Oracle12C,快速搭建Oracle学习环境