nginx+swoole+yii2 vs nginx+php-fpm+yii2 簡單的性能對比

1.本地環(huán)境的搭建

環(huán)境:

  • wsl,Ubuntu版本 18.04 LTS
  • 安裝docker,安裝docker-compose
  • 接口測試是在局域網(wǎng)環(huán)境測試

注:cpu是四核,Intel(R) Core(TM) i5-7500 CPU @ 3.40GHz,空余內(nèi)存12G. 但是由于本機運行了大量的服務(wù),所以參考價值不大。
兩種運行環(huán)境是在同一臺電腦下做的對比,且運行其中一個環(huán)境會先停掉另外一個,不存在相互影響的情況。
php都開啟了opcache。

1.1 docker+nginx+swoole +yii2

具體配置文件:https://github.com/10xjzheng/SwooleVsPhpfpm/tree/master/swoole

直接上docker-compose 配置

version: '3.4'
services:
    web:
        image: nginx:latest
        volumes:
            - ./nginx/config/nginx.conf:/etc/nginx/nginx.conf:ro
            - ./nginx/config/qmyx.conf:/etc/nginx/conf.d/qmyx.conf:ro
            #- "./nginx/ssl:/etc/ssl"
            - "/mnt/d/wamp/www/qmyx-base:/usr/share/nginx/html/qmyx-base" #需要修改為你項目的目錄
        ports:
            - "80:80"  # api http
            - "443:443" # api https
        restart: always
        depends_on:
            - php
    php:
        image: 10xjzheng/swoole:latest
        volumes:
            - "/mnt/d/wamp/www/qmyx-base:/home/web" #需要修改為你項目的目錄
        ports:
            - "8888:80"
        command: php /home/web/yii swoole-backend/start #需要修改為你項目的目錄
        restart: always

swoole鏡像是自己build的,dockerfile如下:

FROM php:7.4

ADD sources.list /etc/apt/sources.list

# install modules : GD iconv
RUN apt-get update && apt-get install -y \
        procps \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libpng-dev \
        openssl \
        libssh-dev \
        libpcre3 \
        libpcre3-dev \
        libnghttp2-dev \
        libhiredis-dev \
        libonig-dev \
        curl \
        wget \
        zip \
        unzip \
        git && \
        apt-get autoremove && apt-get clean
# install php pdo_mysql opcache
# WARNING: Disable opcache-cli if you run you php
RUN docker-php-ext-configure gd --with-freetype --with-jpeg && \
    docker-php-ext-install \
    iconv \
    gd \
    pdo_mysql \
    mysqli \
    iconv \
    mbstring \
    json \
    opcache \
    sockets \
    pcntl && \
    echo "opcache.enable_cli=1" >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini

#install redis
RUN pecl install redis && docker-php-ext-enable redis

# install composer
ENV COMPOSER_ALLOW_SUPERUSER 1
RUN curl -sS https://getcomposer.org/installer | php && \
    mv composer.phar /usr/local/bin/composer && \
    composer self-update --clean-backups

