Swoole+Redis實現異步隊列,處理高併發場景

之前做過redis處理併發的,處理效率要比直接操作mysql快1一倍左右,但其實效率還是不高,雖然redis是很快,但因爲要經過nginx,單個nginx處理併發的能力也是有限的,所以這一塊的瓶頸是nginx的併發能力。

既然如此,我們就玩點花的,用PHP處理併發的最強工具,Swoole

Swoole是什麼?

Swoole的實際就是一個底層使用C實現的PHP協程框架,他使 PHP 開發人員可以編寫高性能高併發的 TCP、UDP、Unix Socket、HTTP、 WebSocket 等服務,而這次,我們用到的就是http服務器

下面這個是我封裝好的一個Swoole類,只要裝好swoole和redis擴展就能用

<?php
class Swoole
{
    function __construct()
    {
       $http = new swoole_http_server("0.0.0.0", 8888);

        $http->on("start", function ($server)
        {
            echo "Swoole http server is started at http://wrsndm:8888\n";
        });

        $http->on("request", function ($request, $response)
        {
            $this->response = $response;
            //防止Chrome請求2次的問題
            if ($request->server['path_info'] == '/favicon.ico' || $request->server['request_uri'] == '/favicon.ico') {
                $response->end();
                return;
            }
            $response->header("Content-Type", "text/plain; charset=utf-8");//中文編碼
            $method = $request->get['method'] ?? 'index';
            $this->$method($request);
//            $response->end("Hello World Rand Number is:" . rand(0, 100) . "\n");
        });

        //實例化Redis
        $this->redis = new Redis();
        $this->redis->connect('redis', '6379');//redis可改成自己的IP

        //啓動swoole_http服務
        $http->start();

    }

    public function index($request)
    {
        $this->response->end('剩餘庫存' . $this->redis->lLen('goods_list'));
    }

    public function redis_qianghuo($request){

        //查詢庫存
        if($this->redis->lLen('goods_list') == 0) {
            //記錄失敗次數
            while (true){
                if(!$this->redis->set('fail_lock',1,['EX'=>5,'NX']))//拿鎖的權限,沒拿到則continue
                    continue;
                $num = $this->redis->get('fail_num');
                $this->redis->set('fail_num', $num+1);
                $this->redis->del('fail_lock');//解鎖
                break;
            }
            $this->ajaxReturn('商品已售完...');
        }
        $uid = $request->server['remote_port'];

        //查詢是否重複購買
//        if($this->redis->sIsMember('bought_list',$uid)) {
//            //記錄重複購買次數
////            while (true){
////                if(!$this->redis->set('bought_lock',1,['EX'=>5,'NX']))//拿鎖的權限,沒拿到則continue
////                    continue;
////                $num = $this->redis->get('bought_num');
////                $this->redis->set('bought_num', $num+1);
////                $this->redis->del('bought_lock');//解鎖
////                break;
////            }
//            $this->ajaxReturn('你已經購買過了!');
//            echo "已經購買過了\n";
//            return;
//        }

        //商品出隊
        $goods_id = $this->redis->rpop('goods_list');
        //uid添加到已搶購名單,防止重複搶購
        $this->redis->sAdd('bought_list',$uid);

        $value = array(
            'uid'   =>  $uid,
            'goods_id'   =>  $goods_id,
            'time'  =>  time(),
        );
        //保存訂單信息
        $this->redis->hSet('order_info',$uid,json_encode($value));
        $this->ajaxReturn('購買成功。');
        echo "購買成功\n";
    }

    private function ajaxReturn($info){
        $this->response->end($info);
    }

}

$swoole = new Swoole();

首先要啓動swoole_http服務器,只能使用cli模式啓動,成功會輸出下面這段文字

然後我們用ab進行1W次的併發測試

ab -c10 -n10000 localhost:8999/?method=redis_qianghuo

然後是Nginx服務器的併發測試

可以看到,swoole處理是6.9秒,且全部成功,nginx處理是18秒,而且有276個失敗請求(我之前測的,錯誤率還挺高),效率提升了差不多2倍,我用的最低配的阿里雲,所以處理能力不是很強,rps只有1400,到生產環境部署,併發能力會更加強勁!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章