防重复请求处理的实践与总结

 

#防重复处理总结
##背景
在业务开发中,我们常会面对防止重复请求的问题。当服务端对于请求的响应涉及数据的修改,或状态的变更时,可能会造成极大的危害。重复请求的后果在交易系统、售后维权,以及支付系统中尤其严重。

前台操作的抖动,快速操作,网络通信或者后端响应慢,都会增加后端重复处理的概率。

前台操作去抖动和防快速操作的措施,我们首先会想到在前端做一层控制。当前端触发操作时,或弹出确认界面,或disable入口并倒计时等等,此处不细表。

但前端的限制仅能解决少部分问题,且不够彻底,后端自有的防重复处理措施必不可少,义不容辞。

在接口实现中,我们常要求接口要满足幂等性,来保证多次重复请求时只有一次有效。

查询类的接口几乎总是幂等的,但在包含诸如数据插入,多模块数据更新时,达到幂等性会比较难,尤其是高并发时的幂等性要求。比如第三方支付前台回调和后台回调,第三方支付批量回调,慢性能业务逻辑(如用户提交退款申请,商家同意退货/退款等)或慢网络环境时,是重复处理的高发场景。

##尝试

这里针对“用户提交退款申请”的例子,说明一下尝试过的防重复处理方法的效果。

后端防重复处理的方式,我们先后尝试了三种:

####1)基于DB中退款订单状态的验证

这种方式简单直观,从DB查询出来的退款详情(包括状态)往往还可以用在后续逻辑中,没有花额外的工作专门应对重复请求的问题。

这种查询状态后进行验证的逻辑,从代码上线后就一直存在于所有含状态的业务逻辑处理中,必不可少。但对于防重复处理效果并不好:在前端添加防重复提交前,每周平均在25笔;前端优化后,每周降到7笔。这个数量占总退款申请数的3%%,一个仍然无法接受的比例。

理论上,任意次请求只要在数据状态更新之前都完成了查询操作,则业务逻辑的重复处理就会发生。如下图所示。优化的方向是减少查询到更新之间业务处理时间,可降低空档期的并发影响。极致情况下如果查询和更新变成了原子操作,则就不存在我们当前的问题。



####2)基于缓存数据状态的验证
Redis存储查询轻量快速。在request进来的时候,可以先记录在缓存中。后续进来的request每次进行验证。整个流程处理完成,清除缓存。以退款为例子:

    I.  每次退款发起申请,读取缓存中是否有以orderId为key的值
    II. 没有,则往缓存中写入以orderId为key的value
    III.有,则说明有该订单的退款正在进行。
    IV. 操作完清缓存,或者缓存存值的时候设置生命周期

与1)的发放相比,数据库换成响应更快的缓存。但是仍然不是原子操作。插入和读取缓存还是有时间间隔。在极致的情况下还是存在重复操作的情况。
此方法优化后,每周1笔重复操作。



####3)利用唯一索引机制的验证

需要原子性操作,想到了数据库的唯一索引。
新建一个TradeLock表:


CREATE TABLE `TradeLock` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`type` int(11) NOT NULL COMMENT '锁类型',
`lockId` int(11) NOT NULL DEFAULT '0' COMMENT '业务ID',
`status` int(11) NOT NULL DEFAULT '0' COMMENT '锁状态',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='Trade锁机制';

每次request进来则往表里面插入数据:

——成功,则可以继续操作(相当于获取锁);
——失败,则说明有操作在进行。

操作完成后,删除此条记录。(相当于释放锁)
目前已经上线,等待下周的数据统计。



####4)基于缓存的计数器验证:

由于数据库的操作比较消耗性能,了解到redis的计数器也是原子性操作。果断采用计数器。既可以提高性能,还不用存储,而且能提升qps的峰值。

还是以订单退款为例子:

每次request进来则新建一个以orderId为key的计数器,然后+1。

如果>1(不能获得锁): 说明有操作在进行,删除。

如果=1(获得锁): 可以操作。

操作结束(删除锁):删除这个计数器。
要了解计数器,可以参考:

link



##总结:

PHP语言自身没有提供进程互斥和锁定机制。因此才有了我们上面的尝试。

网上也有文件锁机制,但是考虑到我们的分布式部署,建议还是用缓存。

在大并发的情况下,程序各种情况的发生。特别是涉及到金额操作,不能有一分一毫的差距。所以在大并发要互斥的情况下可以考虑3、4两种方案。