# install swoole
#TIP: it always get last stable version of swoole coroutine.
RUN cd /root && \
    curl -o /tmp/swoole-releases https://github.com/swoole/swoole-src/releases -L && \
    cat /tmp/swoole-releases | grep 'href=".*archive.*.tar.gz"' | head -1 | \
    awk -F '"' ' {print "curl -o /tmp/swoole.tar.gz https://github.com"$2" -L" > "/tmp/swoole.download"}' && \
    sh /tmp/swoole.download && \
    tar zxvf /tmp/swoole.tar.gz && cd swoole-src* && \
    phpize && \
    ./configure \
    --enable-coroutine \
    --enable-openssl  \
    --enable-http2  \
    --enable-async-redis \
    --enable-mysqlnd && \
    make && make install && \
    docker-php-ext-enable swoole && \
    echo "swoole.fast_serialize=On" >> /usr/local/etc/php/conf.d/docker-php-ext-swoole-serialize.ini && \
    rm -rf /tmp/*

# set China timezone
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
    echo 'Asia/Shanghai' > /etc/timezone && \
    echo "[Date]\ndate.timezone=Asia/Shanghai" > /usr/local/etc/php/conf.d/timezone.ini

nginx配置如下:

server {
    proxy_ignore_client_abort on;
    set $web /usr/share/nginx/html/qmyx-base/backend/web;
    root $web;
    server_name my.swoole.com;

    location ~* .(ico|gif|bmp|jpg|jpeg|png|swf|js|css|mp3)$ {
        root  $web;
    }

    location / {
        proxy_http_version 1.1;
        proxy_set_header Connection "keep-alive";
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host http://my.swoole.com;
        proxy_pass http://php:80;
    }
 }

1.2 docker+nginx+php-fpm+yii2

具體配置文件:https://github.com/10xjzheng/SwooleVsPhpfpm/tree/master/php-fpm
直接上docker-compose 配置

version: '3.4'
services:
    web:
        image: nginx:latest
        volumes:
            - ./nginx/config/nginx.conf:/etc/nginx/nginx.conf:ro
            - ./nginx/config/qmyx.conf:/etc/nginx/conf.d/qmyx.conf:ro
            #- "./nginx/ssl:/etc/ssl"
            - /mnt/d/wamp/www/qmyx-base:/usr/share/nginx/html/qmyx-base:rw #需要修改為你項目的目錄
        ports:
            - "80:80"
        restart: always
        depends_on:
            - php
    php:
        image: 10xjzheng/php-fpm:latest
        volumes:
            - /mnt/d/wamp/www/qmyx-base:/usr/share/nginx/html/qmyx-base:rw #需要修改為你項目的目錄
        ports:
            - "9000:9000"
        restart: always

php-fpm鏡像也是自己build的,跟swoole鏡像區(qū)別只在于鏡像源swoole用的是php7.4,php-fpm用的是php-fpm7.4,然后swoole鏡像裝了swoole后者沒有。
dockerfile:

FROM php:7.4-fpm

ADD sources.list /etc/apt/sources.list

# install modules : GD iconv
RUN apt-get update && apt-get install -y \
        procps \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libpng-dev \
        openssl \
        libssh-dev \
        libpcre3 \
        libpcre3-dev \
        libnghttp2-dev \
        libhiredis-dev \
        libonig-dev \
        curl \
        wget \
        zip \
        unzip \
        git && \
        apt-get autoremove && apt-get clean
# install php pdo_mysql opcache
# WARNING: Disable opcache-cli if you run you php
RUN docker-php-ext-configure gd --with-freetype --with-jpeg && \
    docker-php-ext-install \
    iconv \
    gd \
    pdo_mysql \
    mysqli \
    iconv \
    mbstring \
    json \
    opcache \
    sockets \
    pcntl && \
    echo "opcache.enable_cli=1" >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini

#install redis
RUN pecl install redis && docker-php-ext-enable redis

# install composer
ENV COMPOSER_ALLOW_SUPERUSER 1
RUN curl -sS https://getcomposer.org/installer | php && \
    mv composer.phar /usr/local/bin/composer && \
    composer self-update --clean-backups

# set China timezone
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
    echo 'Asia/Shanghai' > /etc/timezone && \
    echo "[Date]\ndate.timezone=Asia/Shanghai" > /usr/local/etc/php/conf.d/timezone.ini

1.3 壓測工具wrk的安裝

  • 安裝:依次執(zhí)行下列命令:
git clone https://github.com/wg/wrk.git
cd wrk
make
cp wrk /usr/local/bin
  • 使用方法: wrk <選項> <被測HTTP服務(wù)的URL>
  Options:                                            
    -c, --connections <N>  跟服務(wù)器建立并保持的TCP連接數(shù)量  
    -d, --duration    <T>  壓測時間           
    -t, --threads     <N>  使用多少個線程進行壓測   

    -s, --script      <S>  指定Lua腳本路徑       
    -H, --header      <H>  為每一個HTTP請求添加HTTP頭      
        --latency          在壓測結(jié)束后,打印延遲統(tǒng)計信息   
        --timeout     <T>  超時時間     
    -v, --version          打印正在使用的wrk的詳細版本信息

  <N>代表數(shù)字參數(shù),支持國際單位 (1k, 1M, 1G)
  <T>代表時間參數(shù),支持時間單位 (2s, 2m, 2h)
  • 示例
wrk -t 8 -c 200 -d 30s  --latency http://www.5lmh.com
Running 30s test @ http://www.5lmh.com (壓測時間30s)
  8 threads and 200 connections (共8個測試線程,200個連接)
  Thread Stats   Avg      Stdev     Max   +/- Stdev
              (平均值) (標準差)(最大值)(正負一個標準差所占比例)
    Latency    46.67ms  215.38ms   1.67s    95.59%
    (延遲)
    Req/Sec     7.91k     1.15k   10.26k    70.77%
    (處理中的請求數(shù))
  Latency Distribution (延遲分布)
     50%    2.93ms
     75%    3.78ms
     90%    4.73ms
     99%    1.35s (99分位的延遲)
  1790465 requests in 30.01s, 684.08MB read (30.01秒內(nèi)共處理完成了1790465個請求,讀取了684.08MB數(shù)據(jù))
Requests/sec:  59658.29 (平均每秒處理完成59658.29個請求)
Transfer/sec:     22.79MB (平均每秒讀取數(shù)據(jù)22.79MB)

2.壓測實驗

2.1 YII2測試代碼

yii2框架使用高級版本。執(zhí)行下面命令:

php composer.phar create-project yiisoft/yii2-app-advanced advanced

或者直接下載再解壓:
https://github.com/yiisoft/yii2/releases/download/2.0.32/yii-advanced-app-2.0.32.tgz

然后安裝yii2-swoole:

composer require "feehi/yii2-swoole"

具體說明文檔見:https://github.com/liufee/yii2-swoole

根據(jù)上述文檔修改配置:

'controllerMap'=>[
        'swoole-backend' => [
            'class' => feehi\console\SwooleController::class,
            'rootDir' => str_replace('console/config', '', __DIR__ ),//yii2項目根路徑
            'app' => 'backend',
            'host' => '0.0.0.0',
            'port' => 80,
            'web' => 'web',//默認為web。rootDir app web目的是拼接yii2的根目錄,如果你的應(yīng)用為basic,那么app為空即可。
            'debug' => true,//默認開啟debug,上線應(yīng)置為false
            'env' => 'dev',//默認為dev,上線應(yīng)置為prod
            'swooleConfig' => [
               'reactor_num' => 4, //4核
                'worker_num' => 10, //10個進程
                'daemonize' => false,
                'log_file' => __DIR__ . '/../../backend/runtime/logs/swoole.log',
                'log_level' => 0,
                'pid_file' => __DIR__ . '/../../backend/runtime/server.pid',
            ],
        ]
    ],

測試代碼分為兩種:

  • 1.沒有db操作的,直接返回信息
  • 2.有db操作

2.2 我們先來測試沒有db操作的

  • 代碼如下:
class BrokerController extends Controller
{

    /**
     * Displays homepage.
     * @return array
     */
    public function actionIndex()
    {
        Yii::$app->response->format = Response::FORMAT_JSON;
        return ['code' => 0, 'message' => 'success'];
    }
}
  • 先運行swoole環(huán)境
