hhxsv5 / laravel-s
🚀 LaravelS 是 Laravel/Lumen 和 Swoole 之间的即用型适配器。
Requires
- php: >=8.2
- ext-curl: *
- ext-json: *
- ext-pcntl: *
- swoole/ide-helper: @dev
- symfony/console: >=6.4.0
Requires (Dev)
- phpunit/phpunit: >=4.8.36
Suggests
- ext-inotify: Inotify, used to real-time reload.
- ext-swoole: Coroutine-based concurrency library for PHP, require >= 1.7.19.
- dev-PHP-8.x
- v3.8.2
- v3.8.1
- v3.8.0
- v3.7.39
- v3.7.38
- v3.7.37
- v3.7.36
- v3.7.35
- v3.7.34
- v3.7.33
- v3.7.32
- v3.7.31
- v3.7.30
- v3.7.29
- v3.7.28
- v3.7.27
- v3.7.26
- v3.7.25
- v3.7.24
- v3.7.23
- v3.7.22
- v3.7.21
- v3.7.20
- v3.7.19
- v3.7.18
- v3.7.17
- v3.7.16
- v3.7.15
- v3.7.14
- v3.7.13
- v3.7.12
- v3.7.11
- v3.7.10
- v3.7.9
- v3.7.8
- v3.7.7
- v3.7.6
- v3.7.5
- v3.7.4
- v3.7.3
- v3.7.2
- v3.7.1
- v3.7.0
- v3.6.4
- v3.6.3
- v3.6.2
- v3.6.1
- v3.6.0
- v3.5.17
- v3.5.16
- v3.5.15
- v3.5.14
- v3.5.13
- v3.5.12
- v3.5.11
- v3.5.10
- v3.5.9
- v3.5.8
- v3.5.7
- v3.5.6
- v3.5.5
- v3.5.4
- v3.5.3
- v3.5.2
- v3.5.1
- v3.5.0
- v3.4.4
- v3.4.3
- v3.4.2
- v3.4.1
- v3.4.0
- v3.3.10
- v3.3.9
- v3.3.8
- v3.3.7
- v3.3.6
- v3.3.5
- v3.3.4
- v3.3.3
- v3.3.2
- v3.3.1
- v3.3.0
- v3.2.1
- v3.2.0
- v3.1.3
- v3.1.2
- v3.1.1
- v3.1.0
- v3.0.7
- v3.0.6
- v3.0.5
- v3.0.4
- v3.0.3
- v3.0.2
- v3.0.1
- v3.0.0
- v2.0.7
- v2.0.6
- v2.0.5
- v2.0.4
- v2.0.3
- v2.0.2
- v2.0.1
- v2.0.0
- v2.0.0-Alpha
- v1.11.3
- v1.11.2
- v1.11.1
- v1.11.0
- v1.10.19
- v1.10.18
- v1.10.17
- v1.10.16
- v1.10.15
- v1.10.14
- v1.10.13
- v1.10.12
- v1.10.11
- v1.10.10
- v1.10.9
- v1.10.8
- v1.10.7
- v1.10.6
- v1.10.5
- v1.10.3
- v1.10.2
- v1.10.1
- v1.10.0
- v1.9.17
- v1.9.16
- v1.9.15
- v1.9.14
- v1.9.13
- v1.9.12
- v1.9.11
- v1.9.10
- v1.9.9
- v1.9.8
- v1.9.7
- v1.9.6
- v1.9.5
- v1.9.4
- v1.9.2
- v1.9.1
- v1.9.0
- v1.8.14
- v1.8.13
- v1.8.12
- v1.8.11
- v1.8.10
- v1.8.9
- v1.8.8
- v1.8.7
- v1.8.6
- v1.8.5
- v1.8.4
- v1.8.3
- v1.8.2
- v1.8.1
- v1.8.0
- v1.7.14
- v1.7.13
- v1.7.12
- v1.7.11
- v1.7.10
- v1.7.9
- v1.7.8
- v1.7.7
- v1.7.6
- v1.7.5
- v1.7.4
- v1.7.3
- v1.7.1
- v1.7.0
- v1.6.13
- v1.6.12
- v1.6.11
- v1.6.10
- v1.6.9
- v1.6.8
- v1.6.7
- v1.6.6
- v1.6.5
- v1.6.4
- v1.6.3
- v1.6.2
- v1.6.1
- v1.6.0
- v1.5.3
- v1.5.2
- v1.5.1
- v1.5.0
- v1.4.3
- v1.4.2
- v1.4.1
- v1.4.0
- v1.3.6
- v1.3.5
- v1.3.4
- v1.3.3
- v1.3.2
- v1.3.1
- v1.3.0
- v1.2.2
- v1.1.11
- v1.1.10
- v1.1.9
- v1.1.8
- v1.1.7
- v1.1.6
- v1.1.5
- v1.1.4
- v1.1.2
- v1.1.1
- v1.1.0
- v1.0.7
- v1.0.6
- v1.0.5
- v1.0.4
- v1.0.3
- v1.0.1
- v1.0.0
- dev-PHP-7.x
- dev-feature/php8
- dev-revert-448-feature/custom-swoole-response
- dev-feature/custom-swoole-response
- dev-feature/multi-task-wait
- dev-feature/promotheus-more-metrics
- dev-feature/prometheus
- dev-revert-375-master
- dev-feature/dd
- dev-feature/connection_pool
- dev-feature/full_support_coroutine
- dev-feature/coroutine_clients
This package is auto-updated.
Last update: 2024-09-13 03:41:12 UTC
README
🚀 LaravelS 是 Laravel/Lumen 和 Swoole 之间的即用型适配器
持续更新
- 请
关注
此存储库以获取最新更新。
目录
- 特性
- 基准测试
- 要求
- 安装
- 运行
- 部署
- 与 Nginx 协作(推荐)
- 与 Apache 协作
- 启用 WebSocket 服务器
- 监听事件
- 异步任务队列
- 毫秒级定时任务
- 修改代码后自动重启
- 在您的项目中获取 SwooleServer 实例
- 使用 SwooleTable
- 多端口混合协议
- 协程
- 自定义进程
- 通用组件
- 其他特性
- 重要通知
- 用户和案例
- 替代方案
- 赞助商
- 许可
特性
-
内置 Http/WebSocket 服务器
-
内存驻留
-
优雅重启
-
支持 Laravel/Lumen,具有良好的兼容性
-
简单且即用型
基准测试
要求
安装
1. 通过 Composer(packagist) 安装包。
# PHP >=8.2 composer require "hhxsv5/laravel-s:~3.8.0" # PHP >=5.5.9,<=7.4.33 # composer require "hhxsv5/laravel-s:~3.7.0" # Make sure that your composer.lock file is under the VCS
2. 注册服务提供者(选择以下两个之一)。
-
Laravel
: 在config/app.php
文件中,Laravel 5.5+ 自动支持包发现,您应跳过此步骤
'providers' => [ //... Hhxsv5\LaravelS\Illuminate\LaravelSServiceProvider::class, ],
-
Lumen
: 在bootstrap/app.php
文件中$app->register(Hhxsv5\LaravelS\Illuminate\LaravelSServiceProvider::class);
3. 发布配置和二进制文件。
在升级 LaravelS 后,您需要重新发布;点击 此处 查看每个版本的变更说明。
php artisan laravels publish # Configuration: config/laravels.php # Binary: bin/laravels bin/fswatch bin/inotify
4. 修改 config/laravels.php
:监听 IP、监听端口,参考 设置。
性能调优
-
工作进程数量:LaravelS 使用 Swoole 的
同步 IO
模式,工作进程数量设置越大,并发性能越好,但会导致更多内存使用和进程切换开销。如果一个请求需要 100ms,为了提供 1000QPS 的并发能力,至少需要配置 100 个 Worker 进程。计算方法是:worker_num = 1000QPS/(1s/1ms) = 100,因此需要增量压力测试以计算最佳worker_num
。
运行
运行前请仔细阅读通知
,重要通知(重要)。
- 命令:
php bin/laravels {start|stop|restart|reload|info|help}
。
- 命令
start
和restart
的启动选项。
运行时
文件:start
将自动执行php artisan laravels config
并生成这些文件,开发者通常不需要关注它们,建议添加到.gitignore
中。
部署
建议通过 Supervisord 监视主进程,前提是不带
-d
选项,并将swoole.daemonize
设置为false
。
[program:laravel-s-test]
directory=/var/www/laravel-s-test
command=/usr/local/bin/php bin/laravels start -i
numprocs=1
autostart=true
autorestart=true
startretries=3
user=www-data
redirect_stderr=true
stdout_logfile=/var/log/supervisor/%(program_name)s.log
与 Nginx 协作(推荐)
示例.
gzip on; gzip_min_length 1024; gzip_comp_level 2; gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml application/x-httpd-php image/jpeg image/gif image/png font/ttf font/otf image/svg+xml; gzip_vary on; gzip_disable "msie6"; upstream swoole { # Connect IP:Port server 127.0.0.1:5200 weight=5 max_fails=3 fail_timeout=30s; # Connect UnixSocket Stream file, tips: put the socket file in the /dev/shm directory to get better performance #server unix:/yourpath/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; # Don't forget to bind the host server_name laravels.com; root /yourpath/laravel-s-test/public; access_log /yourpath/log/nginx/$server_name.access.log main; autoindex off; index index.html index.htm; # Nginx handles the static resources(recommend enabling gzip), LaravelS handles the dynamic resource. location / { try_files $uri @laravels; } # Response 404 directly when request the PHP file, to avoid exposing public/*.php #location ~* \.php$ { # return 404; #} location @laravels { # proxy_connect_timeout 60s; # proxy_send_timeout 60s; # proxy_read_timeout 120s; proxy_http_version 1.1; 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; # "swoole" is the upstream proxy_pass http://swoole; } }
与 Apache 协作
LoadModule proxy_module /yourpath/modules/mod_proxy.so LoadModule proxy_balancer_module /yourpath/modules/mod_proxy_balancer.so LoadModule lbmethod_byrequests_module /yourpath/modules/mod_lbmethod_byrequests.so LoadModule proxy_http_module /yourpath/modules/mod_proxy_http.so LoadModule slotmem_shm_module /yourpath/modules/mod_slotmem_shm.so LoadModule rewrite_module /yourpath/modules/mod_rewrite.so LoadModule remoteip_module /yourpath/modules/mod_remoteip.so LoadModule deflate_module /yourpath/modules/mod_deflate.so <IfModule deflate_module> SetOutputFilter DEFLATE DeflateCompressionLevel 2 AddOutputFilterByType DEFLATE text/html text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml application/x-httpd-php image/jpeg image/gif image/png font/ttf font/otf image/svg+xml </IfModule> <VirtualHost *:80> # Don't forget to bind the host ServerName www.laravels.com ServerAdmin [email protected] DocumentRoot /yourpath/laravel-s-test/public; DirectoryIndex index.html index.htm <Directory "/"> AllowOverride None Require all granted </Directory> RemoteIPHeader X-Forwarded-For ProxyRequests Off ProxyPreserveHost On <Proxy balancer://laravels> BalancerMember http://192.168.1.1:5200 loadfactor=7 #BalancerMember http://192.168.1.2:5200 loadfactor=3 #BalancerMember http://192.168.1.3:5200 loadfactor=1 status=+H ProxySet lbmethod=byrequests </Proxy> #ProxyPass / balancer://laravels/ #ProxyPassReverse / balancer://laravels/ # Apache handles the static resources, LaravelS handles the dynamic resource. RewriteEngine On RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-d RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f RewriteRule ^/(.*)$ balancer://laravels%{REQUEST_URI} [P,L] ErrorLog ${APACHE_LOG_DIR}/www.laravels.com.error.log CustomLog ${APACHE_LOG_DIR}/www.laravels.com.access.log combined </VirtualHost>
启用 WebSocket 服务器
WebSocket 服务器的监听地址与 Http 服务器相同。
1. 创建 WebSocket 处理器类,并实现接口 WebSocketHandlerInterface
。实例将在启动时自动创建,您无需手动创建。
namespace App\Services; use Hhxsv5\LaravelS\Swoole\WebSocketHandlerInterface; use Swoole\Http\Request; use Swoole\Http\Response; use Swoole\WebSocket\Frame; use Swoole\WebSocket\Server; /** * @see https://www.swoole.co.uk/docs/modules/swoole-websocket-server */ class WebSocketService implements WebSocketHandlerInterface { // Declare constructor without parameters public function __construct() { } // public function onHandShake(Request $request, Response $response) // { // Custom handshake: https://www.swoole.co.uk/docs/modules/swoole-websocket-server-on-handshake // The onOpen event will be triggered automatically after a successful handshake // } public function onOpen(Server $server, Request $request) { // Before the onOpen event is triggered, the HTTP request to establish the WebSocket has passed the Laravel route, // so Laravel's Request, Auth information are readable, Session is readable and writable, but only in the onOpen event. // \Log::info('New WebSocket connection', [$request->fd, request()->all(), session()->getId(), session('xxx'), session(['yyy' => time()])]); // The exceptions thrown here will be caught by the upper layer and recorded in the Swoole log. Developers need to try/catch manually. $server->push($request->fd, 'Welcome to LaravelS'); } public function onMessage(Server $server, Frame $frame) { // \Log::info('Received message', [$frame->fd, $frame->data, $frame->opcode, $frame->finish]); // The exceptions thrown here will be caught by the upper layer and recorded in the Swoole log. Developers need to try/catch manually. $server->push($frame->fd, date('Y-m-d H:i:s')); } public function onClose(Server $server, $fd, $reactorId) { // The exceptions thrown here will be caught by the upper layer and recorded in the Swoole log. Developers need to try/catch manually. } }
2. 修改 config/laravels.php
。
// ... 'websocket' => [ 'enable' => true, // Note: set enable to true 'handler' => \App\Services\WebSocketService::class, ], 'swoole' => [ //... // Must set dispatch_mode in (2, 4, 5), see https://www.swoole.co.uk/docs/modules/swoole-server/configuration 'dispatch_mode' => 2, //... ], // ...
3. 使用 SwooleTable
绑定 FD & UserId,可选,Swoole Table 示例。您还可以使用其他全局存储服务,如 Redis/Memcached/MySQL,但请注意,多个 Swoole 服务器
之间的 FD 可能会发生冲突。
4. 与 Nginx 协作(推荐)
参考 WebSocket 代理
map $http_upgrade $connection_upgrade { default upgrade; '' close; } upstream swoole { # Connect IP:Port server 127.0.0.1:5200 weight=5 max_fails=3 fail_timeout=30s; # Connect UnixSocket Stream file, tips: put the socket file in the /dev/shm directory to get better performance #server unix:/yourpath/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; # Don't forget to bind the host server_name laravels.com; root /yourpath/laravel-s-test/public; access_log /yourpath/log/nginx/$server_name.access.log main; autoindex off; index index.html index.htm; # Nginx handles the static resources(recommend enabling gzip), LaravelS handles the dynamic resource. location / { try_files $uri @laravels; } # Response 404 directly when request the PHP file, to avoid exposing public/*.php #location ~* \.php$ { # return 404; #} # Http and WebSocket are concomitant, Nginx identifies them by "location" # !!! The location of WebSocket is "/ws" # Javascript: var ws = new WebSocket("ws://laravels.com/ws"); location =/ws { # proxy_connect_timeout 60s; # proxy_send_timeout 60s; # proxy_read_timeout: Nginx will close the connection if the proxied server does not send data to Nginx in 60 seconds; At the same time, this close behavior is also affected by heartbeat setting of Swoole. # proxy_read_timeout 60s; proxy_http_version 1.1; 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_connect_timeout 60s; # proxy_send_timeout 60s; # proxy_read_timeout 60s; proxy_http_version 1.1; 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; } }
5. 心跳设置
-
Swoole 的心跳设置
// config/laravels.php 'swoole' => [ //... // All connections are traversed every 60 seconds. If a connection does not send any data to the server within 600 seconds, the connection will be forced to close. 'heartbeat_idle_time' => 600, 'heartbeat_check_interval' => 60, //... ],
-
Nginx 的代理读取超时
# Nginx will close the connection if the proxied server does not send data to Nginx in 60 seconds proxy_read_timeout 60s;
6. 控制器中的推送数据
namespace App\Http\Controllers; class TestController extends Controller { public function push() { $fd = 1; // Find fd by userId from a map [userId=>fd]. /**@var \Swoole\WebSocket\Server $swoole */ $swoole = app('swoole'); $success = $swoole->push($fd, 'Push data to fd#1 in Controller'); var_dump($success); } }
监听事件
系统事件
通常,您可以重置/销毁一些
global/static
变量,或更改当前的Request/Response
对象。
-
laravels.received_request
在 LaravelS 解析Swoole\Http\Request
到Illuminate\Http\Request
后,在 Laravel 的 Kernel 处理此请求之前。// Edit file `app/Providers/EventServiceProvider.php`, add the following code into method `boot` // If no variable $events, you can also call Facade \Event::listen(). $events->listen('laravels.received_request', function (\Illuminate\Http\Request $req, $app) { $req->query->set('get_key', 'hhxsv5');// Change query of request $req->request->set('post_key', 'hhxsv5'); // Change post of request });
-
laravels.generated_response
在 Laravel 的 Kernel 处理请求后,在 LaravelS 解析Illuminate\Http\Response
到Swoole\Http\Response
之前。// Edit file `app/Providers/EventServiceProvider.php`, add the following code into method `boot` // If no variable $events, you can also call Facade \Event::listen(). $events->listen('laravels.generated_response', function (\Illuminate\Http\Request $req, \Symfony\Component\HttpFoundation\Response $rsp, $app) { $rsp->headers->set('header-key', 'hhxsv5');// Change header of response });
自定义异步事件
此功能依赖于
Swoole
的AsyncTask
,您需要先在config/laravels.php
中设置swoole.task_worker_num
。异步事件处理性能受 Swoole 任务进程数量的影响,您需要适当设置 task_worker_num。
1. 创建事件类。
use Hhxsv5\LaravelS\Swoole\Task\Event; class TestEvent extends Event { protected $listeners = [ // Listener list TestListener1::class, // TestListener2::class, ]; private $data; public function __construct($data) { $this->data = $data; } public function getData() { return $this->data; } }
2. 创建监听器类。
use Hhxsv5\LaravelS\Swoole\Task\Event; use Hhxsv5\LaravelS\Swoole\Task\Task; use Hhxsv5\LaravelS\Swoole\Task\Listener; class TestListener1 extends Listener { public function handle(Event $event) { \Log::info(__CLASS__ . ':handle start', [$event->getData()]); sleep(2);// Simulate the slow codes // Deliver task in CronJob, but NOT support callback finish() of task. // Note: Modify task_ipc_mode to 1 or 2 in config/laravels.php, see https://www.swoole.co.uk/docs/modules/swoole-server/configuration $ret = Task::deliver(new TestTask('task data')); var_dump($ret); // The exceptions thrown here will be caught by the upper layer and recorded in the Swoole log. Developers need to try/catch manually. // return false; // Stop propagating this event to subsequent listeners } }
3. 触发事件。
// Create instance of event and fire it, "fire" is asynchronous. use Hhxsv5\LaravelS\Swoole\Task\Event; $event = new TestEvent('event data'); // $event->delay(10); // Delay 10 seconds to fire event // $event->setTries(3); // When an error occurs, try 3 times in total $success = Event::fire($event); var_dump($success);// Return true if sucess, otherwise false
异步任务队列
此功能依赖于
Swoole
的AsyncTask
,您需要先在config/laravels.php
中设置swoole.task_worker_num
。任务处理性能受 Swoole 任务进程数量的影响,您需要适当设置 task_worker_num。
1. 创建任务类。
use Hhxsv5\LaravelS\Swoole\Task\Task; class TestTask extends Task { private $data; private $result; public function __construct($data) { $this->data = $data; } // The logic of task handling, run in task process, CAN NOT deliver task public function handle() { \Log::info(__CLASS__ . ':handle start', [$this->data]); sleep(2);// Simulate the slow codes // The exceptions thrown here will be caught by the upper layer and recorded in the Swoole log. Developers need to try/catch manually. $this->result = 'the result of ' . $this->data; } // Optional, finish event, the logic of after task handling, run in worker process, CAN deliver task public function finish() { \Log::info(__CLASS__ . ':finish start', [$this->result]); Task::deliver(new TestTask2('task2 data')); // Deliver the other task } }
2. 发送任务。
// Create instance of TestTask and deliver it, "deliver" is asynchronous. use Hhxsv5\LaravelS\Swoole\Task\Task; $task = new TestTask('task data'); // $task->delay(3);// delay 3 seconds to deliver task // $task->setTries(3); // When an error occurs, try 3 times in total $ret = Task::deliver($task); var_dump($ret);// Return true if sucess, otherwise false
毫秒级定时任务
基于 Swoole 的毫秒级定时器 的 cron 作业包装器,替换
Linux
的Crontab
。
1. 创建 cron 作业类。
namespace App\Jobs\Timer; use App\Tasks\TestTask; use Swoole\Coroutine; use Hhxsv5\LaravelS\Swoole\Task\Task; use Hhxsv5\LaravelS\Swoole\Timer\CronJob; class TestCronJob extends CronJob { protected $i = 0; // !!! The `interval` and `isImmediate` of cron job can be configured in two ways(pick one of two): one is to overload the corresponding method, and the other is to pass parameters when registering cron job. // --- Override the corresponding method to return the configuration: begin public function interval() { return 1000;// Run every 1000ms } public function isImmediate() { return false;// Whether to trigger `run` immediately after setting up } // --- Override the corresponding method to return the configuration: end public function run() { \Log::info(__METHOD__, ['start', $this->i, microtime(true)]); // do something // sleep(1); // Swoole < 2.1 Coroutine::sleep(1); // Swoole>=2.1 Coroutine will be automatically created for run(). $this->i++; \Log::info(__METHOD__, ['end', $this->i, microtime(true)]); if ($this->i >= 10) { // Run 10 times only \Log::info(__METHOD__, ['stop', $this->i, microtime(true)]); $this->stop(); // Stop this cron job, but it will run again after restart/reload. // Deliver task in CronJob, but NOT support callback finish() of task. // Note: Modify task_ipc_mode to 1 or 2 in config/laravels.php, see https://www.swoole.co.uk/docs/modules/swoole-server/configuration $ret = Task::deliver(new TestTask('task data')); var_dump($ret); } // The exceptions thrown here will be caught by the upper layer and recorded in the Swoole log. Developers need to try/catch manually. } }
2. 注册 cron 作业。
// Register cron jobs in file "config/laravels.php" [ // ... 'timer' => [ 'enable' => true, // Enable Timer 'jobs' => [ // The list of cron job // Enable LaravelScheduleJob to run `php artisan schedule:run` every 1 minute, replace Linux Crontab // \Hhxsv5\LaravelS\Illuminate\LaravelScheduleJob::class, // Two ways to configure parameters: // [\App\Jobs\Timer\TestCronJob::class, [1000, true]], // Pass in parameters when registering \App\Jobs\Timer\TestCronJob::class, // Override the corresponding method to return the configuration ], 'max_wait_time' => 5, // Max waiting time of reloading // Enable the global lock to ensure that only one instance starts the timer when deploying multiple instances. This feature depends on Redis, please see https://laravel.net.cn/docs/7.x/redis 'global_lock' => false, 'global_lock_key' => config('app.name', 'Laravel'), ], // ... ];
3. 注意:构建服务器集群时,将启动多个定时器,因此您需要确保只启动一个定时器,以避免重复运行任务。
4. LaravelS v3.4.0
开始支持热重启 [Reload] Timer
进程。LaravelS 接收到 SIGUSR1
信号后,将等待 max_wait_time
(默认 5)秒结束进程,然后 Manager 进程将再次拉起 Timer
进程。
5. 如果您只需要使用 分钟级
的计划任务,建议启用 Hhxsv5\LaravelS\Illuminate\LaravelScheduleJob
而不是 Linux Crontab,这样您可以遵循 Laravel 任务调度 的编码习惯并配置 Kernel
。
// app/Console/Kernel.php protected function schedule(Schedule $schedule) { // runInBackground() will start a new child process to execute the task. This is asynchronous and will not affect the execution timing of other tasks. $schedule->command(TestCommand::class)->runInBackground()->everyMinute(); }
修改代码后自动重启
-
通过
inotify
,仅支持 Linux。1. 安装 inotify 扩展。
2. 在 设置 中打开开关。
3.注意:仅在使用Linux系统时修改文件以接收文件更改事件。建议使用最新的Docker。 Vagrant解决方案。
-
通过
fswatch
,支持OS X/Linux/Windows。1.安装fswatch。
2.在项目根目录下运行命令。
# Watch current directory ./bin/fswatch # Watch app directory ./bin/fswatch ./app
-
通过
inotifywait
,支持Linux。1.安装inotify-tools。
2.在项目根目录下运行命令。
# Watch current directory ./bin/inotify # Watch app directory ./bin/inotify ./app
-
当上述方法不起作用时,终极解决方案:设置
max_request=1,worker_num=1
,这样在处理一个请求后Worker
进程将重新启动。这种方法性能很差,因此仅在开发环境中使用
。
在项目中获取SwooleServer
的实例
/** * $swoole is the instance of `Swoole\WebSocket\Server` if enable WebSocket server, otherwise `Swoole\Http\Server` * @var \Swoole\WebSocket\Server|\Swoole\Http\Server $swoole */ $swoole = app('swoole'); var_dump($swoole->stats()); $swoole->push($fd, 'Push WebSocket message');
使用SwooleTable
1.定义表,支持多个。
所有定义的表将在Swoole启动前创建。
// in file "config/laravels.php" [ // ... 'swoole_tables' => [ // Scene:bind UserId & FD in WebSocket 'ws' => [// The Key is table name, will add suffix "Table" to avoid naming conflicts. Here defined a table named "wsTable" 'size' => 102400,// The max size 'column' => [// Define the columns ['name' => 'value', 'type' => \Swoole\Table::TYPE_INT, 'size' => 8], ], ], //...Define the other tables ], // ... ];
2.访问Table
:所有表实例都将绑定到SwooleServer
,通过app('swoole')->xxxTable
访问。
namespace App\Services; use Hhxsv5\LaravelS\Swoole\WebSocketHandlerInterface; use Swoole\Http\Request; use Swoole\WebSocket\Frame; use Swoole\WebSocket\Server; class WebSocketService implements WebSocketHandlerInterface { /**@var \Swoole\Table $wsTable */ private $wsTable; public function __construct() { $this->wsTable = app('swoole')->wsTable; } // Scene:bind UserId & FD in WebSocket public function onOpen(Server $server, Request $request) { // var_dump(app('swoole') === $server);// The same instance /** * Get the currently logged in user * This feature requires that the path to establish a WebSocket connection go through middleware such as Authenticate. * E.g: * Browser side: var ws = new WebSocket("ws://127.0.0.1:5200/ws"); * Then the /ws route in Laravel needs to add the middleware like Authenticate. * Route::get('/ws', function () { * // Respond any content with status code 200 * return 'websocket'; * })->middleware(['auth']); */ // $user = Auth::user(); // $userId = $user ? $user->id : 0; // 0 means a guest user who is not logged in $userId = mt_rand(1000, 10000); // if (!$userId) { // // Disconnect the connections of unlogged users // $server->disconnect($request->fd); // return; // } $this->wsTable->set('uid:' . $userId, ['value' => $request->fd]);// Bind map uid to fd $this->wsTable->set('fd:' . $request->fd, ['value' => $userId]);// Bind map fd to uid $server->push($request->fd, "Welcome to LaravelS #{$request->fd}"); } public function onMessage(Server $server, Frame $frame) { // Broadcast foreach ($this->wsTable as $key => $row) { if (strpos($key, 'uid:') === 0 && $server->isEstablished($row['value'])) { $content = sprintf('Broadcast: new message "%s" from #%d', $frame->data, $frame->fd); $server->push($row['value'], $content); } } } public function onClose(Server $server, $fd, $reactorId) { $uid = $this->wsTable->get('fd:' . $fd); if ($uid !== false) { $this->wsTable->del('uid:' . $uid['value']); // Unbind uid map } $this->wsTable->del('fd:' . $fd);// Unbind fd map $server->push($fd, "Goodbye #{$fd}"); } }
多端口混合协议
更多信息请参考Swoole服务器添加监听器
为了让我们的主服务器支持更多协议,而不仅仅是Http和WebSocket,我们在LaravelS中引入了Swoole的多端口混合协议
特性,并将其命名为Socket
。现在,您可以在Laravel上轻松构建TCP/UDP
应用程序。
-
创建
Socket
处理类,并扩展Hhxsv5\LaravelS\Swoole\Socket\{TcpSocket|UdpSocket|Http|WebSocket}
。namespace App\Sockets; use Hhxsv5\LaravelS\Swoole\Socket\TcpSocket; use Swoole\Server; class TestTcpSocket extends TcpSocket { public function onConnect(Server $server, $fd, $reactorId) { \Log::info('New TCP connection', [$fd]); $server->send($fd, 'Welcome to LaravelS.'); } public function onReceive(Server $server, $fd, $reactorId, $data) { \Log::info('Received data', [$fd, $data]); $server->send($fd, 'LaravelS: ' . $data); if ($data === "quit\r\n") { $server->send($fd, 'LaravelS: bye' . PHP_EOL); $server->close($fd); } } public function onClose(Server $server, $fd, $reactorId) { \Log::info('Close TCP connection', [$fd]); $server->send($fd, 'Goodbye'); } }
这些
Socket
连接与您的HTTP
/WebSocket
连接共享相同的Worker进程。因此,如果您想传递任务、使用SwooleTable
,甚至是Laravel组件(如DB、Eloquent等),这根本不是问题。同时,您可以直接通过成员属性swoolePort
访问Swoole\Server\Port
对象。public function onReceive(Server $server, $fd, $reactorId, $data) { $port = $this->swoolePort; // Get the `Swoole\Server\Port` object }
namespace App\Http\Controllers; class TestController extends Controller { public function test() { /**@var \Swoole\Http\Server|\Swoole\WebSocket\Server $swoole */ $swoole = app('swoole'); // $swoole->ports: Traverse all Port objects, https://www.swoole.co.uk/docs/modules/swoole-server/multiple-ports $port = $swoole->ports[0]; // Get the `Swoole\Server\Port` object, $port[0] is the port of the main server foreach ($port->connections as $fd) { // Traverse all connections // $swoole->send($fd, 'Send tcp message'); // if($swoole->isEstablished($fd)) { // $swoole->push($fd, 'Send websocket message'); // } } } }
-
注册Sockets。
// Edit `config/laravels.php` //... 'sockets' => [ [ 'host' => '127.0.0.1', 'port' => 5291, 'type' => SWOOLE_SOCK_TCP,// Socket type: SWOOLE_SOCK_TCP/SWOOLE_SOCK_TCP6/SWOOLE_SOCK_UDP/SWOOLE_SOCK_UDP6/SWOOLE_UNIX_DGRAM/SWOOLE_UNIX_STREAM 'settings' => [// Swoole settings:https://www.swoole.co.uk/docs/modules/swoole-server-methods#swoole_server-addlistener 'open_eof_check' => true, 'package_eof' => "\r\n", ], 'handler' => \App\Sockets\TestTcpSocket::class, 'enable' => true, // whether to enable, default true ], ],
关于心跳配置,它只能在
主服务器
上设置,不能在Socket
上配置,但Socket
继承了主服务器
的心跳配置。对于TCP套接字,当Swoole的
dispatch_mode
为1/3
时,onConnect
和onClose
事件将被阻塞,因此如果您想取消阻塞这两个事件,请将dispatch_mode
设置为2/4/5
。'swoole' => [ //... 'dispatch_mode' => 2, //... ];
-
测试。
-
TCP:
telnet 127.0.0.1 5291
-
UDP: [Linux]
echo "Hello LaravelS" > /dev/udp/127.0.0.1/5292
-
其他协议的注册示例。
- UDP
'sockets' => [ [ 'host' => '0.0.0.0', 'port' => 5292, 'type' => SWOOLE_SOCK_UDP, 'settings' => [ 'open_eof_check' => true, 'package_eof' => "\r\n", ], 'handler' => \App\Sockets\TestUdpSocket::class, ], ],
- Http
'sockets' => [ [ 'host' => '0.0.0.0', 'port' => 5293, 'type' => SWOOLE_SOCK_TCP, 'settings' => [ 'open_http_protocol' => true, ], 'handler' => \App\Sockets\TestHttp::class, ], ],
- WebSocket: 主服务器必须
开启WebSocket
,即设置websocket.enable
为true
。
'sockets' => [ [ 'host' => '0.0.0.0', 'port' => 5294, 'type' => SWOOLE_SOCK_TCP, 'settings' => [ 'open_http_protocol' => true, 'open_websocket_protocol' => true, ], 'handler' => \App\Sockets\TestWebSocket::class, ], ],
协程
-
警告:协程中代码执行的顺序是随机的。应该通过协程ID隔离请求级别的数据。然而,Laravel/Lumen中有许多单例和静态属性,不同请求之间的数据会相互影响,这是
不安全的
。例如,数据库连接是一个单例,相同的数据库连接共享同一个PDO资源。这在同步阻塞模式下是正常的,但在异步协程模式下不起作用。每个查询都需要创建不同的连接并维护不同连接的IO状态,这需要连接池。 -
不要
启用协程,只有自定义进程可以使用协程。
自定义进程
支持开发者创建用于监控、报告或其他特殊任务的特殊工作进程。请参阅addProcess。
-
创建实现CustomProcessInterface的Proccess类。
namespace App\Processes; use App\Tasks\TestTask; use Hhxsv5\LaravelS\Swoole\Process\CustomProcessInterface; use Hhxsv5\LaravelS\Swoole\Task\Task; use Swoole\Coroutine; use Swoole\Http\Server; use Swoole\Process; class TestProcess implements CustomProcessInterface { /** * @var bool Quit tag for Reload updates */ private static $quit = false; public static function callback(Server $swoole, Process $process) { // The callback method cannot exit. Once exited, Manager process will automatically create the process while (!self::$quit) { \Log::info('Test process: running'); // sleep(1); // Swoole < 2.1 Coroutine::sleep(1); // Swoole>=2.1: Coroutine & Runtime will be automatically enabled for callback(). Pay attention to the compatibility between the components used and the coroutines. If they are not compatible, only some coroutines can be enabled, such as: \Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_TCP | SWOOLE_HOOK_SLEEP | SWOOLE_HOOK_FILE); // Deliver task in custom process, but NOT support callback finish() of task. // Note: Modify task_ipc_mode to 1 or 2 in config/laravels.php, see https://www.swoole.co.uk/docs/modules/swoole-server/configuration $ret = Task::deliver(new TestTask('task data')); var_dump($ret); // The upper layer will catch the exception thrown in the callback and record it in the Swoole log, and then this process will exit. The Manager process will re-create the process after 3 seconds, so developers need to try/catch to catch the exception by themselves to avoid frequent process creation. // throw new \Exception('an exception'); } } // Requirements: LaravelS >= v3.4.0 & callback() must be async non-blocking program. public static function onReload(Server $swoole, Process $process) { // Stop the process... // Then end process \Log::info('Test process: reloading'); self::$quit = true; // $process->exit(0); // Force exit process } // Requirements: LaravelS >= v3.7.4 & callback() must be async non-blocking program. public static function onStop(Server $swoole, Process $process) { // Stop the process... // Then end process \Log::info('Test process: stopping'); self::$quit = true; // $process->exit(0); // Force exit process } }
-
注册TestProcess。
// Edit `config/laravels.php` // ... 'processes' => [ 'test' => [ // Key name is process name 'class' => \App\Processes\TestProcess::class, 'redirect' => false, // Whether redirect stdin/stdout, true or false 'pipe' => 0, // The type of pipeline, 0: no pipeline 1: SOCK_STREAM 2: SOCK_DGRAM 'enable' => true, // Whether to enable, default true //'num' => 3 // To create multiple processes of this class, default is 1 //'queue' => [ // Enable message queue as inter-process communication, configure empty array means use default parameters // 'msg_key' => 0, // The key of the message queue. Default: ftok(__FILE__, 1). // 'mode' => 2, // Communication mode, default is 2, which means contention mode // 'capacity' => 8192, // The length of a single message, is limited by the operating system kernel parameters. The default is 8192, and the maximum is 65536 //], //'restart_interval' => 5, // After the process exits abnormally, how many seconds to wait before restarting the process, default 5 seconds ], ],
-
注意:callback() 不能退出。如果退出,Manager进程将重新创建进程。
-
示例:向自定义进程写入数据。
// config/laravels.php 'processes' => [ 'test' => [ 'class' => \App\Processes\TestProcess::class, 'redirect' => false, 'pipe' => 1, ], ],
// app/Processes/TestProcess.php public static function callback(Server $swoole, Process $process) { while ($data = $process->read()) { \Log::info('TestProcess: read data', [$data]); $process->write('TestProcess: ' . $data); } }
// app/Http/Controllers/TestController.php public function testProcessWrite() { /**@var \Swoole\Process[] $process */ $customProcesses = \Hhxsv5\LaravelS\LaravelS::getCustomProcesses(); $process = $customProcesses['test']; $process->write('TestController: write data' . time()); var_dump($process->read()); }
通用组件
Apollo
LaravelS
将在启动时拉取Apollo
配置并将其写入.env
文件。同时,LaravelS
将启动自定义进程apollo
以监控配置并在配置更改时自动reload
。
-
启用 Apollo:将
--enable-apollo
和 Apollo 参数添加到启动参数中。php bin/laravels start --enable-apollo --apollo-server=http://127.0.0.1:8080 --apollo-app-id=LARAVEL-S-TEST
-
支持热更新(可选)。
// Edit `config/laravels.php` 'processes' => Hhxsv5\LaravelS\Components\Apollo\Process::getDefinition(),
// When there are other custom process configurations 'processes' => [ 'test' => [ 'class' => \App\Processes\TestProcess::class, 'redirect' => false, 'pipe' => 1, ], // ... ] + Hhxsv5\LaravelS\Components\Apollo\Process::getDefinition(),
-
可用参数列表。
Prometheus
支持 Prometheus 监控和警报,Grafana 可视化查看监控指标。请参考 Docker Compose 了解 Prometheus 和 Grafana 的环境搭建。
-
需要扩展 APCu >= 5.0.0,请使用
pecl install apcu
安装。 -
将配置文件
prometheus.php
复制到项目config
目录。根据需要修改配置。# Execute commands in the project root directory cp vendor/hhxsv5/laravel-s/config/prometheus.php config/
如果您的项目是
Lumen
,您还需要在bootstrap/app.php
中手动加载配置$app->configure('prometheus');
。 -
配置
global
中间件:Hhxsv5\LaravelS\Components\Prometheus\RequestMiddleware::class
。为了尽可能准确地统计请求时间消耗,RequestMiddleware
必须是第一个全局中间件,需要将其放置在其他中间件之前。 -
注册 ServiceProvider:
Hhxsv5\LaravelS\Components\Prometheus\ServiceProvider::class
。 -
在
config/laravels.php
中配置 CollectorProcess 以定期收集 Swoole Worker/Task/Timer 进程的指标。'processes' => Hhxsv5\LaravelS\Components\Prometheus\CollectorProcess::getDefinition(),
-
创建输出指标的路线。
use Hhxsv5\LaravelS\Components\Prometheus\Exporter; Route::get('/actuator/prometheus', function () { $result = app(Exporter::class)->render(); return response($result, 200, ['Content-Type' => Exporter::REDNER_MIME_TYPE]); });
-
完成 Prometheus 的配置并启动它。
global: scrape_interval: 5s scrape_timeout: 5s evaluation_interval: 30s scrape_configs: - job_name: laravel-s-test honor_timestamps: true metrics_path: /actuator/prometheus scheme: http follow_redirects: true static_configs: - targets: - 127.0.0.1:5200 # The ip and port of the monitored service # Dynamically discovered using one of the supported service-discovery mechanisms # https://prometheus.ac.cn/docs/prometheus/latest/configuration/configuration/#scrape_config # - job_name: laravels-eureka # honor_timestamps: true # scrape_interval: 5s # metrics_path: /actuator/prometheus # scheme: http # follow_redirects: true # eureka_sd_configs: # - server: http://127.0.0.1:8080/eureka # follow_redirects: true # refresh_interval: 5s
-
启动 Grafana,然后导入 panel json。
其他特性
配置 Swoole 事件
支持的事件
1. 创建一个实现相应接口的事件类。
namespace App\Events; use Hhxsv5\LaravelS\Swoole\Events\ServerStartInterface; use Swoole\Atomic; use Swoole\Http\Server; class ServerStartEvent implements ServerStartInterface { public function __construct() { } public function handle(Server $server) { // Initialize a global counter (available across processes) $server->atomicCount = new Atomic(2233); // Invoked in controller: app('swoole')->atomicCount->get(); } }
namespace App\Events; use Hhxsv5\LaravelS\Swoole\Events\WorkerStartInterface; use Swoole\Http\Server; class WorkerStartEvent implements WorkerStartInterface { public function __construct() { } public function handle(Server $server, $workerId) { // Initialize a database connection pool // DatabaseConnectionPool::init(); } }
2. 配置。
// Edit `config/laravels.php` 'event_handlers' => [ 'ServerStart' => [\App\Events\ServerStartEvent::class], // Trigger events in array order 'WorkerStart' => [\App\Events\WorkerStartEvent::class], ],
无服务器
阿里云函数计算
函数计算.
1. 修改 bootstrap/app.php
并设置存储目录。因为项目目录是只读的,/tmp
目录只能读和写。
$app->useStoragePath(env('APP_STORAGE_PATH', '/tmp/storage'));
2. 创建一个 shell 脚本 laravels_bootstrap
并授予 可执行权限
。
#!/usr/bin/env bash set +e # Create storage-related directories mkdir -p /tmp/storage/app/public mkdir -p /tmp/storage/framework/cache mkdir -p /tmp/storage/framework/sessions mkdir -p /tmp/storage/framework/testing mkdir -p /tmp/storage/framework/views mkdir -p /tmp/storage/logs # Set the environment variable APP_STORAGE_PATH, please make sure it's the same as APP_STORAGE_PATH in .env export APP_STORAGE_PATH=/tmp/storage # Start LaravelS php bin/laravels start
3. 配置 template.xml
。
ROSTemplateFormatVersion: '2015-09-01' Transform: 'Aliyun::Serverless-2018-04-03' Resources: laravel-s-demo: Type: 'Aliyun::Serverless::Service' Properties: Description: 'LaravelS Demo for Serverless' fc-laravel-s: Type: 'Aliyun::Serverless::Function' Properties: Handler: laravels.handler Runtime: custom MemorySize: 512 Timeout: 30 CodeUri: ./ InstanceConcurrency: 10 EnvironmentVariables: BOOTSTRAP_FILE: laravels_bootstrap
重要通知
单例问题
-
在 FPM 模式下,单例实例将在每次请求中实例化和回收,请求开始=>实例化实例=>请求结束=>回收实例。
-
在 Swoole Server 下,所有单例实例都将保留在内存中,其生命周期不同于 FPM,请求开始=>实例化实例=>请求结束=>不回收单例实例。因此,需要开发者在每个请求中维护单例实例的状态。
-
常见解决方案
-
编写一个
XxxCleaner
类来清理单例对象状态。此类实现接口Hhxsv5\LaravelS\Illuminate\Cleaners\CleanerInterface
,然后将其注册到laravels.php
的cleaners
中。 -
通过
Middleware
重置单例实例的状态。 -
重新注册
ServiceProvider
,将XxxServiceProvider
添加到laravels.php
文件的register_providers
中。这样,在每次请求中重新初始化单例实例参考。
-
清理器
已知问题
已知问题:已知问题和解决方案的包。
调试方法
-
日志记录;如果您想输出到控制台,可以使用
stderr
,Log::channel('stderr')->debug('debug message')。 -
Laravel Dump Server(Laravel 5.7 默认已集成)。
读取请求
通过 Illuminate\Http\Request
对象读取请求,$_ENV 可读,$_SERVER 部分可读,不能使用
$_GET/$_POST/$_FILES/$_COOKIE/$_REQUEST/$_SESSION/$GLOBALS。
public function form(\Illuminate\Http\Request $request) { $name = $request->input('name'); $all = $request->all(); $sessionId = $request->cookie('sessionId'); $photo = $request->file('photo'); // Call getContent() to get the raw POST body, instead of file_get_contents('php://input') $rawContent = $request->getContent(); //... }
输出响应
响应通过 Illuminate\Http\Response
对象,兼容 echo/vardump()/print_r(),不能使用
dd()/exit()/die()/header()/setcookie()/http_response_code() 函数。
public function json() { return response()->json(['time' => time()])->header('header1', 'value1')->withCookie('c1', 'v1'); }
持久连接
单例连接
将驻留在内存中,建议为了更好的性能开启 持久连接
。
- 数据库连接,它
将
在断开后立即自动重连。
// config/database.php 'connections' => [ 'my_conn' => [ 'driver' => 'mysql', 'host' => env('DB_MY_CONN_HOST', 'localhost'), 'port' => env('DB_MY_CONN_PORT', 3306), 'database' => env('DB_MY_CONN_DATABASE', 'forge'), 'username' => env('DB_MY_CONN_USERNAME', 'forge'), 'password' => env('DB_MY_CONN_PASSWORD', ''), 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', 'strict' => false, 'options' => [ // Enable persistent connection \PDO::ATTR_PERSISTENT => true, ], ], ],
- Redis 连接,它
不会
在断开后立即自动重连,并且会抛出关于丢失连接的异常,下次连接时再重连。你需要在每次操作 Redis 之前确保SELECT DB
正确。
// config/database.php 'redis' => [ 'client' => env('REDIS_CLIENT', 'phpredis'), // It is recommended to use phpredis for better performance. 'default' => [ 'host' => env('REDIS_HOST', 'localhost'), 'password' => env('REDIS_PASSWORD', null), 'port' => env('REDIS_PORT', 6379), 'database' => 0, 'persistent' => true, // Enable persistent connection ], ],
关于内存泄漏
-
避免使用全局变量。如果必要,请手动清理或重置它们。
-
无限地向
static
/global
变量追加元素会导致 OOM(内存不足)。class Test { public static $array = []; public static $string = ''; } // Controller public function test(Request $req) { // Out of Memory Test::$array[] = $req->input('param1'); Test::$string .= $req->input('param2'); }
-
内存泄漏检测方法
-
修改
config/laravels.php
:worker_num=1, max_request=1000000
,测试后请记得改回; -
添加路由
/debug-memory-leak
,无需route middleware
,以观察Worker
进程的内存变化;
Route::get('/debug-memory-leak', function () { global $previous; $current = memory_get_usage(); $stats = [ 'prev_mem' => $previous, 'curr_mem' => $current, 'diff_mem' => $current - $previous, ]; $previous = $current; return $stats; });
-
启动
LaravelS
并请求/debug-memory-leak
,直到diff_mem
小于或等于零;如果diff_mem
总是大于零,则意味着可能在Global Middleware
或Laravel 框架
中存在内存泄漏; -
完成
第3步
后,交替
请求业务路由和/debug-memory-leak
(建议使用ab
/wrk
对业务路由进行大量请求),内存的初始增加是正常的。在大量请求业务路由后,如果diff_mem
总是大于零并且curr_mem
继续增加,则高概率存在内存泄漏;如果curr_mem
总是在一定范围内变化而不继续增加,则低概率存在内存泄漏。 -
如果你仍然无法解决问题,max_request 是最后的保障。
-