爱迪生尝试了1600多种材料选择了钨丝发明了灯泡,实践出真知。遇到问题,和问题斗争,最后解决问题是一个最大提升自我的过程,不但加宽自己的知识广度,更加深了自己的技能深度。达到目标之后的成就感更是不言而喻。

    • 作者:蘑菇街商家平台资金团队工程师 @木照

http://mogu.io/prevent-duplicate-requests-4

 

时间: 2024-10-25 12:54:00

防重复请求处理的实践与总结的相关文章

页面防重复提交方法总结

有时候我们的程序执行比较慢,而且我们页面也不怎么友好,没什么提示信息.操作人员以为没有点击提交按钮,就会再一次点击提交.这会导致很多问题出现.下面介绍三种防重复提交的方法.   1.提交按钮置disabled         当用户提交后,立即把按钮置为不可用状态.这种用js来实现.            提交前           $("#submit").attr('disabled','true');          $("#submit").val(&qu

Android ListView最佳处理方式,ListView拖动防重复数据显示,单击响应子控件

Android ListView最佳处理方式,ListView拖动防重复数据显示,单击响应子控件. 1.为了防止拖动ListView时,在列表末尾重复数据显示.需要加入 HashMap<Integer,View> lmap = new HashMap<Integer,View>();其中Integer为列表位置,View为子项视图,加入数据前首先if (lmap.get(position)==null) ,满足条件时,加入lmap.put(position, convertView

javascript 事件统计,防重复统计的例子

事件统计防重复统计,不服来挑错 function track(eventName, params) {   var img = new Image();   if(typeof params == 'object') {     params = JSON.stringify(params);   } else {     params = '';   }   var t = new Date().valueOf();   var random = Math.random();   var re

防直接访问 防重复提交

防止直接访问PHP页面 1.可以在A加COOKIE,B判断COOKIE后用完删掉COOKIE 2. 使用$_SERVER['HTTP_REFERER'] 得到链接到当前页面的前一页面的地址 3.token令牌 Java代码   <?php   /*  * PHP简单利用token防止表单重复提交  * 此处理方法纯粹是为了给初学者参考  */   session_start();   function set_token()   {       $_SESSION['token'] = md5(

asp.net表单提交时防重复提交并执行前台的JS验证_实用技巧

在项目开发中,遇到这样的一个情况,就是用户重复提交.当然这个不能怪用户,只能怪.NET或者服务器反应迟钝......我是这样理解的. 在网上搜了一下,解决方案是不少,比如: http://bbs.csdn.net/topics/340048988 (这个大家提了不少建议) http://www.cnblogs.com/blsong/archive/2009/12/24/1631144.html (这个基本上总结了网上的方法) 但实际上做互联网web项目中,需要在前台执行JS或者Jquery的验证

Asp.net防重复提交机制实现方法_实用技巧

为Button或其他控件加上下面两个属性即可 1.UseSubmitBehavior="false"使用服务器端提交机制,即执行OnClick事件. 2.OnClientClick客户端点击按钮后,设置控件为不可用,控件文本显示处理中-,待服务器端执行完OnClick事件,控件自动恢复为可用. 代码如下: 复制代码 代码如下: UseSubmitBehavior="false" OnClientClick="this.disabled=true;this.

php页面防重复提交方法总结_php技巧

1.提交按钮置disabled       当用户提交后,立即把按钮置为不可用状态.这种用js来实现.          提交前 复制代码 代码如下:         $("#submit").attr('disabled','true');         $("#submit").val("正在提交,请稍等");          ........................................................

JSP学习之------&amp;gt;客户端防表单重复提交和服务器端session防表单重复提交

1.什么叫表单重复提交:      所谓表单重复提交,是指用户通过多次点击提交按钮或多次刷新表单提交页面等造成用户表单重复提交的现象 2.表单重复提交有哪些情况:      (1)用户在程序提交表单的时间段里多次提交表单      (2)重复刷新提交后的表单      (3)用户点击浏览器回退按钮,然后再次提交 3.如果解决表单重复提交:     (1)方法1:客户端防表单重复提交:  一般通过js代码防止第一种情况的发生,对于第二种和第三种的情况很难避免,并且稍微有经验的用户可以通过去掉页面j

解析php防止form重复提交的方法

本篇文章是对php防止form重复提交的方法进行了详细的分析介绍,需要的朋友参考下   php 防止表单重复提交实例: 复制代码 代码如下: <?php session_start(); $_SESSION['fsess']=($_SESSION['fsess'])?$_SESSION['fsess']:time(); ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "htt