docker-compose up  -d

監(jiān)控一下進程的cpu和內(nèi)存:

docker stats
image.png

swoole啟用了10個worker進程,使用了59M內(nèi)存,cpu基本沒占用。

執(zhí)行壓測命令,即3個線程1000個鏈接,持續(xù)3s。

wrk -t 3 -c 1000 -d 3s --latency http://my.swoole.com/broker/index

cpu瞬間飆升,內(nèi)存也飆升:


image.png

image.png

看看測試報告:


image.png

qps是每秒80個左右,還有不少timeout,不知道是什么原因。
延遲分布的話,居然要1.9s左右,這只是個返回json數(shù)據(jù)的接口,暈死。

  • php-fpm環(huán)境
    為了保持和swoole一樣數(shù)量的worker進程,我把配置改成以下配置:


    image.png

    同swoole環(huán)境,先啟動:

docker-compose up  -d

監(jiān)控一下進程的cpu和內(nèi)存:

docker stats

啟動后,占用的內(nèi)存和CPU如下圖:


image.png

看似內(nèi)存占用會少一點。

然后還是執(zhí)行一下壓測命令,和swoole保持一致:

wrk -t 3 -c 1000 -d 3s --latency http://my.swoole.com/broker/index

可以看到CPU同樣暴漲,但內(nèi)存的情況卻好很多:


image.png

image.png

再看看測試報告:


image.png

