摘要:連接信息可以用原生也可以用其它的框架集成鎖定默認過期時間避免死鎖新版已經集成了大多數集成操作解鎖業務相關的可以是庫存物品數等用戶相關的下單下單個數用戶場次是為了方便異常處理方便數據查找商品場次此方法不具備原子
ip = $config["ip"]; } if(isset($config["port"])){ $this->ip = $config["port"]; } } /** * Redis連接信息可以用原生,也可以用其它的框架集成 */ $this->_redis = new Redis(); $this->_redis->connect($this->ip,$this->port); } /** 鎖定 * @param int $intTimeout 默認過期時間(避免死鎖) * @return bool */ private function lock($intTimeout = 8) { #新版set,已經集成了大多數集成操作 $strRet = $this->_redis->set($this->_lockKey, time().rand(10000,99999).rand(1000,9999).rand(100,999), "ex", $intTimeout, "nx"); if($strRet) { return true; }else{ return false; } } /** 解鎖 * @throws Exception */ private function unlock() { $strRet = $this->_redis->del($this->_lockKey); if($strRet) { return true; }else{ if($this->_redis->get($this->_lockKey)) { return false ; }else{ return false ; } } } /** * 業務相關的key,可以是庫存,物品數等 */ const ORDER_KEY = "order_num"; /** * 用戶相關的key */ const USER_KEY = "user_num"; /** Redis下單 * @param int $num 下單個數 * @param string $userId 用戶ID * * 場次是為了方便異常處理,方便數據查找 * @param string $bout 商品場次 => order_num:1 , order_num:2 * @return bool * @throws Exception */ public function order( string $userId ,string $bout = "1" ,int $num = 1) { $orderKey = self::ORDER_KEY.":".$bout ; $userKey = self::USER_KEY.":".$bout ; //此方法不具備原子性 并發處理是不能做條件判斷 //$len = $this->_redis->llen(); #實際為n+1次觸發完結,這里只做Redis自減 $check = $this->_redis->lpop($orderKey); if(!$check){ #當前order_num已經為0! //自動補貨為 100 ,$bout有一定的處理規則,不能亂傳 self::autoBuild(100,$bout); return false ; } //特殊處理,避免n+1次的情況 $len = $this->_redis->llen($orderKey) ; if($len == 0) { //自動補貨為 100 ,$bout有一定的處理規則,不能亂傳 self::autoBuild(100,$bout); return false ; } //添加用戶數據 $result = $this->_redis->lpush($userKey,$userId); if($result){ return true ; }else{ return false ; } } /** 失敗處理 * #增加當前庫存 * #減少用戶庫存 * @param string $num * @param string $userId * @param $bout * @return bool * @throws Exception */ public function _out(string $num,string $userId,$bout) { #并發參與時,總庫存有5個,一共10次請求,成功5次,退款1次,實際庫存1次 #失敗處理時和_buildOrder加上同一把鎖,避免更新下次庫存時,上次庫存累積 #_out 和 _buildOrder 同時只能有一個在執行,不然鎖會報錯,也避免下不必要的死鎖 self::lock(); //減用戶庫存 $user = $this->_redis->lpop(self::USER_KEY.":".$bout); if(!$user) { return false ; } //增加商品庫存 $all = $this->_redis->lpush(self::ORDER_KEY.":".$bout,$userId); if(!$all) { //TODO::這里需要做容錯處理,即再商品庫存增加失敗時,做記錄 return false ; } self::unlock(); } /** 自動構建 * @param int $num * @param $bout * @throws Exception */ private function autoBuild( int $num ,$bout) { $a = $this->_redis->get(self::ORDER_KEY.":".$bout); if(!$a) { //庫存已完結 $this->_buildOrder(self::ORDER_KEY.":".$bout,$num); } } /** 物品庫存規則 * @param $orderKey * @param $num * @return string * @throws Exception */ private function _buildOrder($orderKey,$num) { //鎖定 self::lock(); $ckNum = "0" ;#Redis操作后返回為string類型 #總數 與$ckNum要相同類型 不然可能會出現判斷錯誤 if($num < 0) { throw new Exception("商品數量錯誤!"); } $beforeNum = 0 ; //上一次庫存判斷 () if($beforeNum > 0) { throw new Exception("商品未售罄!"); } //當前庫存判斷 $length = $this->_redis->llen($orderKey); if($length > 0) { throw new Exception("商品已經存在!"); } //生成當前庫存 while ($ckNum < $num) { if($ckNum == $num) { break ; }else if($ckNum > $num){ break ; }else{ $ckNum = $this->_redis->lpush($orderKey,1) ; if($ckNum >=$num) { break ; } } } //并發時 循環成功 redis不一定成功 /*for ($i=1;$i<=$num ;$i++) { $ckNum = $this->_redis->lpush(self::$_allCoin.self::getNum().":".$coin,1); if($ckNum >= $num) { break ; } }*/ //解鎖 self::unlock(); return $ckNum ; } }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/30998.html
摘要:一為什么難秒殺系統難做的原因庫存只有一份,所有人會在集中的時間讀和寫這些數據。又例如搶票,亦與秒殺類似,瞬時流量更甚。 一、為什么難 ????秒殺系統難做的原因:庫存只有一份,所有人會在集中的時間讀和寫這些數據。例如小米手機每周二的秒殺,可能手機只有1萬部,但瞬時進入的流量可能是幾百幾千萬。又例如12306搶票,亦與秒殺類似,瞬時流量更甚。 主要需要解決的問題有兩個: 高并發對數據庫...
摘要:依賴注入傳統的思路應用程序用到一個類就會創建類并調用類的方法。這樣你可以完全控制依賴關系,通過調整不同的注入對象,來控制程序的行為。例如類用到了,可以在不修改類代碼的情況下,改用。 依賴注入 傳統的思路 應用程序用到一個Foo類,就會創建Foo類并調用Foo類的方法。 假如這個方法內需要一個Bar類,就會創建Bar類并調用Bar類的方法。 而這個方法內需要一個Bim類,就會創建Bim...
摘要:在秒殺,搶購等并發場景下,可能會出現超賣的現象,在語言中并沒有原生提供并發的解決方案,因此就需要借助其他方式來實現并發控制。借助文件排他鎖,在處理下單請求的時候,用鎖定一個文件,成功拿到鎖的才能處理訂單。 在秒殺,搶購等并發場景下,可能會出現超賣的現象,在PHP語言中并沒有原生提供并發的解決方案,因此就需要借助其他方式來實現并發控制。 列出常見的解決方案有: 使用隊列,額外起一個進程...
閱讀 1010·2021-11-22 13:52
閱讀 924·2019-08-30 15:44
閱讀 570·2019-08-30 15:43
閱讀 2424·2019-08-30 12:52
閱讀 3473·2019-08-29 16:16
閱讀 637·2019-08-29 13:05
閱讀 2943·2019-08-26 18:36
閱讀 1975·2019-08-26 13:46