ホームページ  >  記事  >  バックエンド開発  >  laravel+Redis はストレステストに合格したキューの高同時実行処理を実装するだけです

laravel+Redis はストレステストに合格したキューの高同時実行処理を実装するだけです

不言
不言オリジナル
2018-07-06 10:37:078678ブラウズ

この記事では、laravel Redisのストレステストを通過するキューの簡単な実装の高同時処理を中心に紹介します。一定の参考値があります。共有します。必要な友人は参考にしてください。

フラッシュ セール活動

一般的なオンライン モールでは、一般的なフラッシュ セールやその他の活動など、高度に並行したビジネス状況にさらされることがよくあります。一部のリクエスト情報 フィルタリングと製品在庫の問題。

リクエストでよくある状況は、同じユーザーが複数のリクエストを発行したり、悪意のある攻撃が含まれたり、一部の注文を再購入したりすることです。

在庫に関しては、売れすぎの状況を考慮する必要があります。

シンプルで利用可能な同時処理をシミュレートしてみましょう。

コードに直接移動します

コード プロセス

1. ユーザー リクエストをシミュレートし、ユーザーを Redis キューに書き込みます

2. から 1 つを取り出しますユーザー 処理する情報を要求します (このステップでは、フィルタリング要求、再購入注文など、さらに多くの処理を行うことができます)

3. ユーザーは、在庫を減らすために注文 (支払いなど) を行います。以下の処理には 2 つの方法が使用されます

1 つは、Redis のシングルスレッド アトミック操作機能を使用して、プログラムを線形に動作させ、データの一貫性を維持します。

もう 1 つは、業務に応じて調整できるトランザクションを使用した運用です。ここでは 1 つずつ説明しません。

実際のビジネス状況はさらに複雑ですが、それは基本的な考え方の拡張によるものです。

<?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 ベンチを使用してコードをテストしました

コードを

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

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

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

ここでは 1200 件のリクエストを完了し、そのうち 1199 件が失敗としてマークされました。これは、Apache ベンチが最初のリクエスト応答の内容をベンチマークとして使用するためです。

後続のリクエスト応答の内容が矛盾している場合、失敗としてマークされます。マークの数が表示された場合は、失敗としてマークされます。長さが正しくありませんが、基本的には無視してかまいません。リクエストは実際に完了しました。

上記がこの記事の全内容です。皆様の学習に少しでもお役に立てれば幸いです。その他の関連コンテンツについては、PHP 中国語 Web サイトをご覧ください。

関連する推奨事項:

nginx 設定ファイルの fastcgi_param の設定エラーの解決策

wp_head() 関数の使用方法ワードプレスで

#

以上がlaravel+Redis はストレステストに合格したキューの高同時実行処理を実装するだけですの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。