首頁 >後端開發 >php教程 >laravel+Redis簡單實現隊列通過壓力測試的高並發處理

laravel+Redis簡單實現隊列通過壓力測試的高並發處理

不言
不言原創
2018-07-06 10:37:078713瀏覽

這篇文章主要介紹了關於laravel Redis簡單實現隊列通過壓力測試的高並發處理,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下

秒殺活動

在一般的網路商城中我們會經常接觸到一些高並發的業務狀況,例如我們常見的秒殺搶購等活動,

在這些業務中我們經常需要處理一些關於請求信息過濾以及商品庫存的問題。

在請求中比較常見的狀況是同一用戶發出多次請求或包含惡意的攻擊,以及一些訂單的複購等情況。

而在庫存方面則需要考慮超賣這種狀況。

下面我們來模擬一個簡單可用的並發處理。

直接上程式碼

程式碼的流程

1.模擬使用者要求,將使用者寫入redis佇列

2.從使用者中取出一個請求資訊進行處理(可以在這個步驟做更多的處理,請求過濾,訂單複購等)

3.用戶下單(支付等),減少庫存。下面使用了兩種方式進行了處理,一種使用了Redis中單線程原子操作的特性使程式一直線性操作從而保持了資料的一致

另外一種是用了事務進行操作,可以根據業務進行調整,這裡就不一一描述了。

實際的業務狀況更為複雜,但更多的是出於對基礎思路的拓展。

<?php

namespace 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(&#39;user_list&#39;);     ///取出一个用户     ///     ///一些对请求的处理     ///
        if (!is_null($num)) {       ///将需要秒杀的商品放入队列中
            $this->AddGoodToRedis(1);       ///需要注意的是我们如果写的是秒杀活动的话,需要做进一步的处理,例如设置商品队列的缓存等方式,这里就实现了       ///下订单减库存
            $this->GetGood(1,$num);
        }
    }

    public function DoLog($log) {
        file_put_contents("test.txt", $log . &#39;\r\n&#39;, FILE_APPEND);
    }

    /**
     * 重点在于Redis中存储数据的单线程的原子性,!!!无论多少请求同时执行这个方法,依然是依次执行的!!!!!
     * 这种方式性能较高,并且确保了对数据库的单一操作,但容错率极低,一旦出现未可预知的错误会导致数据混乱;
     */
    public function GetGood($id,$user_id) {
        $good = \App\Goods::find($id);
        if (is_null($good)) {
            $this->DoLog("商品不存在");
            return &#39;error&#39;;
        }

        ///去除一个库存
        $num = Redis::lpop(&#39;good_list&#39;);
        ///判断取出库存是否成功
        if (!$num) {
            $this->DoLog("取出库存失败");
            return &#39;error&#39;;
        } else {
            ///创建订单
            $order = new \App\Order();
            $order->good_id = $good->good_id;
            $order->user_id = $user_id;
            $order->save();

            $ok = DB::table(&#39;Goods&#39;)
                    ->where(&#39;good_id&#39;, $good->good_id)
                    ->decrement(&#39;good_left&#39;, $num);
            if (!$ok) {
                $this->DoLog("库存减少失败");
                return;
            }
            echo &#39;下单成功&#39;;
        }
    }

    public function AddUserToRedis() {
        $user_count = 1000;
        for ($i = 0; $i < $user_count; $i++) {
            try {
                Redis::lpush(&#39;user_list&#39;, rand(1, 10000));
            } catch (Exception $e) {
                echo $e->getMessage();
            }
        }
        $user_num = Redis::llen(&#39;user_list&#39;);
        dd($user_num);
    }

    public function AddGoodToRedis($id) {

        $good = \App\Goods::find($id);
        if ($good == null) {
            $this->DoLog("商品不存在");
            return;
        }

        ///获取当前redis中的库存。
        $left = Redis::llen(&#39;good_list&#39;);
        ///获取到当前实际存在的库存,库存减去Redis中剩余的数量。
        $count = $good->good_left - $left;
        //        dd($good->good_left);
        ///将实际库存添加到Redis中
        for ($i = 0; $i < $count; $i++) {
            Redis::lpush(&#39;good_list&#39;, 1);
        }
        echo Redis::llen(&#39;good_list&#39;);
    }

    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([&#39;goods_id&#39; => $id])->sharedLock()->first();
            //对商品表进行加锁(悲观锁)
            if ($good->good_left) {
                $ok = DB::table(&#39;Goods&#39;)
                        ->where(&#39;good_id&#39;, $good->good_id)
                        ->decrement(&#39;good_left&#39;, $num);
                if ($ok) {
                    // 提交事务
                    DB::commit();
                    echo&#39;下单成功&#39;;
                } else {
                    $this->DoLog("库存减少失败");
                }
            } else {
                $this->DoLog("库存剩余为空");
            }
            DB::rollBack();
            return &#39;error&#39;;
        } catch (Exception $e) {
            // 出错回滚数据
            DB::rollBack();
            return &#39;error&#39;;
            //执行其他操作
        }
    }

}

AB測試

這裡我使用了apache bench對程式碼進行測試

呼叫程式碼中的 

AddUserToRedis()
方法将一堆请求用户放进redis队列中
先看库存

这里设置了一千个库存
开始压力测试

向我们的程序发起1200个请求,并发量为200

這裡我們完成了1200個請求,其中標記失敗的有1199個。這是因為apache bench會以第一個請求回應的內容作為基準,

如果後續請求回應內容不一致會標記為失敗,如果看到length中標記的數量不要方,基本上可以忽略,我們的請求實際上是完成了的。

以上就是本文的全部內容,希望對大家的學習有所幫助,更多相關內容請關注PHP中文網!

相關推薦:

 對於nginx設定檔中的fastcgi_param的設定錯誤的解決

##wordpress使用wp_head()函數的方法
#

以上是laravel+Redis簡單實現隊列通過壓力測試的高並發處理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn