larave octane 是什么

octanelaravel 官方提供的第三方包,它可以通过集成 SwooleRoadRunner 这两种高性能的应用服务器来提高应用程序的性能。

RoadRunner 本身是一个用 Go 语言编写的开源 PHP 应用程序服务器。
Swoole 是一个使用 C++ 语言编写的基于异步事件驱动和协程的并行网络通信引擎,为 PHP 提供协程、高性能网络编程支持。

ApacheNginxPHP-FPM 非常相似,HTTP 请求可以发送到 RoadRunnerSwoole 以执行 PHP 代码,然后将响应发送回。 RoadRunnerSwoole 的不同之处在于它可以将 PHP 运行中的应用程序保留在内存中,以便在应用程序启动后获得更快的速度。 对于 Laravel 来说,这是相当有帮助的。

环境要求

  1. PHP 8.0+
  2. Larave 8.35+
  3. SwooleRoadRunner 必须在 unix 系统下安装(macOS, linux, window(WSL2)
  4. RoadRunner:请先确保 curlzip sockets 等 php 扩展已经安装
  5. 本地虚拟机,IP: 192.168.2.11

安装历程

按照 Laravel 的官方文档来安装 SwooleRoadRunner 第三方包是一件非常痛苦的事,因为安装的过程中经常会遇到乱七八糟的问题。为了减少大家走的弯路,所以我有必要在这里着重强调几个问题。

  1. 一定要在 unix 系统下安装,unix 系统包括 macOS linuxwindows的linux子系统
  2. 为了能够在本地能够访问虚拟机,请先关闭 linux 防火墙

Step1. 安装 octane

composer require laravel/octane

假设你已经有了新的 laravel 项目。进入项目根目录下执行以上命令。

Step2. 配置 nginx 代理

为什么要先配置 nginx 的代理?

官方文档 中说:

The Octane server can be started via the octane:start Artisan command. By default, this command will utilize the server specified by the server configuration option of your application's octane configuration file:

php artisan octane:start

By default, Octane will start the server on port 8000, so you may access your application in a web browser via http://localhost:8000.

意思是执行启动命令后,默认端口 8000,在浏览器中可以通过 http://localhost:8000 访问。 但我通过 http://192.168.2.11:8000 却访问不了,知道这个问题原因的朋友可以留言告知下。

按照官方文档nginx 配置

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

server {
    listen 8009;            # 浏览器端访问的端口
    #listen [::]:80;
    server_name 127.0.0.1;
    server_tokens off;
    root /var/www/html/octane/public;

    index index.php;

    charset utf-8;

    location /index.php {
        try_files /not_exists @octane;
    }

    location / {
        try_files $uri $uri/ @octane;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    access_log /var/log/nginx/octane-access.log main;
    error_log  /var/log/nginx/octane-error.log error;

    error_page 404 /index.php;

    location @octane {
        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;

        proxy_pass http://127.0.0.1:8010$suffix;            # 代理请求
    }
}

注意上面配置中注释的地方:

  • listen 8009: 是我将要在浏览器中访问的端口 http://192.168.2.11:8009
  • http://127.0.0.1:8010: 是 octane 启动时指定的端口

Step3. 使用 Swoole

1. 安装 Swoole 扩展

Octane 是依赖 Swoole 扩展的。php Swoole 扩展的安装大家从网上找些教程安装下就行。

2. 发布 octane 的配置文件

php artisan octane:install

此命令将会在 config 目录下生成 octane.php 的配置文件

3. 开启服务

php artisan octane:start --server=swoole --port=8010

输出:

  INFO  Server running…

  Local: http://127.0.0.1:8010 

  Press Ctrl+C to stop the server

看到这些,说明 octane 已经成功启动了。

4. 浏览器中访问

因为我们刚才已经配置了 nginx 代理,只要在浏览器中输入 http://192.168.2.11:8009 并回车。
可以看到命令界面上会出现 GET 请求。

从上图可以看到,第一次请求用了 85.46ms,而从第二次之后只用了几毫秒,速度上有了极大的提升。

Step4. ab 压测

下面是虚拟机上的liunx系统的数据:

  1. 查看内核版本

    [root@localhost ~]# uname -r
    3.10.0-1160.41.1.el7.x86_64
  2. 查看 CPU

    [root@localhost ~]# grep "model name" /proc/cpuinfo | cut -f2 -d:
    Intel(R) Core(TM) i5-10500 CPU @ 3.10GHz
    Intel(R) Core(TM) i5-10500 CPU @ 3.10GHz
  3. 查看 CPU 核数

    [root@localhost ~]# cat /proc/cpuinfo |grep "cores"|uniq
    cpu cores    : 2
  4. 查看内存

    [root@localhost ~]# grep MemTotal /proc/meminfo | cut -f2 -d:
    1863020 kB

Swoole 压测

1. 创建 users 表:
CREATE TABLE `users` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `role` tinyint(3) unsigned NOT NULL DEFAULT '2' COMMENT '角色:1-管理员,2-会员...',
  `created_at` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
  `updated_at` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
  `deleted_at` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '删除时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='会员表';
2. 修改配置文件
# .env
DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=test
DB_USERNAME=root
DB_PASSWORD=123456

3. 生成控制器并编写代码

生成控制器

php artisan make:controller UserController

编写代码

namspace App\Http\Controllers;

class UserController extends Controller
{
    public array $store = [];

    public function index(Request $request)
    {
        $nameList = ['james', 'lucy', 'jack', 'jessica', 'lily'];

        $name = $nameList[array_rand($nameList)];

        if (rand(0, 7) >= 2) {   // 0-1 读 2-7 写
            $test = new User();
            $test->name = $name;
            $test->password = Hash::make(123456);
            $test->role = 2;
            $test->created_at = time();
            $test->updated_at = time();
            $test->deleted_at = time();
            $test->save();

            $data = ['id' => $test->id];
        } else {
            $data = User::query()->where('name', $name)->first();;
        }
    }
}
4. 添加路由
# web.php
Route::get('test-mysql', 'UserController@index');
5. 开始压测

压测命令

ab -n 1000 -c 8 http://127.0.0.1:8010/test-mysql
  • -n 1000: 指定测试会话使用的请求数为1000
  • -c 8: 8个并发请求

使用 Octane 压测结果如下:

[root@localhost octane]# ab -n 1000 -c 8 http://127.0.0.1:8010/test-mysql
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:        
Server Hostname:        127.0.0.1
Server Port:            8010

Document Path:          /test-mysql
Document Length:        0 bytes

Concurrency Level:      8
Time taken for tests:   22.268 seconds
Complete requests:      1000
Failed requests:        0
Write errors:           0
Total transferred:      1048000 bytes
HTML transferred:       0 bytes
Requests per second:    44.91 [#/sec] (mean)
Time per request:       178.141 [ms] (mean)
Time per request:       22.268 [ms] (mean, across all concurrent requests)
Transfer rate:          45.96 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       1
Processing:    14  177  43.8    180     327
Waiting:       14  177  43.8    179     327
Total:         14  177  43.8    180     327

Percentage of the requests served within a certain time (ms)
  50%    180
  66%    195
  75%    207
  80%    217
  90%    230
  95%    241
  98%    254
  99%    261
 100%    327 (longest request)

再看看不用 Octane 时的压测结果

执行一下命令,8008 是我单独配置的端口,不经过 octane

ab -n 1000 -c 8 http://127.0.0.1:8008/test-mysql

结果:

[root@localhost octane]# ab -n 1000 -c 8 http://127.0.0.1:8008/test-mysql
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:        nginx/1.20.1
Server Hostname:        127.0.0.1
Server Port:            8008

Document Path:          /test-mysql
Document Length:        0 bytes

Concurrency Level:      8
Time taken for tests:   55.076 seconds
Complete requests:      1000
Failed requests:        0
Write errors:           0
Total transferred:      1134000 bytes
HTML transferred:       0 bytes
Requests per second:    18.16 [#/sec] (mean)
Time per request:       440.606 [ms] (mean)
Time per request:       55.076 [ms] (mean, across all concurrent requests)
Transfer rate:          20.11 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:    70  438 193.7    424    1587
Waiting:       70  438 193.7    424    1587
Total:         70  438 193.7    424    1588

Percentage of the requests served within a certain time (ms)
  50%    424
  66%    511
  75%    565
  80%    593
  90%    652
  95%    705
  98%    783
  99%   1183
 100%   1588 (longest request)

根据上面两种结果的比较可以看出,octane 速度明显提升了几倍。

Step6. 优势与劣势

1. 优势

Octane 之前,我们一直用 Nginx + PHP-FPM 的组合,它执行的顺序是:

  1. 一个请求来到 Nginx
  2. Nginx 将请求委托给 PHP-FPMPHP-FPM 要么产生一个新的 woker,要么使用一个现有的 woker 来处理请求。
  3. PHP-FPM worker 启动 Laravel 应用程序。
  4. Laravel 的应用程序处理请求。
  5. PHP-FPM 将结果返回给 NginxNginx 将输出返回给客户端。

如上所述,每个请求从开始创建进程和到启动 Laravel 应用程序都会产生开销。在第4步中,lavale 框架会加载所有需要的依赖,包括 vendor包, Service Providers, Middlewareroute 等等,随着第三方 service 越来越多,启动速度越来越不受控。

Laravel Octane 则通过启动 Application 一次,常驻内存的方式来加速应用

2. 劣势

  1. 服务容器注入可能绑定不成功
  2. HTTP Request 注入时请求参数可能会不正确
  3. 可能会出现内存泄漏的情况

结语:

Laravel Octane 离新发布只有几个月的时间,对于很多开发者来说,它并不是必须的,因为许多应用瓶颈都与未优化的数据库查询、缓慢的会话存储或缓慢的外部 API 请求有关。如果要在自己的项目中使用 Laravel Octane,就要做好会遇到更多让人头疼的问题的准备。

下一篇会讲解 RoadRunner 的使用。


大步点点
191 声望14 粉丝

There are only two things you need to be a great programmer: curiosity and kindness. Everything else you can learn over time. Everything.