這篇文章主要介紹了關於laravel+Redis簡單實現隊列通過壓力測試的高並發處理 ,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下
秒殺活動
在一般的網路商城中我們會經常接觸到一些高並發的業務狀況,例如我們常見的秒殺搶購等活動,
在這些業務中我們經常需要處理一些關於請求資訊過濾以及商品庫存的問題。
在請求中比較常見的狀況是同一使用者發出多次請求或者包含惡意的攻擊,以及一些訂單的複購等情況。
而在庫存方面則需要考慮超賣這種狀況。
下面我們來類比一個簡單可用的並發處理。
直接上代碼
代碼的流程
1.類比使用者請求,將使用者寫入redis隊列中
2.從使用者中取出一個請求資訊進行處理(可以在這個步驟做更多的處理,請求過濾,訂單複購等)
3.使用者下單(支付等),減少庫存。下面使用了兩種方式進行了處理,一種使用了Redis中單線程原子操作的特性使程式一直線性操作從而保持了資料的一致。
另外一種是用了事務進行操作,可以根據業務進行調整,這裡就不一一描述了。
實際的業務狀況更為複雜,但更多的是出於對基礎思路的拓展。
<?phpnamespace App\Http\Controllers\SecKill;use App\Http\Controllers\Controller;use Illuminate\Support\Facades\DB;use Illuminate\Support\Facades\Redis;class SecKillControllers extends Controller { public function SecKillTest() { ///在此之前我們已經將一千過使用者寫入了redis中了 $num = Redis::lpop('user_list'); ///取出一個使用者 /// ///一些對請求的處理 /// if (!is_null($num)) { ///將需要秒殺的商品放入隊列中 $this->AddGoodToRedis(1); ///需要注意的是我們如果寫的是秒殺活動的話,需要做進一步的處理,例如設定商品隊列的緩衝等方式,這裡就實現了 ///下訂單減庫存 $this->GetGood(1,$num); } } public function DoLog($log) { file_put_contents("test.txt", $log . '\r\n', FILE_APPEND); } /** * 重點在於Redis中儲存資料的單線程的原子性,!!!無論多少請求同時執行這個方法,依然是依次執行的!!!!! * 這種方式效能較高,並且確保了對資料庫的單一操作,但容錯率極低,一旦出現未可預知的錯誤會導致資料混亂; */ public function GetGood($id,$user_id) { $good = \App\Goods::find($id); if (is_null($good)) { $this->DoLog("商品不存在"); return 'error'; } ///去除一個庫存 $num = Redis::lpop('good_list'); ///判斷取出庫存是否成功 if (!$num) { $this->DoLog("取出庫存失敗"); return 'error'; } else { ///建立訂單 $order = new \App\Order(); $order->good_id = $good->good_id; $order->user_id = $user_id; $order->save(); $ok = DB::table('Goods') ->where('good_id', $good->good_id) ->decrement('good_left', $num); if (!$ok) { $this->DoLog("庫存減少失敗"); return; } echo '下單成功'; } } public function AddUserToRedis() { $user_count = 1000; for ($i = 0; $i < $user_count; $i++) { try { Redis::lpush('user_list', rand(1, 10000)); } catch (Exception $e) { echo $e->getMessage(); } } $user_num = Redis::llen('user_list'); dd($user_num); } public function AddGoodToRedis($id) { $good = \App\Goods::find($id); if ($good == null) { $this->DoLog("商品不存在"); return; } ///擷取當前redis中的庫存。 $left = Redis::llen('good_list'); ///擷取到當前實際存在的庫存,庫存減去Redis中剩餘的數量。 $count = $good->good_left - $left; // dd($good->good_left); ///將實際庫存添加到Redis中 for ($i = 0; $i < $count; $i++) { Redis::lpush('good_list', 1); } echo Redis::llen('good_list'); } public function getGood4Mysql($id) { DB::beginTransaction(); ///開啟事務對庫存以及下單進行處理 try { ///建立訂單 $order = new \App\Order(); $order->good_id = $good->good_id; $order->user_id = rand(1, 1000); $order->save(); $good = DB::table("goods")->where(['goods_id' => $id])->sharedLock()->first(); //對商品表進行加鎖(悲觀鎖) if ($good->good_left) { $ok = DB::table('Goods') ->where('good_id', $good->good_id) ->decrement('good_left', $num); if ($ok) { // 提交事務 DB::commit(); echo'下單成功'; } else { $this->DoLog("庫存減少失敗"); } } else { $this->DoLog("庫存剩餘為空白"); } DB::rollBack(); return 'error'; } catch (Exception $e) { // 出錯復原資料 DB::rollBack(); return 'error'; //執行其他動作 } }}
AB測試
這裡我使用了apache bench對代碼進行測試
調用 代碼中的
AddUserToRedis()方法將一堆請求使用者放進redis隊列中先看庫存
這裡設定了一千個庫存開始壓力測試
向我們的程式發起1200個請求,並發量為200
這裡我們完成了1200個請求,其中標記失敗的有1199個。這個是因為apache bench會以第一個請求響應的內容作為基準,
如果後續請求響應內容不一致會標記為失敗,如果看到length中標記的數量不要方,基本可以忽略,我們的請求實際是完成了的。
以上就是本文的全部內容,希望對大家的學習有所協助,更多相關內容請關注topic.alibabacloud.com!