使用Laraveltw/laravel-swoole加速Laravel项目

3,314 阅读3分钟

Laravel 慢在哪里?

  • 初始化框架需要加载208+文件被加载
  • 每个文件都需要被解析和编译
  • 每个请求结束后编译结果会被销毁
  • 默认的session 驱动也是基于文件
  • Laravel本身是一个全栈、组件框架

Laradock 安装Swoole

本地环境是以Laradock为例,需要在 laradock 目录下的 .env 中将下面两行配置值设置为 true

...
WORKSPACE_INSTALL_SWOOLE=true
...
PHP_FPM_INSTALL_SWOOLE=true
...

然后运行 重新构建 Docker 容器

$ docker-compose build php-fpm workspace

构建完成后重启这两个容器,进入 workspace 容器,运行 php -m 查看 Swoole 是否安装成功,如果扩展列表包含 swoole 则表示安装成功。

$ php -m | grep swoole

swoole

Tips: 额外提示

删除镜像

docker rmi $(docker images --filter "dangling=true" -q --no-trunc)

删除全部镜像

docker image prune -a -f

清除 Container,Image

docker system prune

重新编译镜像

$ cd ~/laradock

$ docker-compose build php-fpm workspace mysql redis

workspace 和 php-fpm 的区别

环境配置文件 .env 里包含相似的两块配置:workspace 和 php-fpm,它们对应两个不同的容器,一个是 FPM,一个是 CLI。不管是安装插件还是修改配置,都要分开修改。

使用 Laradock 创建 Laravel 开发环境

CentOS编译安装Swoole

在服务器中,克隆源码

$ git clone https://gitee.com/swoole/swoole

进入swoole目录,编译安装


$ cd swoole

$ phpize 
Configuring for:
PHP Api Version:         20170718
Zend Module Api No:      20170718
Zend Extension Api No:   320170718

$ ./configure
checking for grep that handles long lines and -e... /usr/bin/grep
checking for egrep... /usr/bin/grep -E
checking for a sed that does not truncate output... /usr/bin/sed
checking for cc... cc
checking whether the C compiler works... yes
checking for C compiler default output fi
...

configure: creating ./config.status
config.status: creating config.h
config.status: executing libtool commands

$ make && make install

...

Build complete.
Don't forget to run 'make test'.

Installing shared extensions:     /usr/lib64/php/modules/
Installing header files:          /usr/include/php/

configure: error: in `/root/swoole': configure: error: C++ preprocessor "/lib/cpp" fails sanity check

遇到以上问题

$ yum install  -y glibc-headers 

$ yum install -y gcc-c++

确认是否已经安装了swoole扩展

$ php -m|grep swoole

如果没有加载成功,修改php.ini并添加扩展。使用php --ini查看php.ini的文件位置,找到扩展如何添加,比如在我的CentOS 7中安装扩展如下:

$ cd /etc/php.d

$ vi swoole.ini

; Enable zip extension module
extension=swoole.so

重启php-fpm

$ systemctl restart php-fpm

再次查看是否安装成功

$ php -m | grep swoole

swoole

对比Larave-S 与 LaravelTW扩展

  • 两者在Github的活跃/Star/fork方面不分伯仲
  • laravel-s: 特性多,QQ群沟通快,更新活跃
  • swooletw: 台湾社区维护,支持socket.io
  • laravel-s在处理异常时直接返回500并提示错误,不太适合JSON返回场景
  • laravetw 在HTTP与WebSocket通过Route区分开来,更符合Laravel的规范

综上述,推荐使用LaravelTW扩展。

安装LaravelS

composer 安装最新版本

composer require "hhxsv5/laravel-s:~3.5.0" -vvv

发布配置

php artisan laravels publish

开启HTTP服务

[root@iz8vbh3xuahhi5gqllg1u1z current]# php bin/laravels start
 _                               _  _____
| |                             | |/ ____|
| |     __ _ _ __ __ ___   _____| | (___
| |    / _` | '__/ _` \ \ / / _ \ |\___ \
| |___| (_| | | | (_| |\ V /  __/ |____) |
|______\__,_|_|  \__,_| \_/ \___|_|_____/

