美文网首页
03 幂等性设计

03 幂等性设计

作者: 格林哈 | 来源:发表于2022-09-28 11:21 被阅读0次

    一、概述

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

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

    3. 存储

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

    3. HTTP 的幂等性

    1. http 常见方法的幂等性

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

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

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

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

    5. HTTP POST 方法用于创建资源,所对应的 URI 并非创建的资源本身,而是去执行创建动作的操作者,有副作用,不满足幂等性。

    6. HTTP PUT 方法用于创建或更新操作,所对应的 URI 是要创建或更新的资源本身,有副作用,它应该满足幂等性。

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

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

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

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

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

    • 前端实现
      • 点击提交, 按钮disable
    • 后端
      • 数据库唯一索引
        • 适合场景
          • 插入操作
      • 基于redis 实现一套幂等性防重框架
        • 适合场景
          • 更新操作,配合业务操作
        • 步骤
          • 用个拦截器,拦截所有参数, 参数拼接作为key去redis 判断下。
            • 适合 系统参数比较规定。
          • 先uuid 获取,然后操作成功删除。
      • 数据库状态机
        • 适合场景
          • 更新操作
      • 通过锁实现
        • 使用数据库实现幂等性
          • 通过悲观锁来实现幂等性
          • 通过唯一索引来实现幂等性
          • 通过乐观锁来实现幂等性
        • 使用 JVM 锁实现幂等性
        • 使用分布式锁实现幂等性

    相关文章

      网友评论

          本文标题:03 幂等性设计

          本文链接:https://www.haomeiwen.com/subject/yvkcartx.html