>  기사  >  백엔드 개발  >  laravel+Redis는 스트레스 테스트를 통과한 대기열의 높은 동시 처리를 구현합니다.

laravel+Redis는 스트레스 테스트를 통과한 대기열의 높은 동시 처리를 구현합니다.

不言
不言원래의
2018-07-06 10:37:078677검색

이 글에서는 스트레스 테스트를 통과하는 대기열의 높은 동시성 처리를 간단히 구현하기 위해 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 test

여기서 Apache 벤치를 사용하여 호출 코드에서 코드를 테스트했습니다

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

🎜🎜#

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

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

여기서 1200개의 요청을 완료했는데 그 요청은 실패로 표시되었습니다. 1199. 이는 Apache 벤치가 첫 번째 요청 응답의 내용을 벤치마크로 사용하기 때문입니다.

후속 요청 응답의 내용이 일치하지 않는 경우 숫자가 표시됩니다. 표시 길이가 올바르지 않으면 기본적으로 무시할 수 있습니다. 요청이 실제로 완료됩니다.

위 내용은 모두의 학습에 도움이 되기를 바랍니다. 더 많은 관련 내용은 PHP 중국어 홈페이지를 주목해주세요!

관련 권장 사항:

nginx 구성 파일의 fastcgi_param 구성 오류에 대한 해결 방법

#🎜🎜 #워드프레스가 wp_head() 함수를 사용하는 방법

위 내용은 laravel+Redis는 스트레스 테스트를 통과한 대기열의 높은 동시 처리를 구현합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.