Speed up your Laravel/Lumen
>>> Components
+-------------------------+--------------+
| Component               | Version      |
+-------------------------+--------------+
| PHP                     | 7.2.22       |
| Swoole                  | 4.4.13-alpha |
| LaravelS                | 3.5.14       |
| Laravel Framework [dev] | 6.4.1        |
+-------------------------+--------------+
>>> Protocols
+-----------+--------+-------------------+----------------+
| Protocol  | Status | Handler           | Listen At      |
+-----------+--------+-------------------+----------------+
| Main HTTP | On     | Laravel Framework | 127.0.0.1:5200 |
+-----------+--------+-------------------+----------------+
>>> Feedback: https://github.com/hhxsv5/laravel-s
[2019-11-13 09:14:52] [TRACE] Swoole is running, press Ctrl+C to quit.

使用Supervisor管理进程

[program:develop-laravel-swoole-api]
command=/usr/bin/php /var/www/develop/zqz/api/current/bin/laravels start -i
numprocs=1
autostart=true
autorestart=true
startretries=3
redirect_stderr=true
stdout_logfile=/var/www/develop/zqz/api/current/storage/logs/supervisord-stdout.log
user=apache

NGINX 配置

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

upstream swoole {
    # 通过 IP:Port 连接
    server 127.0.0.1:5200 weight=5 max_fails=3 fail_timeout=30s;
    # 通过 UnixSocket Stream 连接,小诀窍:将socket文件放在/dev/shm目录下,可获得更好的性能
    #server unix:/xxxpath/laravel-s-test/storage/laravels.sock weight=5 max_fails=3 fail_timeout=30s;
    #server 192.168.1.1:5200 weight=3 max_fails=3 fail_timeout=30s;
    #server 192.168.1.2:5200 backup;
    keepalive 16;
}


server {
    listen  80;

    server_name  api-dev.zqz.com;
    root   /var/www/develop/zqz/api/current/public;
    access_log  /var/log/nginx/api-dev.zqz.com.access.log  main;


    gzip on;
    gzip_comp_level 9;
    gzip_buffers 4 32k;
    gzip_min_length  1k;
    gzip_types text/plain application/json application/x-javascript application/css application/xml application/xml+rss text/javascript application/x-httpd-php image/jpeg image/gif image/png image/x-ms-bmp;
    gzip_vary on;

    autoindex off;
    index index.html index.htm;
    # Nginx处理静态资源(建议开启gzip),LaravelS处理动态资源。
    location / {
        try_files $uri @laravels;
    }
    # Http和WebSocket共存,Nginx通过location区分
    # Javascript: var ws = new WebSocket("ws://laravels.com/ws");
    location =/ws {
        proxy_http_version 1.1;
        # proxy_connect_timeout 60s;
        # proxy_send_timeout 60s;
        # proxy_read_timeout:如果60秒内被代理的服务器没有响应数据给Nginx,那么Nginx会关闭当前连接;同时,Swoole的心跳设置也会影响连接的关闭
        # proxy_read_timeout 60s;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Real-PORT $remote_port;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header Scheme $scheme;
        proxy_set_header Server-Protocol $server_protocol;
        proxy_set_header Server-Name $server_name;
        proxy_set_header Server-Addr $server_addr;
        proxy_set_header Server-Port $server_port;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_pass http://swoole;
    }

    location @laravels {
        proxy_http_version 1.1;
        # proxy_connect_timeout 60s;
        # proxy_send_timeout 60s;
        # proxy_read_timeout 120s;
        proxy_set_header Connection "";
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Real-PORT $remote_port;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header Scheme $scheme;
        proxy_set_header Server-Protocol $server_protocol;
        proxy_set_header Server-Name $server_name;
        proxy_set_header Server-Addr $server_addr;
        proxy_set_header Server-Port $server_port;
        proxy_pass http://swoole;
    }
}

安装laraveltw/laravel-swoole

composer 安装最新版本

composer require swooletw/laravel-swoole -vvv

发布配置

php artisan vendor:publish --tag=laravel-swoole

运行HTTP服务

[root@iz8vbh3xuahhi5gqllg1u1z current]# php artisan swoole:http infos
+-----------------+---------------------------------------------------------------------------------+
| Name            | Value                                                                           |
+-----------------+---------------------------------------------------------------------------------+
| PHP Version     | 7.2.22                                                                          |
| Swoole Version  | 4.4.13-alpha                                                                    |
| Laravel Version | 6.4.1                                                                           |
| Listen IP       | 127.0.0.1                                                                       |
| Listen Port     | 1215                                                                            |
| Server Status   | Offline                                                                         |
| Reactor Num     | 8                                                                               |
| Worker Num      | 8                                                                               |
| Task Worker Num | 0                                                                               |
| Websocket Mode  | Off                                                                             |
| Master PID      | None                                                                            |
| Manager PID     | None                                                                            |
| Log Path        | /var/www/develop/zqz/api/releases/05c0ab09/storage/logs/swoole_http.log |
+-----------------+---------------------------------------------------------------------------------+

