摘要:連接信息可以用原生也可以用其它的框架集成鎖定默認(rèn)過期時(shí)間避免死鎖新版已經(jīng)集成了大多數(shù)集成操作解鎖業(yè)務(wù)相關(guān)的可以是庫存物品數(shù)等用戶相關(guān)的下單下單個(gè)數(shù)用戶場(chǎng)次是為了方便異常處理方便數(shù)據(jù)查找商品場(chǎng)次此方法不具備原子
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 默認(rèn)過期時(shí)間(避免死鎖) * @return bool */ private function lock($intTimeout = 8) { #新版set,已經(jīng)集成了大多數(shù)集成操作 $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 ; } } } /** * 業(yè)務(wù)相關(guān)的key,可以是庫存,物品數(shù)等 */ const ORDER_KEY = "order_num"; /** * 用戶相關(guān)的key */ const USER_KEY = "user_num"; /** Redis下單 * @param int $num 下單個(gè)數(shù) * @param string $userId 用戶ID * * 場(chǎng)次是為了方便異常處理,方便數(shù)據(jù)查找 * @param string $bout 商品場(chǎng)次 => 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 ; //此方法不具備原子性 并發(fā)處理是不能做條件判斷 //$len = $this->_redis->llen(); #實(shí)際為n+1次觸發(fā)完結(jié),這里只做Redis自減 $check = $this->_redis->lpop($orderKey); if(!$check){ #當(dāng)前order_num已經(jīng)為0! //自動(dòng)補(bǔ)貨為 100 ,$bout有一定的處理規(guī)則,不能亂傳 self::autoBuild(100,$bout); return false ; } //特殊處理,避免n+1次的情況 $len = $this->_redis->llen($orderKey) ; if($len == 0) { //自動(dòng)補(bǔ)貨為 100 ,$bout有一定的處理規(guī)則,不能亂傳 self::autoBuild(100,$bout); return false ; } //添加用戶數(shù)據(jù) $result = $this->_redis->lpush($userKey,$userId); if($result){ return true ; }else{ return false ; } } /** 失敗處理 * #增加當(dāng)前庫存 * #減少用戶庫存 * @param string $num * @param string $userId * @param $bout * @return bool * @throws Exception */ public function _out(string $num,string $userId,$bout) { #并發(fā)參與時(shí),總庫存有5個(gè),一共10次請(qǐng)求,成功5次,退款1次,實(shí)際庫存1次 #失敗處理時(shí)和_buildOrder加上同一把鎖,避免更新下次庫存時(shí),上次庫存累積 #_out 和 _buildOrder 同時(shí)只能有一個(gè)在執(zhí)行,不然鎖會(huì)報(bào)錯(cuò),也避免下不必要的死鎖 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::這里需要做容錯(cuò)處理,即再商品庫存增加失敗時(shí),做記錄 return false ; } self::unlock(); } /** 自動(dòng)構(gòu)建 * @param int $num * @param $bout * @throws Exception */ private function autoBuild( int $num ,$bout) { $a = $this->_redis->get(self::ORDER_KEY.":".$bout); if(!$a) { //庫存已完結(jié) $this->_buildOrder(self::ORDER_KEY.":".$bout,$num); } } /** 物品庫存規(guī)則 * @param $orderKey * @param $num * @return string * @throws Exception */ private function _buildOrder($orderKey,$num) { //鎖定 self::lock(); $ckNum = "0" ;#Redis操作后返回為string類型 #總數(shù) 與$ckNum要相同類型 不然可能會(huì)出現(xiàn)判斷錯(cuò)誤 if($num < 0) { throw new Exception("商品數(shù)量錯(cuò)誤!"); } $beforeNum = 0 ; //上一次庫存判斷 () if($beforeNum > 0) { throw new Exception("商品未售罄!"); } //當(dāng)前庫存判斷 $length = $this->_redis->llen($orderKey); if($length > 0) { throw new Exception("商品已經(jīng)存在!"); } //生成當(dāng)前庫存 while ($ckNum < $num) { if($ckNum == $num) { break ; }else if($ckNum > $num){ break ; }else{ $ckNum = $this->_redis->lpush($orderKey,1) ; if($ckNum >=$num) { break ; } } } //并發(fā)時(shí) 循環(huán)成功 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 ; } }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/30998.html
摘要:一為什么難秒殺系統(tǒng)難做的原因庫存只有一份,所有人會(huì)在集中的時(shí)間讀和寫這些數(shù)據(jù)。又例如搶票,亦與秒殺類似,瞬時(shí)流量更甚。 一、為什么難 ????秒殺系統(tǒng)難做的原因:庫存只有一份,所有人會(huì)在集中的時(shí)間讀和寫這些數(shù)據(jù)。例如小米手機(jī)每周二的秒殺,可能手機(jī)只有1萬部,但瞬時(shí)進(jìn)入的流量可能是幾百幾千萬。又例如12306搶票,亦與秒殺類似,瞬時(shí)流量更甚。 主要需要解決的問題有兩個(gè): 高并發(fā)對(duì)數(shù)據(jù)庫...
摘要:依賴注入傳統(tǒng)的思路應(yīng)用程序用到一個(gè)類就會(huì)創(chuàng)建類并調(diào)用類的方法。這樣你可以完全控制依賴關(guān)系,通過調(diào)整不同的注入對(duì)象,來控制程序的行為。例如類用到了,可以在不修改類代碼的情況下,改用。 依賴注入 傳統(tǒng)的思路 應(yīng)用程序用到一個(gè)Foo類,就會(huì)創(chuàng)建Foo類并調(diào)用Foo類的方法。 假如這個(gè)方法內(nèi)需要一個(gè)Bar類,就會(huì)創(chuàng)建Bar類并調(diào)用Bar類的方法。 而這個(gè)方法內(nèi)需要一個(gè)Bim類,就會(huì)創(chuàng)建Bim...
摘要:質(zhì)量高在設(shè)計(jì)時(shí),可重用現(xiàn)有的,在以前的項(xiàng)目的領(lǐng)域中已被測(cè)試過的類使系統(tǒng)滿足業(yè)務(wù)需求并具有較高的質(zhì)量。代碼塊捕獲異常,并創(chuàng)建一個(gè)包含異常信息的對(duì)象。這樣可以解決超賣的問題,但是會(huì)導(dǎo)致文件得開銷很大。 6.你們公司是使用什么框架? 答:我們公司采用的是TP框架,運(yùn)用的mysql+apache+php進(jìn)行開發(fā),因?yàn)門P框架是一個(gè)免費(fèi)開源的,輕量級(jí)的php開發(fā)框架,而且是我們中國人自己開發(fā)的,...
摘要:在秒殺,搶購等并發(fā)場(chǎng)景下,可能會(huì)出現(xiàn)超賣的現(xiàn)象,在語言中并沒有原生提供并發(fā)的解決方案,因此就需要借助其他方式來實(shí)現(xiàn)并發(fā)控制。借助文件排他鎖,在處理下單請(qǐng)求的時(shí)候,用鎖定一個(gè)文件,成功拿到鎖的才能處理訂單。 在秒殺,搶購等并發(fā)場(chǎng)景下,可能會(huì)出現(xiàn)超賣的現(xiàn)象,在PHP語言中并沒有原生提供并發(fā)的解決方案,因此就需要借助其他方式來實(shí)現(xiàn)并發(fā)控制。 列出常見的解決方案有: 使用隊(duì)列,額外起一個(gè)進(jìn)程...
閱讀 1093·2021-11-22 13:52
閱讀 1004·2019-08-30 15:44
閱讀 621·2019-08-30 15:43
閱讀 2488·2019-08-30 12:52
閱讀 3533·2019-08-29 16:16
閱讀 711·2019-08-29 13:05
閱讀 2999·2019-08-26 18:36
閱讀 2077·2019-08-26 13:46