防止重复提交策略

论坛 期权论坛 脚本     
匿名技术用户   2020-12-30 07:49   65   0

前言

业务开发中,常常面临防止重复提交问题,当该情况发生往往会带来验证后果。前端操作抖动、快速操作、网络延迟以及后台处理慢等等都会增加后端重复处理的概率;

方案

  1. 前端提交之后,屏蔽提交按钮。该方案虽然可以启动一定作用,对于模拟接口请求就没有用。
  2. 提交表单跳转其他页面。该方案在极致情况下也是不安全的。
  3. 利用Session防止表单重复提交。客户端请求一个页面,服务端生成一个token(令牌)存在session中,并且把token放在页面中一起发给客户端,客户端提交请求时,服务端先验证token合法性,如果通过从session删除该值,继续处理业务;如果验证不通过返回错误,理论上接口并发请求还是可能会出现重复提交。
  4. 利用数据库字段设置唯一索引。可以有效避免表单重复提交,但增大服务器和数据库开销。
  5. 利用Redis加锁和删除锁。Redis中设置一个单据锁,利用set操作的原子性,只有获取该锁(未提交表单)才能提交表单,未获取(已提交表单)则直接返回客户端;这里单据锁的值过期时间一定要大于中间逻辑执行时间,不然就会出现重复提交。
    <?php
    // 加载redis组件
    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);
    // 防止重复变量名称
    $redis_name = 'lock';
    
    // 模拟用户ID
    $uid = 1;
    $ok = $redis->rawCommand('set', $redis_name . '_' . $uid, $uid, 'EX', 5, 'NX');
    if (!$ok) {
        // 未获取到锁,表示已提交
        // 其他处理逻辑...
        write_log('test.log', ['status' => 0, 'msg' => '订单已提交,请勿重复提交', 'data' => ['uid' => $uid, 'ok' => $ok]]);
        return false;
    }
    // 获取到锁,处理订单逻辑...
    sleep(3);
    // 其他逻辑处理完成,删除锁
    $redis->del($redis_name . '_' . $uid);
    
    write_log('test.log', ['status' => 1, 'msg' => '订单处理完成', 'data' => ['uid' => $uid, 'ok' => $ok]]);
    $redis->close();
    
    function write_log($filepath, $data) {
        $dir_name = dirname($filepath);
        if (!file_exists($dir_name)) { // 目录不存在就创建
            // iconv防止中文名乱码
            mkdir(iconv("UTF-8", "GBK", $dir_name), 0777, true);
        }
        $fp = fopen($filepath, "a+");// 打开文件资源通道 不存在则自动创建
        fwrite($fp, date("Y-m-d H:i:s") . ":" . json_encode($data, JSON_UNESCAPED_UNICODE) . "\r\n");//写入文件
        fclose($fp);// 关闭资源通道
    }
    
    ---------------------------------------------------------------------------------------
    模拟并发提交
    /usr/local/wrk/wrk -t4 -c100 -d1s http://127.0.0.1:1010/queue_redis/avoid_duplicate.php
    Running 1s test @ http://queue.babytime.vip/queue_redis/avoid_duplicate.php
      4 threads and 100 connections
      Thread Stats   Avg      Stdev     Max   +/- Stdev
        Latency   131.30ms  136.14ms 663.84ms   89.58%
        Req/Sec   168.42    146.36   500.00     84.62%
      534 requests in 1.03s, 99.60KB read
    Requests/sec:    518.12
    Transfer/sec:     96.64KB
    
    查看日志记录
    [root@Lewis queue_redis]# tail -f test.log 
    2020-01-07 19:12:19:{"status":0,"msg":"订单已提交,请勿重复提交","data":{"uid":1,"ok":false}}
    2020-01-07 19:12:19:{"status":0,"msg":"订单已提交,请勿重复提交","data":{"uid":1,"ok":false}}
    2020-01-07 19:12:19:{"status":0,"msg":"订单已提交,请勿重复提交","data":{"uid":1,"ok":false}}
    2020-01-07 19:12:19:{"status":0,"msg":"订单已提交,请勿重复提交","data":{"uid":1,"ok":false}}
    2020-01-07 19:12:19:{"status":0,"msg":"订单已提交,请勿重复提交","data":{"uid":1,"ok":false}}
    2020-01-07 19:12:19:{"status":0,"msg":"订单已提交,请勿重复提交","data":{"uid":1,"ok":false}}
    2020-01-07 19:12:19:{"status":0,"msg":"订单已提交,请勿重复提交","data":{"uid":1,"ok":false}}
    2020-01-07 19:12:19:{"status":0,"msg":"订单已提交,请勿重复提交","data":{"uid":1,"ok":false}}
    2020-01-07 19:12:19:{"status":0,"msg":"订单已提交,请勿重复提交","data":{"uid":1,"ok":false}}
    2020-01-07 19:12:21:{"status":1,"msg":"订单处理完成","data":{"uid":1,"ok":true}}
    
    
    
    

总结

以上方案都有其优缺点,根据自己项目实际情况调整和搭配使用。

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP