03 幂等性设计

一、概述

  • 一次和多次请求某一个资源应该具有相同的副作用。

1. 服务间调用状态

  • 把系统解耦后,服务间的调用三个状态

    1. 成功 success
    2. 失败 failed
    3. 超时 timeout, 超时完全不知道是什么状态
      • 假如超时 是网络传输丢包的问题
        • 可能是请求时就没有请求到
        • 可能是请求到了,返回结果时没有正常返回等等情况。
  • 因为系统超时,而调用方重试一下,会给我们的系统带来不一致的副作用。

    • 两种处理方式
      1. 一种是需要下游系统提供相应的查询接口。
        • 上游系统在timeout 后去查询一下。
          • 查到了,表明做了,成功了就不用做了, 失败了就走失败流程
      2. 一种是通过幂等性的方式。
        • 这个查询操作交给下游系统,上游系统只管重试,下游系统保证一次和多次的请求结果是一样的。

二、幂等性的方式

1. 全局ID

  • 要做到幂等性的交易接口,需要一个唯一的标识,来标志交易是同一笔交易。
  • 全局id 参考 全局id 的文章。

2. 处理流程

1. 流程

  1. 是需要一个存储来记录收到的交易。
  2. 当收到交易请求的时候,我们回到这个存储中去查询
    • 如果查到了就不再做操作。并把上次做的结果返回。
    • 如果没有查到,那么我们就记录下来。
  • image.png

2 问题

  • 上面流程有个问题,28定律,80%的请求都是正常的,让100%的请求都到这个存储里去查一下,这会导致处理流程变的很慢。

2.1 解决

  • 最好是当这个存储出现冲突的时候会报错,
    • 我们收到交易请求后,直接去存储里记录这个 ID(相对于数据的 Insert 操作),如果出现 ID 冲突了的异常,那么我们就知道这个之前已经有人发过来了,所以就不用再做了。
  • 假如是mysql数据库中你可以使用 insert into … values … on DUPLICATE KEY UPDATE … 这样的操作
CREATE TABLE unique_table
(
    id    bigint DEFAULT 0 NOT NULL
        CONSTRAINT `PRIMARY`
        PRIMARY KEY,
    type  varchar(50)      NULL,
    state int    DEFAULT 0 NOT NULL
);

insert into unique_table values (2,'订单系统',1) on DUPLICATE KEY UPDATE state = state + 1 ;
# 第一次操作 1 row affected in 19 ms
# 第二次操作 2 rows affected in 15 ms
根据 row的值不同,判断值是否有

  • 假如是更新的场景
update table set status = “paid” where id = xxx and status = “unpaid”;
  • 网上还有 MVCC 通过使用版本号等其他方式,我觉得这些都不标准,我们希望我们有一个标准的方式来做这个事,所以,最好还是用一个 ID。

3. 存储

  • 因为幂等性服是分布式的,所以需要存储也是共享的。
  • 这样每个服务就变成没有状态的了。但是,这个存储就成了一个非常关键的依赖,其扩展性和可用性也成了非常关键的指标。
  • 你可以使用关系型数据库,或是 key-value 的 NoSQL(如 MongoDB)来构建这个存储系统。

3. HTTP 的幂等性

1. http 常见方法的幂等性

image.png
  1. HTTP GET 方法用于获取资源,不应有副作用,所以是幂等的

  2. HTTP HEAD 和 GET 本质是一样的,区别在于 HEAD 不含有呈现数据,而仅仅是 HTTP 头信息,不应有副作用,也是幂等的

    • 欲判断某个资源是否存在,我们通常使用 GET,但这里用 HEAD 则意义更加明确。也就是说,HEAD 方法可以用来做探活使用。
  3. HTTP OPTIONS 主要用于获取当前 URL 所支持的方法,所以也是幂等的

    • 若请求成功,则它会在 HTTP 头中包含一个名为“Allow”的头,值是所支持的方法,如“GET, POST”。
  4. HTTP DELETE 方法用于删除资源,有副作用,但它应该满足幂等性。

    • 比如:DELETE http://www.forum.com/article/4231,调用一次和 N 次对系统产生的副作用是相同的,即删掉 ID 为 4231 的帖子。因此,调用者可以多次调用或刷新页面而不必担心引起错误。
  5. HTTP POST 方法用于创建资源,所对应的 URI 并非创建的资源本身,而是去执行创建动作的操作者,有副作用,不满足幂等性。

    • 比如:POST http://www.forum.com/articles的语义是在http://www.forum.com/articles下创建一篇帖子,HTTP 响应中应包含帖子的创建状态以及帖子的 URI。两次相同的 POST 请求会在服务器端创建两份资源,它们具有不同的 URI;所以,POST 方法不具备幂等性。
  6. HTTP PUT 方法用于创建或更新操作,所对应的 URI 是要创建或更新的资源本身,满足幂等性

    • 用于更新资源,没有的话则执行创建操作。每次执行请求时都会先判断一下序号为1的花信息是否存在,不存在则创建,否则视为更新。
  7. HTTP PATCH 是对PUT方法的补充,用来对已知资源进行局部更新 , 不满足幂等性

  8. TRACE 方法 实现沿通向目标资源的路径的消息环回(loop-back)测试,提供了一种实用的 debug 机制。

    • 请求的最终接收者应当原样反射(reflect)它接收到的消息,除了以下字段部分,作为一个Content-Type 为 message/http 的 200(OK)响应的消息的主体(body)返回给客户端。

2. POST 方式不支持幂等性,的幂等性设计。

  1. 在表单中需要隐藏一个 token(可以前后端生成)

  2. 后端把用户提交的数据和这个 token 保存在数据库中

    • 如果有重复提交,那么数据库中的 token 会做排它限制,从而做到幂等性。
  3. 更为稳妥的做法是,后端成功后向前端返回 302 跳转,把用户的前端页跳转到 GET 请求,把刚刚 POST 的数据给展示出来。如果是 Web 上的最好还把之前的表单设置成过期,这样用户不能通过浏览器后退按钮来重新提交。这个模式又叫做 PRG 模式(Post/Redirect/Get)。

3 . POST 方式不支持幂等性,常用方式

  • 前端实现
    • 点击提交, 按钮disable
  • 后端
    • 数据库唯一索引
      • 适合场景
        • 插入操作
    • 基于redis 实现一套幂等性防重框架
      • 适合场景
        • 更新操作,配合业务操作
      • 步骤
        • 用个拦截器,拦截所有参数, 参数拼接作为key去redis 判断下。
          • 适合 系统参数比较规定。
        • 先uuid 获取,然后操作成功删除。
    • 数据库状态机
      • 适合场景
        • 更新操作
    • 通过锁实现
      • 使用数据库实现幂等性
        • 通过悲观锁来实现幂等性
        • 通过唯一索引来实现幂等性
        • 通过乐观锁来实现幂等性
      • 使用 JVM 锁实现幂等性
      • 使用分布式锁实现幂等性
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容