本文正在参加「金石方案 . 分割6万现金大奖」
hello,咱们好,我是张张,「架构精进之路」公号作者。
引子
在日常工作中的一些技能设计方案评定会上,经常会有人提到留意服务接口的幂等性问题,最近就有个组内同学就跑到跟前问我,幂等性到底是个啥?
在目前分布式/微服务化的今日,供给的服务能力丰富多样,根据 HTTP 协议的 Web API 是时下最为流行的一种分布式服务供给方法,关于服务的幂等性保障尤为重要。
我想了想,觉得有必要好好给他遍及一下才行,否则以后干事还是一头雾水
今日方案就关于服务幂等性的一系列问题,在此将资料总结收拾,顺便共享给咱们~
1、何为幂等性?
幂等(idempotence),来源于数学中的一个概念,例如:幂等函数/幂等方法(指用相同的参数重复履行,并能取得相同成果的函数,这些函数不影响体系状态,也不必忧虑重复履行会对体系形成改动)。
简略了解即:屡次调用对体系的产生的影响是相同的,即对资源的作用是相同的。
幂等性
幂等性强调的是外界经过接口对体系内部的影响, 只需一次或屡次调用对某一个资源应该具有同样的副作用就行。
留意:这里指对资源形成的副作用必须是相同的,可是回来值允许不同!
2、幂等性首要场景有哪些?
依据上面临幂等性的定义咱们得知:产生重复数据或数据不共同,这个绝大部分是因为产生了重复恳求。
这里的重复恳求是指同一个恳求在一些状况下被屡次建议。
导致这个状况会有哪些场景呢?
-
微服务架构下,不同微服务间会有大量的根据 http,rpc 或许 mq 音讯的网络通信,会有第三个状况【不知道】,也便是超时。假如超时了,微服务框架会进行重试。
-
用户交互的时候屡次点击,无意地触发多笔买卖。
-
MQ 音讯中间件,音讯重复消费
-
第三方平台的接口(如:付出成功回调接口),因为异常也会导致屡次异步回调
-
其他中间件/运用服务依据本身的特性,也有可能进行重试。
3、幂等性的作用是什么?
幂等性首要确保屡次调用对资源的影响是共同的。
在阐述作用之前,咱们运用资源处理运用来阐明一下:
HTTP 与数据库的 CRUD 操作对应:
- PUT :CREATE
- GET :READ
- POST :UPDATE
- DELETE :DELETE
(其实不光是数据库,任何数据如文件图表都是这样)
1)查询
SELECT * FROM users WHERE xxx;
不会对数据产生任何变化,天然具有幂等性。
2)新增
INSERT INTO users (user_id, name) VALUES (1, 'zhangsan');
case1:带有仅有索引(如:`user_id`),重复插入会导致后续履行失利,具有幂等性;
case2:不带有仅有索引,屡次插入会导致数据重复,不具有幂等性。
3)修正
case1:直接赋值,不论履行多少次 score 都相同,具有幂等性。
UPDATE users SET score = 30 WHERE user_id = 1;
case2:计算赋值,每次操作 score 数据都不相同,不具有幂等性。
UPDATE users SET score = score + 30 WHERE user_id = 1;
4)删去
case1:绝对值删去,重复屡次成果相同,具有幂等性。
DELETE FROM users WHERE id = 1;
case2:相对值删去,重复屡次成果不共同,不具有幂等性。
DELETE top(3) FROM users;
总结:通常只需求对写恳求(新增 &更新)作幂等性确保。
4、如何处理幂等性问题?
咱们在网上查找幂等性问题的处理方案,会有各种各样的解法,可是如何判别哪种处理方案关于自己的事务场景是最优解,这种状况下,就需求咱们抓问题实质。
经过以上剖析,咱们得到了处理幂等性问题便是要操控对资源的写操作。
咱们从问题各个环节流程来剖析处理:
幂等性问题剖析
4.1 操控重复恳求
操控动作触发源头,即前端做幂等性操控完成
相对不太牢靠,没有从根本上处理问题,仅算作辅助处理方案。
首要处理方案:
-
操控操作次数,例如:提交按钮仅可操作一次(提交动作后按钮置灰)
-
及时重定向,例如:下单/付出成功后跳转到成功提示页面,这样消除了浏览器前进或撤退形成的重复提交问题。
4.2 过滤重复动作
操控过滤重复动作,是指在动作流经过程中操控有效恳求数量。
1)分布式锁
运用 Redis 记载当时处理的事务标识,当检测到没有此使命在处理中,就进入处理,否则判为重复恳求,可做过滤处理。
订单建议付出恳求,付出体系会去 Redis 缓存中查询是否存在该订单号的 Key,假如不存在,则向 Redis 增加 Key 为订单号。查询订单付出已经付出,假如没有则进行付出,付出完成后删去该订单号的 Key。经过 Redis 做到了分布式锁,只有这次订单订单付出恳求完成,下次恳求才能进来。
分布式锁比较去重表,将放并发做到了缓存中,较为高效。思路相同,同一时间只能完成一次付出恳求。
2)token 令牌
运用流程如下:
1)服务端供给了发送 token 的接口。履行事务前先去获取 token,同时服务端会把 token 保存到 redis 中;
2)然后事务端建议事务恳求时,把 token 一同携带过去,一般放在恳求头部;
3)服务器判别 token 是否存在 redis 中,存在即第一次恳求,可持续履行事务,履行事务完成后将 token 从 redis 中删去;
4)假如判别 token 不存在 redis 中,就表示是重复操作,直接回来重复符号给 client,这样就确保了事务代码不被重复履行。
3)缓冲队列
把所有恳求都快速地接下来,对接入缓冲管道。后续运用异步使命处理管道中的数据,过滤掉重复的恳求数据。
优点:同步转异步,完成高吞吐。
缺点:不能及时回来处理成果,需求后续监听处理成果的异步回来数据。
4.3 处理重复写
完成幂等性常见的方法有:失望锁(for update)、乐观锁、仅有束缚。
1)失望锁(Pessimistic Lock)
简略了解便是:假定每一次拿数据,都有以为会被修正,所以给数据库的行或表上锁。
当数据库履行 select for update 时会获取被 select 中的数据行的行锁,因而其他并发履行的 select for update 假如试图选中同一行则会产生排挤(需求等待行锁被释放),因而到达锁的作用。
select for update 获取的行锁会在当时事务结束时自动释放,因而必须在事务中运用。(留意 for update 要用在索引上,否则会锁表)
START TRANSACTION;
# 开启事务
SELETE * FROM users WHERE id=1 FOR UPDATE;
UPDATE users SET name= 'xiaoming' WHERE id = 1;
COMMIT;
# 提交事务
2)乐观锁(Optimistic Lock)
简略了解便是:便是很乐观,每次去拿数据的时候都以为他人不会修正。更新时假如 version 变化了,更新不会成功。
不过,乐观锁存在失效的状况,便是常说的 ABA 问题,不过假如 version 版本一直是自增的就不会出现 ABA 的状况。
UPDATE users
SET name='xiaoxiao', version=(version+1)
WHERE id=1 AND version=version;
缺点:便是在操作事务前,需求先查询出当时的 version 版本
别的,还存在一种:状态机操控
例如:付出状态流通流程:待付出->付出中->已付出
具有一定要的前置要求的,严格来讲,也归于乐观锁的一种。
3)仅有束缚
常见的便是运用数据库仅有索引或许大局事务仅有标识(如:source+序列号等)。
这个机制是运用了数据库的主键仅有束缚的特性,处理了在 insert 场景时幂等问题。但主键的要求不是自增的主键,这样就需求事务生成大局仅有的主键。
大局 ID 生成方案:
-
UUID:结合机器的网卡、当地时间、一个随记数来生成 UUID;
-
数据库自增 ID:运用数据库的 id 自增战略,如 MySQL 的 auto_increment。
-
Redis 完成:经过供给像 INCR 和 INCRBY 这样的自增原子指令,确保生成的 ID 肯定是仅有有序的。
-
雪花算法-Snowflake:由 Twitter 开源的分布式 ID 生成算法,以区分命名空间的方法将 64-bit 位分割成多个部分,每个部分代表不同的含义。
小结:按照运用上的最优收益,引荐排序为:乐观锁 > 仅有束缚 > 失望锁。
5、总结
通常状况下,非幂等问题,首要是因为重复且不确认的写操作形成的。
1、处理重复的首要思考点
从恳求全流程,操控重复恳求触发以及重复数据处理
-
客户端 操控建议重复恳求
-
服务端 过滤重复无效恳求
-
底层数据处理 防止重复写操作
2、操控不确认性首要思考点
从服务设计思路上做改动,尽量防止不确认性:
-
计算变量改为数据记载方法
-
规模操作改为确认操作
后记
听了我以上大段的叙述后,他如同收获感满满的似的说:大概了解了…
可是出于本身责任感,我还得叮嘱他几句:
1)幂等性处理 尽管复杂了事务处理,也可能会降低接口的履行功率,可是为了确保体系数据的准确性,对错常有必要的;
2)遇到问题,长于发现并挖掘实质问题,这样处理起来才能高效且精准;
3)选择本身事务场景合适的处理方案,而不要去硬套一些现成的技能完成,无论是组合还是立异,要记住合适的才是最好的。
愿咱们能够把握问题剖析以及处理的能力,都不要一上来就急于处理问题,能够多做些深入剖析,了解实质问题之后再考虑处理办法进行处理。
END
期望今日的解说对咱们有所帮助,谢谢!
Thanks for reading!
作者:架构精进之路,十年研发风雨路,大厂架构师,CSDN 博客专家,专心架构技能沉积学习及共享,工作与认知升级,坚持共享接地气儿的干货文章,等待与你一同生长。
**关注并私信我回复“01”,送你一份程序员生长进阶大礼包,欢迎勾搭。
**