本文正在参加「金石方案 . 分割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”,送你一份程序员生长进阶大礼包,欢迎勾搭。
**