<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      php在大并發下redis鎖實現

        在現如今電商盛行的時期,會出現很多促銷活動,最為常見的就是秒殺。在秒殺系統中最為常見的問題就是會出現超賣的情況,那么如何來杜絕超賣的情形了,在業務邏輯層面可以使用緩存以及加鎖的手法來避免超賣的情形。

        現如今nosql已經非常流行和穩定了,在此我將通過redis和php來說明如何實現鎖機制。當然我使用redis加鎖并不是我的秒殺系統,而是最近做的一個項目有個用戶提現,初期沒有考慮到會有人惡意刷新接口,而導致用戶無限制提現。經過查看nginx日志,發現用戶在同一時間段,通過刷接口的方法超額提現,導致虧損

        起初的提現代碼如下:

      $uid = $this->user_id;
              if (empty($uid)) {
                  return $this->responseJson(300, '請先登錄');
              }
              $money = $this->request->get('money', 'trim');
              $formId = $this->request->get('formId', 'trim', '');
              $user_model = new User();
              $user_info = $user_model->getUserInfoById($uid);
              $balance = $user_info['balance'] / 100;
              $phone = $this->request->get('phone', 'trim');
              if ($money < 1) {
                  return $this->responseJson(300, '最小提現金額為1元');
              }
              if ($balance < $money) {
                  return $this->responseJson(300, '賬戶余額不足');
              }
              $openid = $user_info['openId'];
              if (in_array($openid, ['oMl_x0DiSCYqQuqJOKV9bAqR1Ugk', 'oMl_x0B1zoY70dbiwxAt4lg2fmL4', 'oMl_x0E0jYOK6NpbzwmTVJowpfpk', 'oMl_x0GHeAdKCZ8Iv1KD0CmdZLQ0', 'oMl_x0FBU1eWya1fG5xtVxryUYG4', 'oMl_x0CIMr5tItEy1QPtpI9eFJak', 'oMl_x0JWFdGOnf80W5oZOX-XfGcw'])) {
                  return $this->responseJson(300, '正在處理中');
              }
              if (!empty($phone)) {
                  if (!isset($user_info['phone']) || (isset($user_info['phone']) && $user_info['phone'] != $phone)) {
                      $user_model->update(['_id' => $this->user_id], ['$set' => ['phone' => $phone]]);
                  }
              }
              $order_id = \Common::getOrder();
              $res = $user_model->updateBalanceById($uid, $money * 100);
              if ($res) {
                  $trans_res = \GlobalFunc::transfer($uid, $order_id, $openid, 'NO_CHECK', $money);
      
                  if ($trans_res === false) {
                      $user_model->updateBalanceById($uid, $money * 100, 2);
                      $redis->del('with:draw:' . $uid);
                      if (!empty($formId)) {
                          $TemplateMsg = new \TemplateMsg();
                          $to_user = $user_info['openId'];
                          $tem_id = 'ZSpYvjqdawADxr7j_8DJFuaoAdWbHhXdnAFlp5QF9L0';
                          $data = array(
                              'keyword1' => array('value' => '提現', 'color' => "#173177"),
                              'keyword2' => array('value' => date('Y-m-d H:i:s'), 'color' => "#173177"),
                              'keyword3' => array('value' => $trans_res['err_code_des'] . "。申請提現金額已自動退回賬戶余額中。", 'color' => '#173177'),
                          );
                          $page = 'pages/balance/balance';
                          $TemplateMsg->doSend($to_user, $tem_id, $formId, $data, $page);
                      }
                      return $this->responseJson(300, $trans_res['err_code_des']);
                  }
                  $redis->del('with:draw:' . $uid);
              } else {
                  $redis->del('with:draw:' . $uid);
                  return $this->responseJson(300, '提現失敗,請稍后重試');
              }
      ....

      看上面代碼邏輯感覺似乎沒有什么問題,確實,在正常的情況下是不會出現問題,如果有人惡意的去刷接口的話,上述問題就出現了。為了防止用戶惡意刷接口,所以對現有代碼做了如下修改

      $uid = $this->user_id;
              if (empty($uid)) {
                  return $this->responseJson(300, '請先登錄');
              }
              $money = $this->request->get('money', 'trim');
              $formId = $this->request->get('formId', 'trim', '');
              $user_model = new User();
              $user_info = $user_model->getUserInfoById($uid);
              $balance = $user_info['balance'] / 100;
              $phone = $this->request->get('phone', 'trim');
              if ($money < 1) {
                  return $this->responseJson(300, '最小提現金額為1元');
              }
              if ($balance < $money) {
                  return $this->responseJson(300, '賬戶余額不足');
              }
              $redis = $this->cache('redis');
              $redis->incr('with:draw:' . $uid);
              if (intval($redis->get('with:draw:' . $uid)) > 1) {
                  return $this->responseJson(300, '正在處理中');
              }
              $openid = $user_info['openId'];
              if (in_array($openid, ['oMl_x0DiSCYqQuqJOKV9bAqR1Ugk', 'oMl_x0B1zoY70dbiwxAt4lg2fmL4', 'oMl_x0E0jYOK6NpbzwmTVJowpfpk', 'oMl_x0GHeAdKCZ8Iv1KD0CmdZLQ0', 'oMl_x0FBU1eWya1fG5xtVxryUYG4', 'oMl_x0CIMr5tItEy1QPtpI9eFJak', 'oMl_x0JWFdGOnf80W5oZOX-XfGcw'])) {
                  return $this->responseJson(300, '正在處理中');
              }
              if (!empty($phone)) {
                  if (!isset($user_info['phone']) || (isset($user_info['phone']) && $user_info['phone'] != $phone)) {
                      $user_model->update(['_id' => $this->user_id], ['$set' => ['phone' => $phone]]);
                  }
              }
              $order_id = \Common::getOrder();
              $res = $user_model->updateBalanceById($uid, $money * 100);
              if ($res) {
                  $trans_res = \GlobalFunc::transfer($uid, $order_id, $openid, 'NO_CHECK', $money);
      
                  if ($trans_res === false) {
                      $user_model->updateBalanceById($uid, $money * 100, 2);
                      $redis->del('with:draw:' . $uid);
                      if (!empty($formId)) {
                          $TemplateMsg = new \TemplateMsg();
                          $to_user = $user_info['openId'];
                          $tem_id = 'ZSpYvjqdawADxr7j_8DJFuaoAdWbHhXdnAFlp5QF9L0';
                          $data = array(
                              'keyword1' => array('value' => '提現', 'color' => "#173177"),
                              'keyword2' => array('value' => date('Y-m-d H:i:s'), 'color' => "#173177"),
                              'keyword3' => array('value' => $trans_res['err_code_des'] . "。申請提現金額已自動退回賬戶余額中。", 'color' => '#173177'),
                          );
                          $page = 'pages/balance/balance';
                          $TemplateMsg->doSend($to_user, $tem_id, $formId, $data, $page);
                      }
                      return $this->responseJson(300, $trans_res['err_code_des']);
                  }
                  $redis->del('with:draw:' . $uid);
              } else {
                  $redis->del('with:draw:' . $uid);
                  return $this->responseJson(300, '提現失敗,請稍后重試');
              }
      ...

        代碼調整后似乎防止了用戶刷接口的行為,但是后期有用戶反映,自己提不了現了。經過一番查看,原來redis的值一直存在,雖然用戶操作完成后會刪除key,但是也會存在在用戶沒有完全操作完成而導致流程中斷,所以會導致key刪除失敗,為了解決鎖不釋放的問題,又對上述代碼進行修改,在設置鎖的時候,設置一個過期時間,修復如下

      if (intval($redis->get('with:draw:' . $uid)) > 1) {
                  if ($redis->ttl('with:draw:' . $uid) == -1) {
                      $redis->expire('with:draw:' . $uid, 30);
                  }
                  return $this->responseJson(300, '正在處理中');
              }

        這樣就可以實現鎖不釋放的問題,但是上述代碼除了使用incr操作外,還可以使用redis的setnx來代替,其實是一樣的效果,但是無論你用那種還是有點問題就是,當你寫入成功之后,突然斷網或服務器宕機的情況,這時還會出現上述問題,那應該如何來解決呢。其實完全可以通過redis的 Multi/Exec結合來解決上述問題,其代碼如下

      $redis->multi();
      $redis->setNX($key, $value);
      $redis->expire($key, $ttl);
      $redis->exec();

        這樣就可以解決突然情況帶來的妖怪問題了

        總結:通過redis可以實現大并發的數據請求操作,通過事務的操作來加鎖和釋放鎖,達到數據完整性

       

        雖然上述問題解決了,但是代碼還是有待優化,從 2.6.12 起,SET 涵蓋了 SETEX 的功能,并且 SET 本身已經包含了設置過期時間的功能,也就是說,我們前面需要的功能只用 SET 就可以實現。所以上述代碼可以簡化為

        

      $redis->set($key, $random, array('nx', 'ex' => $ttl));

      以上就是如何解決并發和惡意刷接口的解決方法,以作記錄

      posted on 2017-11-16 17:41  鐵猛  閱讀(1357)  評論(0)    收藏  舉報

      導航

      主站蜘蛛池模板: 欧美高清一区三区在线专区 | 久久99精品中文字幕在| 独山县| 河北真实伦对白精彩脏话| 久久99国产精品尤物| 沙坪坝区| 99久久久无码国产麻豆| 中文字幕国产精品综合| 晋江市| 久久精品av国产一区二区| 国产高清午夜人成在线观看,| 綦江县| 武装少女在线观看高清完整版免费| CAOPORN免费视频国产| 2019国产精品青青草原| 国产乱久久亚洲国产精品| 亚洲亚洲人成综合网络 | 噜噜综合亚洲av中文无码| 亚洲国产成人无码电影| 叶城县| 中国少妇人妻xxxxx| 午夜爽爽爽男女污污污网站| 色欲狠狠躁天天躁无码中文字幕| 热久久这里只有精品国产| 日韩伦人妻无码| 久久综合久中文字幕青草| 国产精品推荐视频一区二区| 隔壁老王国产在线精品| 亚洲人成人一区二区三区| 人妻系列无码专区免费 | 无遮挡aaaaa大片免费看| 久久亚洲中文字幕伊人久久大| 影音先锋啪啪av资源网站| 国产综合av一区二区三区 | 亚洲男女羞羞无遮挡久久丫| 国内自拍视频一区二区三区| 免费视频爱爱太爽了| 成人午夜视频在线| 久久婷婷国产精品香蕉| 大屁股国产白浆一二区| 97欧美精品系列一区二区|