幂等性实现方案

一、什么是幂等性

        所谓幂等性,简单地说,就是对接口的多次调用所产生的结果和调用一次是一致的。扩展一下,这里的接口,可以理解为对外发布的HTTP接口或者Thrift接口,也可以是接收消息的内部接口,甚至是一个内部方法或操作。

二、应用场景

支付业务

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

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

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

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 ,不会调用银行接口,实现了幂等性。

你可能感兴趣的:(幂等性实现方案)