使用Supervisor管理进程

[program:develop-laravel-swoole-api]
command=/usr/bin/php /var/www/develop/zqz/api/current/artisan swoole:http start
numprocs=1
autostart=true
autorestart=true
startretries=3
user=apache

NGINX配置

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen  80;

    server_name  api-dev.zqz.com;
    root   /var/www/develop/zqz/api/current/public;
    access_log  /var/log/nginx/api-dev.zqz.com.access.log  main;


    gzip on;
    gzip_comp_level 9;
    gzip_buffers 4 32k;
    gzip_min_length  1k;
    gzip_types text/plain application/json application/x-javascript application/css application/xml application/xml+rss text/javascript application/x-httpd-php image/jpeg image/gif image/png image/x-ms-bmp;
    gzip_vary on;

    autoindex off;
    index index.html index.htm;
    location = /index.php {
        # Ensure that there is no such file named "not_exists"
        # in your "public" directory.
        try_files /not_exists @swoole;
    }
    # any php files must not be accessed
    #location ~* \.php$ {
    #    return 404;
    #}
    location / {
        try_files $uri $uri/ @swoole;
    }

    location @swoole {
        set $suffix "";

        if ($uri = /index.php) {
            set $suffix ?$query_string;
        }

        proxy_http_version 1.1;
        proxy_set_header Host $http_host;
        proxy_set_header Scheme $scheme;
        proxy_set_header SERVER_PORT $server_port;
        proxy_set_header REMOTE_ADDR $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        # IF https
        # proxy_set_header HTTPS "on";

        proxy_pass http://127.0.0.1:1215$suffix;
    }
}

问题集锦

  1. Solve the error:Auth guard driver [api] is not defined.
$ vi config/swoole_http.php

...

 'providers' => [
        Illuminate\Pagination\PaginationServiceProvider::class,
        //Solve the error:`Auth guard driver [api] is not defined.`
        Laravel\Passport\PassportServiceProvider::class,
    ],
...

wrk-HTTP基准测试

wrk是一种现代HTTP基准测试工具,当在单个多核CPU上运行时,能够产生大量负载。它结合了多线程设计和可扩展的事件通知系统,例如epoll和kqueue。

命令行选项

-c, --connections: total number of HTTP connections to keep open with
                   each thread handling N = connections/threads

-d, --duration:    duration of the test, e.g. 2s, 2m, 2h

-t, --threads:     total number of threads to use

-s, --script:      LuaJIT script, see SCRIPTING

-H, --header:      HTTP header to add to request, e.g. "User-Agent: wrk"

    --latency:     print detailed latency statistics

    --timeout:     record a timeout if a response is not received within
                   this amount of time.

wrk 压测

  • CentOS 7.X
  • 8 CPU 16 G
# 使用10个线程创建100个连接请求

$ wrk -t10 -c100 http://api-dev.zqz.com

使用NGINX + PHP-FPM

Running 10s test @ http://api-dev.zqz.com
  10 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   364.10ms  347.89ms   2.00s    86.97%
    Req/Sec    10.64      8.31    59.00     67.01%
  746 requests in 10.10s, 1.30MB read
  Socket errors: connect 0, read 0, write 0, timeout 86
  Non-2xx or 3xx responses: 746
Requests/sec:     73.86
Transfer/sec:    132.15KB

使用 NGINX + Swoole(laraveltw)

Running 10s test @ http://api-dev.zqz.com
  10 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   234.30ms  275.83ms   1.37s    81.79%
    Req/Sec    39.66     29.85   200.00     78.56%
  3816 requests in 10.10s, 1.18MB read
  Socket errors: connect 0, read 0, write 0, timeout 77
  Non-2xx or 3xx responses: 3816
Requests/sec:    377.69
Transfer/sec:    119.50KB

Speed Up Laravel on Top of Swoole

在 laradock 环境中使用 laravel-swoole 加速你的 laravel 应用

PHP & Swoole 与 Java、Go 等技术选型答疑

使用 Laradock 创建 Laravel 开发环境