從測試報告來看,從延遲分布來看swoole相當(dāng)好一點,只是沒那么明顯,但是從qps來看,swoole要好很多,swoole是80 requests/sec, 而php-fpm只有60 requests/sec。

  • 結(jié)果分析
  1. 關(guān)于swoole在高并發(fā)情況下,所占內(nèi)存會高出很多,甚至達到1g以上,還伴隨壓測有一些timeout的情況的問題的探索。
    我寫了壓測過程中php進程占用內(nèi)存的日志,grep查詢了一下,如下圖:


    image.png

    代碼是:

SwooleLogger::flushLog(posix_getpid(). '--' . ' after mem:'.$m2.'MB');

即記錄了當(dāng)前pid的進程占用的php內(nèi)存。
可以看到swoole進程在高并發(fā)情況下,內(nèi)存會暴漲到120M以上。
隨后用docker logs CID(容器ID) 也可以驗證我的想法:

image.png

這是導(dǎo)致有一定數(shù)量的timeout的原因。

為什么會這樣子呢?因為swoole是常駐進程,不斷的handle請求,然后用的又是協(xié)程,即沒個請求占用2M內(nèi)存,那么同時處理100個請求就是200M,如果壓測時間縮短,有可能10個worker進程有可能同時達到頂峰,2G。當(dāng)然在我們這里有些請求處理完內(nèi)存會被釋放,所以并沒直接去到2g,但我們能捕獲到達到1g以上的情況。而php-fpm是每一個接口一個進程跑完就釋放,所以內(nèi)存不會這么暴漲。

2.關(guān)于php-fpm的3s的時間段內(nèi)處理請求數(shù)量少并有那么多timeout的問題。
docker logs CID(容器ID) 看看nginx的容器,可以看到在大量200請求里面還夾雜著不少499:

image.png

499 / ClientClosed Request
An Nginx HTTP server extension. This codeis introduced to log the case when the connection is closed by
client whileHTTP server is processing its request, making server unable to send the HTTP header back

即php-fpm響應(yīng)太慢,nginx直接closed了請求。
這是為什么php-fpm 在3s內(nèi)完成的請求比swoole慢的原因,swoole使用了協(xié)程,對于網(wǎng)絡(luò)IO是很有效率的,同一時間段內(nèi)能接受更多請求也合理。

  1. 結(jié)論:
    swoole在并發(fā)情況下的qps要比php-fpm好很多,但在內(nèi)存方面需要優(yōu)化。

2.3 我們再來測試有db操作的

需要自己先配置mysql的鏈接。
yii 測試代碼如下:

<?php
namespace backend\controllers;

use backend\entities\UserEntity;
use yii\web\Controller;
use Yii;
use yii\web\Response;

/**
 * Site controller
 */
class BrokerController extends Controller
{

    /**
     * Displays homepage.
     *
     * @return string
     */
    public function actionIndex()
    {
        Yii::$app->response->format = Response::FORMAT_JSON;
        /** @var UserEntity  $broker */
        $broker = UserEntity::find()
            ->where(['mobile_tel' => '15220165155'])
            ->select('user_id, user_name,mobile_tel')
            ->limit(1)->asArray()->one();

        $bank = UserEntity::getDb()->createCommand("select * from user_bank where user_id = '{$broker['b_regbrokerId']}'")
            ->queryOne();
        return array_merge($broker, $bank);
    }
}

運行了2個SQL語句。

  • 直接壓測swoole,先把并發(fā)請求降下來
 wrk -t 5 -c 500 -d 5s --latency http://my.swoole.com/broker/index
image.png

5s內(nèi)能處理65個requests, qps是12.5+ Requests/sec。

  • 在壓測php-fpm
 wrk -t 5 -c 500 -d 5s --latency http://my.swoole.com/broker/index
image.png

5s內(nèi)能處理60個requests, qps是12+ Requests/sec。

多次壓測結(jié)果,swoole的qps和單位時間內(nèi)處理的請求還是相對好一點,從延遲發(fā)布來看,swoole的表現(xiàn)也相對好一點。

3. 結(jié)論

  1. swoole在并發(fā)情況下性能還是要好一點,但在我這里測試并不明顯。可能是因為yii2框架的原因,也可能是環(huán)境的原因。畢竟不是獨立的服務(wù)器;
  2. swoole并發(fā)情況下的內(nèi)存問題需要有好的解決方式,一個是可以調(diào)大php的限制內(nèi)存,另外一個是對yii框架運行時占用的內(nèi)存做優(yōu)化。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容