精华内容
下载资源
问答
  • 2019-09-30 09:33:00

    幂等性

    幂等:是一个数学概念,表示N次变换和1次变换的结果相同。

    幂等性: 就是用户对于同一个操作,发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用。也可以称为冲正。

    保证幂等策略: 幂等需要通过唯一的业务单号来保证。也就是说相同的业务单号,认为是同一笔业务。使用这个唯一的业务单号来确保,后面多次的相同的业务单号的处理逻辑和执行效果是一致的。

    下面以支付为例,在不考虑并发的情况下,实现幂等很简单:①先查询一下订单是否已经支付过,②如果已支付过,则返回支付成功;如果没有支付,进行支付流程,修改订单状态为“已支付”。

    保证幂等方案是分成两步:第②步依赖第①步的查询结果,无法保证原子性的,在高并发下就会出现下面的情况:第二次请求在第一次请求第②步订单状态还没有修改为‘已支付状态’的情况下到来。既然得出了这个结论,余下的问题也就变得很简单:把查询和变更状态操作加锁,将并行操作改为串行操作。

    方案一:乐观锁

    如果只是更新已有的数据,没有必要对业务进行加锁,设计表结构时使用乐观锁,一般通过version来做乐观锁,这样既能保证执行效率,又能保证幂等。例如: UPDATE tab1 SET col1=1,version=version+1 WHERE version=#version# 不过,乐观锁存在失效的情况,就是常说的ABA问题,不过如果version版本一直是自增的就不会出现ABA的情况。(从网上找了一张图片很能说明乐观锁,引用过来,出自Mybatis对乐观锁的支持)

    方案二:防重表

    使用订单号orderNo做为去重表的唯一索引,每次请求都根据订单号向去重表中插入一条数据。第一次请求查询订单支付状态,当然订单没有支付,进行支付操作,无论成功与否,执行完后更新订单状态为成功或失败,删除去重表中的数据。后续的订单因为表中唯一索引而插入失败,则返回操作失败,直到第一次的请求完成(成功或失败)。可以看出防重表作用是加锁的功能。

    方案三:分布式锁

    这里使用的防重表可以使用分布式锁代替,比如Redis。订单发起支付请求,支付系统会去Redis缓存中查询是否存在该订单号的Key,如果不存在,则向Redis增加Key为订单号。查询订单支付已经支付,如果没有则进行支付,支付完成后删除该订单号的Key。通过Redis做到了分布式锁,只有这次订单订单支付请求完成,下次请求才能进来。相比去重表,将放并发做到了缓存中,较为高效。思路相同,同一时间只能完成一次支付请求。

    方案四:token令牌

    这种方式分成两个阶段:申请token阶段和支付阶段。 第一阶段,在进入到提交订单页面之前,需要订单系统根据用户信息向支付系统发起一次申请token的请求,支付系统将token保存到Redis缓存中,为第二阶段支付使用。 第二阶段,订单系统拿着申请到的token发起支付请求,支付系统会检查Redis中是否存在该token,如果存在,表示第一次发起支付请求,删除缓存中token后开始支付逻辑处理;如果缓存中不存在,表示非法请求。 实际上这里的token是一个信物,支付系统根据token确认,你是你妈的孩子。不足是需要系统间交互两次,流程较上述方法复杂。

    方案五:支付缓冲区

    把订单的支付请求都快速地接下来,一个快速接单的缓冲管道。后续使用异步任务处理管道中的数据,过滤掉重复的待支付订单。优点是同步转异步,高吞吐。不足是不能及时地返回支付结果,需要后续监听支付结果的异步返回

    更多相关内容
  • 解决主单下多个子单,并会分开多次请求的场景下,主单的幂等问题。 场景: 上游系统请求下游系统。 上游情况: 上游单据分为主单和子单(明细单),一个主单对应多个子单。 发送请求给下游系统的时候,参数结构...

    解决主单下多个子单,并会分开多次请求的场景下,主单号的幂等问题。

    场景:

    上游系统请求下游系统。

    上游情况:

    上游单据分为主单和子单(明细单),一个主单对应多个子单。

    发送请求给下游系统的时候,参数结构是一个主单包含多个子单,并且可能会多次请求下游系统

    下游情况:

    下游单据同样分为主单和子单(明细单),一个主单对应多个子单。主单需要幂等。

    上下游单据对应关系:

    上游主单和下游主单是1:n关系。

    上游子单和下游子单是1:1关系。

    上游请求参数结构示例:

    {
        "bizCode":"INVENTORY_ADJUST",
        "subOrderDTOs":[
            {
                "detailOrderId":"2000001394868005",
                "itemCode":"..."
            },
            {
                "detailOrderId":"2000001394868006",
                "itemCode":"..."
            }
        ],
        "locationCode":"TESTCS1",
        "mainOrderId":"CSCSTESTCS1kt210914000005",
        "merchantCode":"CSCS"
    }

    请求示例:

    一个上游主单会拆分为多次请求:

    第一次请求上游主单号CSCSTESTCS1kt210914000005,对应上游子单号2000001394868005和2000001394868006;

    第二次请求上游主单号同样是CSCSTESTCS1kt210914000005,但是对应上游子单号是2000001394868007,2000001394868008和2000001394868009;

    第三次有请求上游主单号同样是CSCSTESTCS1kt210914000005,但是对应上游子单号是,2000001394868008和2000001394868009;(子单号在第二次请求的时候已经包含在内了)

    第四次有请求上游主单号同样是CSCSTESTCS1kt210914000005,但是对应上游子单号是,2000001394868009和2000001394868008;(与第三次子单号顺序不同)

    第五次请求上游主单号CSCSTESTCS1kt210914000005,对应上游子单号2000001394868005和2000001394868006;(与第一次完全相同)

    请求分析

    对于前三次的请求,我们认为都是不需要幂等的。

    因为子单号的信息不同,我们认为是不同请求。对于第三次的子单号在第二次请求的时候已经包含在内了,但是我们认为这是一次有效的请求,不需要进行幂等。

    第四次和第五次请求是需要幂等的。因为子单号相同,顺序不同,认为是同一请求。

    处理方案

    主单号是一个字符串信息。

    方案1

    是用商家编码、仓编码、上游主单号、业务编码组成

    "CSCS|TESTCS1|CSCSTESTCS1kt210914000005|INVENTORY_ADJUST"

    存在问题:

    同一主单不同子单,会被直接幂等掉,无法进行后续操作。

    方案2

    用商家编码、仓编码、上游主单号、子单号、业务编码组成

    "CSCS|TESTCS1|CSCSTESTCS1kt210914000005|2000001394868005|2000001394868006|INVENTORY_ADJUST"

    存在问题:

    如果统一主单下有多个子单,如果子单过多的话,会导致组成的字符串过长,存入数据库时会出错。如果子单号排序不同,会被认为是不同单据。

    方案3

    用商家编码、仓编码、上游主单号、子单号排序后组成字符串再MD5加密、业务编码组成

    "CSCS|TESTCS1|CSCSTESTCS1kt210914000005|2f7625f846b788abfd84b2e991dd03f8|INVENTORY_ADJUST"

    优点:

    同时解决了方案2和方案1的问题,保证了子单号的顺序,一次请求下子单过多,在经过MD5加密后可以得到固定长度的字符串,这样就可以满足主单据的幂等要求了。

    伪代码

    //获取子单号列表
    List<String> outDetailOrderIdList  = getOutDetailOrderIdList(adjustOrderDTO);
    //子单号列表排序
    outDetailOrderIdList.sort(String::compareTo);
    //子单号列表拼接字符串
    String outDetailOrderIdListJoinStr = String.join("|", outDetailOrderIdList);
    //子单号列表字符串进行MD5编码
    String outDetailOrderIdListMd5 = MD5Util.toMD5(outDetailOrderIdListJoinStr);
            
    //产生幂等号
    String idempotentNoStr =  adjustOrderDTO.getMerchantCode() + VERTICAL_LINE + 
    adjustOrderDTO.getWarehouseCode() + VERTICAL_LINE + 
    adjustOrderDTO.getOutMainOrderId() +VERTICAL_LINE + 
    outDetailOrderIdListMd5 + VERTICAL_LINE + 
    adjustOrderDTO.getOutBizCode() + VERTICAL_LINE;
            

    结论

    所以我们采用了方案3来做幂等验证,刚好满足业务需求。

    展开全文
  • 深入理解幂等

    2021-01-13 18:39:52
    什么是幂等性HTTP/1.1中对幂等性的定义是:一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时问题除外)。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。Methods can ...

    什么是幂等性

    HTTP/1.1中对幂等性的定义是:一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时等问题除外)。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。

    Methods can also have the property of “idempotence” in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.

    这里需要关注几个重点:

    幂等不仅仅只是一次(或多次)请求对资源没有副作用(比如查询数据库操作,没有增删改,因此没有对数据库有任何影响)。

    幂等还包括第一次请求的时候对资源产生了副作用,但是以后的多次请求都不会再对资源产生副作用。

    幂等关注的是以后的多次请求是否对资源产生的副作用,而不关注结果。

    网络超时等问题,不是幂等的讨论范围。

    幂等性是系统服务对外一种承诺(而不是实现),承诺只要调用接口成功,外部多次调用对系统的影响是一致的。声明为幂等的服务会认为外部调用失败是常态,并且失败之后必然会有重试。

    什么情况下需要幂等

    业务开发中,经常会遇到重复提交的情况,无论是由于网络问题无法收到请求结果而重新发起请求,或是前端的操作抖动而造成重复提交情况。 在交易系统,支付系统这种重复提交造成的问题有尤其明显,比如:

    用户在APP上连续点击了多次提交订单,后台应该只产生一个订单;

    向支付宝发起支付请求,由于网络问题或系统BUG重发,支付宝应该只扣一次钱。 很显然,声明幂等的服务认为,外部调用者会存在多次调用的情况,为了防止外部多次调用对系统数据状态的发生多次改变,将服务设计成幂等。

    幂等VS防重

    上面例子中小明遇到的问题,只是重复提交的情况,和服务幂等的初衷是不同的。重复提交是在第一次请求已经成功的情况下,人为的进行多次操作,导致不满足幂等要求的服务多次改变状态。而幂等更多使用的情况是第一次请求不知道结果(比如超时)或者失败的异常情况下,发起多次请求,目的是多次确认第一次请求成功,却不会因多次请求而出现多次的状态变化。

    什么情况下需要保证幂等性

    以SQL为例,有下面三种场景,只有第三种场景需要开发人员使用其他策略保证幂等性:

    SELECT col1 FROM tab1 WHER col2=2,无论执行多少次都不会改变状态,是天然的幂等。

    UPDATE tab1 SET col1=1 WHERE col2=2,无论执行成功多少次状态都是一致的,因此也是幂等操作。

    UPDATE tab1 SET col1=col1+1 WHERE col2=2,每次执行的结果都会发生变化,这种不是幂等的。

    为什么要设计幂等性的服务

    幂等可以使得客户端逻辑处理变得简单,但是却以服务逻辑变得复杂为代价。满足幂等服务的需要在逻辑中至少包含两点:

    首先去查询上一次的执行状态,如果没有则认为是第一次请求

    在服务改变状态的业务逻辑前,保证防重复提交的逻辑

    幂等的不足

    幂等是为了简化客户端逻辑处理,却增加了服务提供者的逻辑和成本,是否有必要,需要根据具体场景具体分析,因此除了业务上的特殊要求外,尽量不提供幂等的接口。

    增加了额外控制幂等的业务逻辑,复杂化了业务功能;

    把并行执行的功能改为串行执行,降低了执行效率。

    保证幂等策略

    幂等需要通过唯一的业务单号来保证。也就是说相同的业务单号,认为是同一笔业务。使用这个唯一的业务单号来确保,后面多次的相同的业务单号的处理逻辑和执行效果是一致的。 下面以支付为例,在不考虑并发的情况下,实现幂等很简单:①先查询一下订单是否已经支付过,②如果已经支付过,则返回支付成功;如果没有支付,进行支付流程,修改订单状态为‘已支付’。

    防重复提交策略

    上述的保证幂等方案是分成两步的,第②步依赖第①步的查询结果,无法保证原子性的。在高并发下就会出现下面的情况:第二次请求在第一次请求第②步订单状态还没有修改为‘已支付状态’的情况下到来。既然得出了这个结论,余下的问题也就变得简单:把查询和变更状态操作加锁,将并行操作改为串行操作。

    乐观锁

    如果只是更新已有的数据,没有必要对业务进行加锁,设计表结构时使用乐观锁,一般通过version来做乐观锁,这样既能保证执行效率,又能保证幂等。例如: UPDATE tab1 SET col1=1,version=version+1 WHERE version=#version# 不过,乐观锁存在失效的情况,就是常说的ABA问题,不过如果version版本一直是自增的就不会出现ABA的情况。(从网上找了一张图片很能说明乐观锁,引用过来,出自Mybatis对乐观锁的支持)

    防重表

    使用订单号orderNo做为去重表的唯一索引,每次请求都根据订单号向去重表中插入一条数据。第一次请求查询订单支付状态,当然订单没有支付,进行支付操作,无论成功与否,执行完后更新订单状态为成功或失败,删除去重表中的数据。后续的订单因为表中唯一索引而插入失败,则返回操作失败,直到第一次的请求完成(成功或失败)。可以看出防重表作用是加锁的功能。

    分布式锁

    这里使用的防重表可以使用分布式锁代替,比如Redis。订单发起支付请求,支付系统会去Redis缓存中查询是否存在该订单号的Key,如果不存在,则向Redis增加Key为订单号。查询订单支付已经支付,如果没有则进行支付,支付完成后删除该订单号的Key。通过Redis做到了分布式锁,只有这次订单订单支付请求完成,下次请求才能进来。相比去重表,将放并发做到了缓存中,较为高效。思路相同,同一时间只能完成一次支付请求。

    token令牌

    这种方式分成两个阶段:申请token阶段和支付阶段。 第一阶段,在进入到提交订单页面之前,需要订单系统根据用户信息向支付系统发起一次申请token的请求,支付系统将token保存到Redis缓存中,为第二阶段支付使用。 第二阶段,订单系统拿着申请到的token发起支付请求,支付系统会检查Redis中是否存在该token,如果存在,表示第一次发起支付请求,删除缓存中token后开始支付逻辑处理;如果缓存中不存在,表示非法请求。 实际上这里的token是一个信物,支付系统根据token确认,你是你妈的孩子。不足是需要系统间交互两次,流程较上述方法复杂。

    支付缓冲区

    把订单的支付请求都快速地接下来,一个快速接单的缓冲管道。后续使用异步任务处理管道中的数据,过滤掉重复的待支付订单。优点是同步转异步,高吞吐。不足是不能及时地返回支付结果,需要后续监听支付结果的异步返回。

    二、应用场景

    支付业务

    如上图所示用户下单后点击支付按钮进行支付。支付系统根据单号创建支付记录,然后调用银行接口,当一行扣款成功后,支付表修改支付状态为已支付。

    但是,存在这样的情况,用户点击按钮无效,连续点击多次,是否会出现同一个订单支付多次?如何避免?这就是今天要讲的的幂等性。

    作为对比,先看没有实现幂等性,也就是用户连续点击按钮,多次调用银行接口的情况。代码如下:

    public void  payForOrder(String orderId){

    Order order = orderDao.findById(orderId);

    Payment payment = paymentDao.findPaymentByOrderId(orderId);

    payment.setOrderId(orderId);

    payment.setMoney(order.getMoney());

    payment.setPayStatus("0");//正在处理中

    paymentDao.update(payment);

    String flag = tranService.invoke(url,paymentId); //调用银行接口

    payment.setPayStatus(flag);

    paymentDao.update(payment);

    }

    有上面代码可知,当用户对一个单多次点击支付时,上面的代码一定会出现多次调用银行接口的情况。那么问题来了,如何保证幂等性?

    介绍一个使用乐观锁实现幂等性的方案。通过新增版本号字段来实现。先看代码:

    public payForOrder(String orderId){

    Order order = orderDao.findById(orderId);

    Payment payment = paymentDao.findPaymentByOrderId(orderId);

    boolean invokeInterface = false ;

    payment.setOrderId(orderId);

    payment.setMoney(order.getMoney());

    payment.setPayStatus("0");//正在处理中

    payment.setVersion(0);

    int records = paymentDao.updateByVersion(payment);

    if(records ){

    String flag = tranService.invoke(url,payment.getId()); //调用银行接口

    payment.setPayStatus(flag);

    paymentDao.update(payment);

    }else{

    logger.error("重复调用............+orderId="+orderId);

    }

    }

    updateByVersion的模拟代码为:

    update  t_payment  set  orderId = #{orderId} , money=#{money},  payStatus=#{payStatus}  version=#{ version } +1

    where id=#{id} and version=#{version}

    用户第一次点击时  paymentDao.updateByVersion(payment) 的返回值为1 ,此时可以调用银行接口;当第二次点击时updateByVersion的返回值为 0 ,不会调用银行接口,实现了幂等性。

    作者:守住阳光

    链接:https://www.jianshu.com/p/50462732a610

    来源:简书

    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    标签:orderId,请求,多次,订单,理解,深入,支付,payment

    来源: https://www.cnblogs.com/qingsongjava/p/12022158.html

    展开全文
  • 幂等怎么保证的

    千次阅读 2022-02-11 09:08:17
    在分布式系统中,保证接口的幂等性非常重要,如提交订单,扣款接口都要保证幂等性,不然会造成重复创建订单,重复扣款,那么如何保证接口的幂等性呢? 前端保证幂等性的方法 按钮只能点击一次 用户点击按钮后将...

    实现幂

    介绍
    幂等性就是同一个操作执行多次,产生的效果一样。如http的get请求,数据库的select请求就是幂等的

    在分布式系统中,保证接口的幂等性非常重要,如提交订单,扣款等接口都要保证幂等性,不然会造成重复创建订单,重复扣款,那么如何保证接口的幂等性呢?

    前端保证幂等性的方法
    按钮只能点击一次
    用户点击按钮后将按钮置灰,或者显示loading状态

    RPG模式
    即Post-Redirect-Get,当客户提交表单后,去执行一个客户端的重定向,转到提交成功页面。避免用户按F5刷新导致的重复提交,也能消除按浏览器后退键导致的重复提交问题。目前绝大多数公司都是这样做的,比如淘宝,京东等

    后端保证幂等性的方法
    使用唯一索引
    对业务唯一的字段加上唯一索引,这样当数据重复时,插入数据库会抛异常

    状态机幂等
    如果业务上需要修改订单状态,例如订单状态有待支付,支付中,支付成功,支付失败。设计时最好只支持状态的单向改变。这样在更新的时候就可以加上条件,多次调用也只会执行一次。例如想把订单状态更新为支持成功,则之前的状态必须为支付中

    update table_name set status = 支付成功 where status = 支付中
    1
    乐观锁实现幂等

    查询数据获得版本号
    通过版本号去更新,版本号匹配则更新,版本号不匹配则不更新
    -- 假如查询出的version为1
    select version from table_name where userid = 10;
    -- 给用户的账户加10
    update table_name set money = money -10, version = version + 1 where userid = 10 and version = 1
    1
    2
    3
    4
    也可以通过条件来实现乐观锁,如库存不能超卖,数量不能小于0

    update table_name set num = num - 10 where num - 10 >= 0
    1
    防重表
    增加一个防重表,业务唯一的id作为唯一索引,如订单号,当想针对订单做一系列操作时,可以向防重表中插入一条记录,插入成功,执行后续操作,插入失败,则不执行后续操作。本质上可以看成是基于MySQL实现的分布式锁。根据业务场景决定执行成功后,是否删除防重表中对应的数据

    分布式锁实现幂等
    执行方法时,先根据业务唯一的id获取分布式锁,获取成功,则执行,失败则不执行。分布式锁可以基于redis,zookeeper,mysql来实现,分布式锁的细节就不介绍了

    select+insert
    先查询一下有没有符合要求的数据,如果没有再执行插入。没有并发的系统中可以保证幂等性,高并发下不要用这种方法,也会造成数据的重复插入。我一般做消息幂等的时候就是先select,有数据直接返回,没有数据加分布式锁进行insert操作

    全局唯一号实现幂等
    通过source(来源)+ seq(序列号)来判断请求是否重复,重复则直接返回请求重复提交,否则执行。如当多个三方系统调用服务的时候,就可以采用这种方式
     

    幂等性最早是数学里面的一个概念,后来被用于计算机领域,用于表示任意多次请求执行的结果均与一次请求执行的结果相同,对于一个接口而言,即无论调用多少次,最终得到的结果都是一样的,用数学语言表达就是f(x)=f(f(x))。

    如何保证幂等性?

    (1) 前端拦截

    (2) 使用数据库实现幂等性

    (3) 使用 JVM 锁实现幂等性

    (4) 使用分布式锁实现幂等性

    (1) 前端拦截

    前端拦截是指通过 Web 站点的页面进行请求拦截,比如在用户点击完“提交”按钮后,我们可以把按钮设置为不可用或者隐藏状态,避免用户重复点击。或者添加提交按钮的事件处理,可以设置一个缓冲时间,例如2秒内的重复提交,只执行最后一次的请求。但前端拦截有一个致命的问题,如果是懂行的程序员或者黑客可以直接绕过页面的 JS 执行,直接模拟请求后端的接口,这样的话,我们前端的这些拦截就不能生效了。因此除了前端拦截一部分正常的误操作之外,后端的验证必不可少。

    (2) 数据库实现

    ① 通过悲观锁来实现幂等性

    ② 通过唯一索引来实现幂等性

    ③ 通过乐观锁来实现幂等性

    ① 通过悲观锁来实现幂等性

    使用悲观锁实现幂等性,一般是配合事务一起来实现,在没有使用悲观锁时,我们通常的执行过程是这样的,首先来判断数据的状态,执行 SQL 如下:

    select status from table_name where id='xxx';

    然后再进行添加操作:

    insert into table_name (id) values ('xxx');

    最后再进行状态的修改:

    update table_name set status='xxx';

    但这种情况因为是非原子操作,所以在高并发环境下可能会造成一个业务被执行两次的问题,当一个程序在执行中时,而另一个程序也开始状态判断的操作。因为第一个程序还未来得及更改状态,所以第二个程序也能执行成功,这就导致一个业务被执行了两次。

    在这种情况下我们就可以使用悲观锁来避免问题的产生,实现 SQL 如下所示:

    begin;  # 1.开始事务
    select * from table_name where id='xxx' for update; # 2.查询状态
    insert into table_name (id) values ('xxx'); # 3.添加操作
    update table_name set status='xxx'; # 4.更改操作
    commit; # 5.提交事务
    

    在实现的过程中需要注意以下两个问题:

    如果使用的是 MySQL 数据库,必须选用 innodb 存储引擎,因为 innodb 支持事务;

    id 字段一定要是主键或者是唯一索引,不然会锁表,影响其他业务执行。

    ② 通过唯一索引来实现幂等性

    我们可以创建一个唯一索引的表来实现幂等性,在每次执行业务之前,先执行插入操作,因为唯一字段就是业务的 ID,因此如果重复插入的话会触发唯一约束而导致插入失败。在这种情况下(插入失败)我们就可以判定它为重复提交的请求。

    唯一索引表的创建示例如下:

    CREATE TABLE `table_name` (
      `id` int NOT NULL AUTO_INCREMENT,
      `orderid` varchar(32) NOT NULL DEFAULT '' COMMENT '唯一id',
      PRIMARY KEY (`id`),
      UNIQUE KEY `uq_orderid` (`orderid`) COMMENT '唯一约束'
    ) ENGINE=InnoDB;

    ③ 通过乐观锁来实现幂等性 

    乐观锁是指在执行数据操作时(更改或添加)进行加锁操作,其他时间不加锁,因此相比于整个执行过程都加锁的悲观锁来说,它的执行效率要高很多。

    乐观锁可以通过版本号来实现,例如以下 SQL:

    update table_name set version = version + 1 where version = 0;

    (3) 使用 JVM 锁实现幂等性

    JVM 锁实现是指通过 JVM 提供的内置锁如 Lock 或者是 synchronized 来实现幂等性。使用 JVM 锁来实现幂等性的一般流程为:首先通过 Lock 对代码段进行加锁操作,然后再判断此订单是否已经被处理过,如果未处理则开启事务执行订单处理,处理完成之后提交事务并释放锁。JVM 锁存在的最大问题在于,它只能应用于单机环境,因为 Lock 本身为单机锁,所以它就不适应于分布式多机环境。

    (4) 使用分布式锁实现幂等性

    分布式锁实现幂等性的逻辑是,在每次执行方法之前先判断是否可以获取到分布式锁,如果可以,则表示为第一次执行方法,否则直接舍弃请求即可。

    需要注意的是分布式锁的 key 必须为业务的唯一标识,我们通常使用 Redis 或者 ZooKeeper 来实现分布式锁;如果使用 Redis 的话,则用 set 命令来创建和获取分布式锁,执行示例如下:

    127.0.0.1:6379> set lock true ex 30 nx
    OK # 创建锁成功

    其中,ex 是用来设置超时时间的;而 nx 是 not exists 的意思,用来判断键是否存在。如果返回的结果为“OK”,则表示创建锁成功,否则表示重复请求,应该舍弃。

    幂等性注意事项

    幂等性的实现与判断需要消耗一定的资源,因此不应该给每个接口都增加幂等性判断,要根据实际的业务情况和操作类型来进行区分。例如,我们在进行查询操作和删除操作时就无须进行幂等性判断。查询操作查一次和查多次的结果都是一致的,因此我们无须进行幂等性判断。删除操作也是一样,删除一次和删除多次都是把相关的数据进行删除(这里的删除指的是条件删除而不是删除所有数据),因此也无须进行幂等性判断。

    幂等性的关键步骤

    实现幂等性的关键步骤分为以下三个:

    (1)每个请求操作必须有唯一的 ID,而这个 ID 就是用来表示此业务是否被执行过的关键凭证,例如,订单支付业务的请求,就要使用订单的 ID 作为幂等性验证的 Key;

    (2)每次执行业务之前必须要先判断此业务是否已经被处理过;

    (3)第一次业务处理完成之后,要把此业务处理的状态进行保存,比如存储到 Redis 中或者是数据库中,这样才能防止业务被重复处理。

    项目实战

    WKD项目中,有些三方系统流程(eg:采购出入库)会通过MQ发送到我们中台库存中心进行异步消费,为了防止MQ传过来的订单数据在我们消费端重复处理,需要做幂等性,解决方法就是对传送消息体dto里中的全局唯一单号”orderNo”字段做唯一性校验,先从缓存中校验单号是否存在,不存在的话说明没有消费过,我们处理完业务逻辑后,会把单号放入redis缓存中,如果单号重复的话就抛异常”单号重复”。

    补充:方法2,还有就是我们有一张库存变动流水表,记录了库存变更成功的详细信息,我们可以拿全局唯一性的单号字段”orderNo”字段去数据库里进行查询,如果已经在日志表中,那么就不再处理这条消息。

    Exchange中心

    常见面试题

    (1) 什么是幂等性?如何保证接口的幂等性?

    (2) 幂等性需要注意什么问题?

    (3) 实现幂等性的关键步骤有哪些?

    (4) 说一说数据库实现幂等性的执行细节?

    (5) 项目用到幂等性得场景?怎么处理得?

    (6) mq重复消费怎么处理?

     

    展开全文
  • 深入理解Java幂等

    2020-06-29 00:31:35
    深入理解Java幂等性 1. 概念理解 幂等:是一个数学概念,表示N次变换和1次变换的结果相同。 幂等操作:其特点是任意多次执行所产生的影响均与一次执行的影响相同(不会改变资源状态,对数据没有副作用)。 幂等性...
  • 核心思想:通过**唯一的业务单号**保证幂等性。 非并发的情况,可以查询某个业务是否操作过,没有则执行(查询券是否使用过);并发时,操作过程加锁(分布式锁)。 select操作,不对数据有影响,天
  • Delete操作的幂等

    2021-06-17 21:46:27
    Delete操作的幂等性 根据唯一业务号去删除 第一次删除时,已将数据删除 第二次再次执行时,由于找不到记录,所以返回的结果是0,对业务数据没有影响。可在删除前进行数据的查询。 删除操作没有唯一业务号,则要看...
  • Java中的幂等

    千次阅读 2019-12-18 18:19:44
    深入理解Java中的幂等性什么是幂等幂等性的使用场景幂等和防重保证幂等性的情况设计幂等性服务保证幂等策略防重复提交策略乐观锁防重表分布式锁token令牌支付缓冲区幂等的不足 什么是幂等幂等性定义: 一次和...
  • 接口幂等性及如何实现幂等性 概述 幂等性,用数学函数表示为,f(f(x)) = f(x),即幂等元素运行多次,还等于原来运行的结果。延伸到我们java接口上就是,一个接口调用多次(每次入参相同),与这个接口调用一次的...
  • 什么是幂等性、幂等性解决方案

    千次阅读 2020-11-22 13:29:59
    一、什么是幂等幂等性是一个技术术语、类似鉴权、都有一堆的解决方案 二、什么情况需要幂等 业务开发中,经常会遇到重复提交的情况,无论是由于网络问题无法收到请求结果而重新发起请求,或是前端的操作抖动而...
  • 一 、天然幂等 1、查询 查询一次和查询多次一样的。 2、删除数据 删除数据,仅仅第一次删除是真正的操作数据,之后删除直接返回成功,这样保证了幂等。 二、锁 1、MVCC方案(乐观锁) 多版本并发控制,update ...
  • 1.什么是幂等性 HTTP/1.1中对幂等性的定义是:一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时问题除外)。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。 ...
  • 幂等性学习一:什么是幂等性在这里需要有以下几个问题需要注意:1:幂等性的实质是一次或多次请求同一个资源,其结果是相同的。其关注的是对资源产生的影响(副作用)而不是结果,结果可以不同。比如列表查询的时候,...
  • 幂等 是在多个同样的交易指令或请求同时或者先后到达后台,即使重复执行,系统也必须始终提供与一致的状态,而不能有其他的副作用。 措施 1、前端预防重复提交。 2、唯一的单号,且状态变更需要检查。 3、 由于高...
  • 1.什么是幂等?在编程中,一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。用通俗的话讲:就是针对一个操作...
  • 分布式系统接口幂等

    千次阅读 2018-08-17 10:56:58
    1.幂等性定义 1.1 数学定义 1.2 HTTP规范的定义 2. 何种接口提供幂等性 2.1 HTTP支持幂等性的接口 2.2 实际业务 3.分布式系统接口幂等性 References 1.幂等性定义 ...
  • 幂等性问题剖析

    2021-07-28 21:23:37
    所谓幂等,简单地说,就是对接口的多次调用所产生的结果和调用一次是一致的。扩展一下,这里的接口,可以理解为对外发布的HTTP接口或者Thrift接口,也可以是接收消息的内部接口,甚至是一个内部方法或操作。 数学上...
  • 谈谈幂等机制

    千次阅读 2018-06-30 14:37:42
    一、什么是幂等性? 幂等性(Idempotence)。在HTTP/1.1规范中幂等性的定义是: Methods can also have the property of “idempotence” in that (aside from error or expiration issues) the side-effects of N &...
  • 幂等性原理

    2020-09-14 22:49:01
    什么是幂等性 HTTP/1.1中对幂等性的定义是:一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时问题除外)。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。 Methods ...
  • java接口幂等性校验

    千次阅读 2019-05-27 17:12:00
    //待更新 转载于:https://www.cnblogs.com/mr-wuxiansheng/p/10931796.html
  • 幂等处理

    2020-08-05 11:26:07
    幂等处理(每天看过思考过的内容记录一下,) idepotence defination: Methods can also have the property of “idempotence” in that (aside from error or expiration issues) the side-effects of N > 0 ...
  • 幂等

    2019-09-10 11:35:17
    什么是幂等性 HTTP/1.1中对幂等性的定义是:一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时问题除外)。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。 Methods...
  • 前言 现如今我们的系统大多拆分为分布式SOA,或者微服务,一套系统中包含了多个子系统服务,而一个子系统服务往往会去调用另一个服务,而服务调用服务无非就是使用RPC通信或者restful,既然...幂等性:就是用户对于...
  • 目录 什么是幂等? 读和写请求都需要做幂等吗? 系统的哪部分需要做幂等?...写请求需要做幂等(对数据发生改变了就根据需要做幂等)。 系统的哪部分需要做幂等? 因为数据访问层和数据库直接联系,涉及到数据的
  • 接口幂等性实战
  • 为什么要设计幂等性的服务 什么是幂等性 HTTP/1.1中对幂等性的定义是:一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时问题除外)。也就是说,其任意多次执行对资源本身所产生的影响均 与一次...
  • 使用mariadb(mysql),redis,zookeeper及其客户端Redisson,Curator来实现分布式锁,及其优劣对比与问题。使用分布式锁,封装注解实现接口幂等性。
  • 什么是幂等策略?

    2017-06-08 17:26:04
    幂等概念来自数学,表示N次变换和1次变换的结果是相同的。这里讨论在某些场景下,客户端在调用服务没有达到预期结果时,会进行多次调用,为避免多次重复的调用对服务资源产生副作用,服务提供者会承诺满足幂等。 举...
  • 订单交易系统中的幂等设计

    千次阅读 2018-04-23 11:15:24
    在一个典型的订单交易系统中,防重和幂等设计是重要而又非常基本的概念。防重是指重复多次提交同样的交易指令或者订单请求到后台,系统必须能够去重,防止重复执行;而幂等,则是在多个同样的交易指令或请求同时或者...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 562
精华内容 224
关键字:

幂等单号