مستندات انجليزية | 中文文档
LaravelS هو محول جاهز بين Laravel/Lumen وSwoole
Watch
هذا المستودع للحصول على آخر التحديثات.خادم Http/WebSocket مدمج
بروتوكول مختلط متعدد المنافذ
عملية مخصصة
ساكن الذاكرة
الاستماع إلى الأحداث غير المتزامنة
قائمة انتظار المهام غير المتزامنة
وظيفة كرون ميلي ثانية
المكونات المشتركة
إعادة التحميل برشاقة
إعادة التحميل تلقائيًا بعد تعديل الكود
يدعم Laravel/Lumen كلاهما، توافق جيد
بسيطة وخارج الصندوق
ما هو أسرع إطار ويب؟
معايير إطار TechEmpower
التبعية | متطلبات |
---|---|
PHP | >=8.2 Recommend 8.2 |
سوول | >=5.0 Recommend 5.1.1 |
لارافيل/لومين | >=10 Recommend 10 |
1.تتطلب الحزمة عبر الملحن (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+ supports package discovery automatically, you should skip this step
' 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، استمع_منفذ، راجع الإعدادات.
5. ضبط الأداء
ضبط معلمات النواة
عدد العمال: يستخدم LaravelS وضع Synchronous IO
الخاص بـ Swoole، وكلما زاد إعداد worker_num
، كان أداء التزامن أفضل، ولكنه سيؤدي إلى زيادة استخدام الذاكرة وتبديل العمليات. إذا استغرق طلب واحد 100 مللي ثانية، لتوفير تزامن 1000QPS، فيجب تكوين 100 عملية عاملة على الأقل. طريقة الحساب هي:worker_num = 1000QPS/(1s/1ms) = 100، لذلك يلزم إجراء اختبار الضغط المتزايد لحساب أفضل worker_num
.
عدد العاملين في المهمة
Please read the notices carefully before running
، ملاحظات مهمة (هامة).
php bin/laravels {start|stop|restart|reload|info|help}
.يأمر | وصف |
---|---|
يبدأ | ابدأ LaravelS، وأدرج العمليات حسب " ps -ef|grep laravels " |
قف | أوقف LaravelS، وقم بتشغيل الطريقة onStop العملية المخصصة |
إعادة تشغيل | أعد تشغيل LaravelS: توقف برشاقة قبل البدء؛ الخدمة unavailable حتى اكتمال بدء التشغيل |
إعادة تحميل | أعد تحميل جميع عمليات المهام/العامل/المؤقت التي تحتوي على رموز عملك، وقم بتشغيل الطريقة onReload العملية المخصصة، ولا يمكن إعادة تحميل العمليات الرئيسية/المدير. بعد تعديل config/laravels.php ، ما عليك only استدعاء restart لإعادة التشغيل |
معلومات | عرض معلومات إصدار المكون |
يساعد | عرض معلومات المساعدة |
start
وإعادة restart
.خيار | وصف |
---|---|
-d|--شيطنة | التشغيل كبرنامج خفي، سيتجاوز هذا الخيار إعداد swoole.daemonize في laravels.php |
-e|--env | البيئة التي يجب تشغيل الأمر فيها، مثل --env=testing ستستخدم ملف التكوين .env.testing أولاً، هذه الميزة تتطلب Laravel 5.2+ |
-i|--تجاهل | تجاهل التحقق من ملف PID للعملية الرئيسية |
-x|--x-الإصدار | إصدار (فرع) المشروع الحالي، المخزن في $_ENV/$_SERVER، يمكن الوصول إليه عبر $_ENV['X_VERSION'] $_SERVER['X_VERSION'] $request->server->get('X_VERSION') |
Runtime
: start
سيقوم تلقائيًا بتنفيذ php artisan laravels config
وإنشاء هذه الملفات، ولا يحتاج المطورون عمومًا إلى الاهتمام بها، فمن المستحسن إضافتها إلى .gitignore
.ملف | وصف |
---|---|
تخزين/laravels.conf | ملف تكوين runtime LaravelS |
تخزين/laravels.pid | ملف PID للعملية الرئيسية |
تخزين/laravels-timer-process.pid | ملف PID لعملية المؤقت |
تخزين/laravels-custom-processes.pid | ملف PID لجميع العمليات المخصصة |
يوصى بالإشراف على العملية الرئيسية من خلال 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
تجريبي.
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;
}
}
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 Sever هو نفس عنوان الاستماع لخادم 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 ومعرف المستخدم، وهو عرض اختياري لجدول Swoole. يمكنك أيضًا استخدام خدمات التخزين العالمية الأخرى، مثل Redis/Memcached/MySQL، ولكن كن حذرًا من احتمال حدوث تعارض بين FD Swoole Servers
المتعددة.
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.Push البيانات في وحدة التحكم
namespace App Http Controllers ;
class TestController extends Controller
{
public function push ()
{
$ fd = 1 ; // Find fd by userId from a map [userId=>fd].
/**@var SwooleWebSocketServer $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 بتحليل SwooleHttpRequest
إلى IlluminateHttpRequest
، قبل أن يعالج Kernel الخاص بـ Laravel هذا الطلب.
// 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 بمعالجة الطلب، قبل أن تقوم LaravelS بتحليل IlluminateHttpResponse
إلى SwooleHttpResponse
.
// 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
});
تعتمد هذه الميزة على
AsyncTask
فيSwoole
، حيث تحتاج إلى تعيينswoole.task_worker_num
فيconfig/laravels.php
أولاً. يتأثر أداء معالجة الأحداث غير المتزامنة بعدد عمليات مهمة 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
تعتمد هذه الميزة على
AsyncTask
فيSwoole
، حيث تحتاج إلى تعيينswoole.task_worker_num
فيconfig/laravels.php
أولاً. يتأثر أداء معالجة المهام بعدد عمليات مهمة 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
قاعدة عمل Wrapper cron على Swoole's Milli Second Timer، تحل محل
Linux
Crontab
.
1. إنشاء فئة عمل كرون.
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. سجل وظيفة كرون.
// 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
// Hhxsv5LaravelSIlluminateLaravelScheduleJob::class,
// Two ways to configure parameters:
// [AppJobsTimerTestCronJob::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.com/docs/7.x/redis
' global_lock ' => false ,
' global_lock_key ' => config ( ' app.name ' , ' Laravel ' ),
],
// ...
];
3.ملاحظة: سيتم تشغيل مؤقتات متعددة عند إنشاء مجموعة الخادم، لذلك تحتاج إلى التأكد من تشغيل مؤقت واحد فقط لتجنب تشغيل مهمة متكررة.
4. يبدأ LaravelS v3.4.0
في دعم عملية المؤقت [إعادة التحميل] Timer
التشغيل السريع. بعد أن يستقبل LaravelS إشارة SIGUSR1
، فإنه ينتظر max_wait_time
(افتراضي 5) ثواني لإنهاء العملية، ثم ستقوم عملية Manager
بسحب عملية Timer
مرة أخرى.
5. إذا كنت تحتاج فقط إلى استخدام المهام المجدولة minute-level
، فمن المستحسن تمكين Hhxsv5LaravelSIlluminateLaravelScheduleJob
بدلاً من 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. الحل المتشرد.
عبر fswatch
، يدعم نظام التشغيل OS X/Linux/Windows.
1. قم بتثبيت fswatch.
2. قم بتشغيل الأمر في الدليل الجذر لمشروعك.
# Watch current directory
./bin/fswatch
# Watch app directory
./bin/fswatch ./app
عبر inotifywait
، دعم Linux.
1. قم بتثبيت أدوات inotify.
2. قم بتشغيل الأمر في الدليل الجذر لمشروعك.
# Watch current directory
./bin/inotify
# Watch app directory
./bin/inotify ./app
عندما لا تعمل الطرق المذكورة أعلاه، الحل النهائي: قم بتعيين max_request=1,worker_num=1
بحيث يتم إعادة تشغيل عملية Worker
بعد معالجة الطلب. أداء هذه الطريقة ضعيف جدًا، so only development environment use
.
SwooleServer
في مشروعك /**
* $swoole is the instance of `SwooleWebSocketServer` if enable WebSocket server, otherwise `SwooleHttpServer`
* @var SwooleWebSocketServer|SwooleHttpServer $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.Access 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 SwooleTable $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 Server AddListener
لجعل خادمنا الرئيسي يدعم المزيد من البروتوكولات، وليس فقط Http وWebSocket، قمنا بإحضار ميزة multi-port mixed protocol
لـ Swoole في LaravelS ونطلق عليها اسم Socket
. الآن، يمكنك إنشاء تطبيقات TCP/UDP
بسهولة أعلى Laravel.
أنشئ فئة معالج Socket
وقم بتوسيع Hhxsv5LaravelSSwooleSocket{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 rn" ) {
$ 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
الخاصة بك. لذلك لن تكون هناك مشكلة على الإطلاق إذا كنت تريد تسليم المهام، استخدم SwooleTable
، وحتى مكونات Laravel مثل DB وEloquent وما إلى ذلك. وفي الوقت نفسه، يمكنك الوصول إلى كائن SwooleServerPort
مباشرة عن طريق خاصية العضو swoolePort
.
public function onReceive ( Server $ server , $ fd , $ reactorId , $ data )
{
$ port = $ this -> swoolePort ; // Get the `SwooleServerPort` object
}
namespace App Http Controllers ;
class TestController extends Controller
{
public function test ()
{
/**@var SwooleHttpServer|SwooleWebSocketServer $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 `SwooleServerPort` 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');
// }
}
}
}
تسجيل المقابس.
// 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 ' => "rn" ,
],
' handler ' => App Sockets TestTcpSocket::class,
' enable ' => true , // whether to enable, default true
],
],
فيما يتعلق بتكوين نبضات القلب، لا يمكن ضبطه إلا على main server
ولا يمكن تهيئته على Socket
، لكن Socket
يرث تكوين نبضات القلب main server
.
بالنسبة لمقبس TCP، سيتم حظر حدثي onConnect
و onClose
عندما يكون dispatch_mode
لـ Swoole هو 1/3
، لذا إذا كنت تريد إلغاء حظر هذين الحدثين، فيرجى ضبط dispatch_mode
على 2/4/5
.
' swoole ' => [
//...
' dispatch_mode ' => 2 ,
//...
];
امتحان.
تي سي بي: telnet 127.0.0.1 5291
UDP: [Linux] echo "Hello LaravelS" > /dev/udp/127.0.0.1/5292
تسجيل مثال على البروتوكولات الأخرى.
' sockets ' => [
[
' host ' => ' 0.0.0.0 ' ,
' port ' => 5292 ,
' type ' => SWOOLE_SOCK_UDP ,
' settings ' => [
' open_eof_check ' => true ,
' package_eof ' => "rn" ,
],
' handler ' => App Sockets TestUdpSocket::class,
],
],
' sockets ' => [
[
' host ' => ' 0.0.0.0 ' ,
' port ' => 5293 ,
' type ' => SWOOLE_SOCK_TCP ,
' settings ' => [
' open_http_protocol ' => true ,
],
' handler ' => App Sockets TestHttp::class,
],
],
turn on 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,
],
],
سوول كوروتين
تحذير: ترتيب تنفيذ التعليمات البرمجية في coroutine معطل. يجب عزل بيانات مستوى الطلب بواسطة معرف coroutine. ومع ذلك، هناك العديد من السمات المفردة والثابتة في Laravel/Lumen، وستؤثر البيانات بين الطلبات المختلفة على بعضها البعض، وهذا Unsafe
. على سبيل المثال، اتصال قاعدة البيانات هو اتصال مفرد، ونفس اتصال قاعدة البيانات يشترك في نفس مورد PDO. يعد هذا أمرًا جيدًا في وضع الحظر المتزامن، لكنه لا يعمل في وضع coroutine غير المتزامن. يحتاج كل استعلام إلى إنشاء اتصالات مختلفة والحفاظ على حالة الإدخال/الإخراج لاتصالات مختلفة، الأمر الذي يتطلب تجمع اتصالات.
DO NOT
بتمكين الكوروتين، فقط العملية المخصصة يمكنها استخدام الكوروتين.
دعم المطورين لإنشاء عمليات عمل خاصة للمراقبة أو إعداد التقارير أو المهام الخاصة الأخرى. راجع عملية الإضافة.
إنشاء فئة Proccess، وتنفيذ CustomProcessInterface.
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: SwooleRuntime::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
}
}
تسجيل عملية الاختبار.
// 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
],
],
ملاحظة: لا يمكن إنهاء رد الاتصال (). في حالة الإنهاء، ستقوم عملية المدير بإعادة إنشاء العملية.
مثال: كتابة البيانات إلى عملية مخصصة.
// 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 SwooleProcess[] $process */
$ customProcesses = Hhxsv5 LaravelS LaravelS:: getCustomProcesses ();
$ process = $ customProcesses [ ' test ' ];
$ process -> write ( ' TestController: write data ' . time ());
var_dump ( $ process -> read ());
}
سيقوم
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 (),
قائمة المعلمات المتاحة.
المعلمة | وصف | تقصير | تجريبي |
---|---|---|---|
خادم أبولو | عنوان URL لخادم أبولو | - | --apollo-server=http://127.0.0.1:8080 |
معرف تطبيق أبولو | معرف التطبيق أبولو | - | --apollo-app-id=LARAVEL-S-TEST |
مساحات الأسماء أبولو | مساحة الاسم التي ينتمي إليها التطبيق، تدعم تحديد المضاعف | طلب | --apollo-namespaces=application --apollo-namespaces=env |
أبولو العنقودية | المجموعة التي ينتمي إليها التطبيق | تقصير | --apollo-cluster=default |
أبولو العميل الملكية الفكرية | يمكن أيضًا استخدام عنوان IP للمثيل الحالي للنشر بالتدرج الرمادي | IP للإنترانت المحلية | --apollo-client-ip=10.2.1.83 |
أبولو سحب المهلة | المهلة الزمنية (بالثواني) عند سحب التكوين | 5 | --apollo-pull-timeout=5 |
أبولو النسخ الاحتياطي القديم-env | ما إذا كان سيتم عمل نسخة احتياطية لملف التكوين القديم عند تحديث ملف التكوين .env | خطأ شنيع | --apollo-backup-old-env |
دعم مراقبة وإنذار بروميثيوس، وعرض 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
، فستحتاج أيضًا إلى تحميل التكوين يدويًا $app->configure('prometheus');
في bootstrap/app.php
.
تكوين البرامج الوسيطة global
: Hhxsv5LaravelSComponentsPrometheusRequestMiddleware::class
. من أجل حساب استهلاك وقت الطلب بأكبر قدر ممكن من الدقة، يجب أن تكون RequestMiddleware
هي first
برمجيات وسيطة عالمية، والتي يجب وضعها أمام البرامج الوسيطة الأخرى.
تسجيل مزود الخدمة: Hhxsv5LaravelSComponentsPrometheusServiceProvider::class
.
قم بتكوين CollectorProcess في config/laravels.php
لتجميع مقاييس عمليات 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.io/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، ثم قم باستيراد لوحة json.
الأحداث المدعومة:
حدث | واجهة | عندما حدث |
---|---|---|
تشغيل الخادم | Hhxsv5LaravelSSwooleEventsServerStartInterface | يحدث عند بدء العملية الرئيسية، this event should not handle complex business logic, and can only do some simple work of initialization . |
توقف الخادم | Hhxsv5LaravelSSwooleEventsServerStopInterface | يحدث عندما يخرج الخادم بشكل طبيعي، CANNOT use async or coroutine related APIs in this event . |
WorkerStart | Hhxsv5LaravelSSwooleEventsWorkerStartInterface | يحدث بعد بدء عملية العامل/المهمة، واكتمال تهيئة Laravel. |
WorkerStop | Hhxsv5LaravelSSwooleEventsWorkerStopInterface | يحدث بعد خروج عملية العامل/المهمة بشكل طبيعي |
خطأ في العمل | Hhxsv5LaravelSSwooleEventsWorkerErrorInterface | يحدث عند حدوث استثناء أو خطأ فادح في عملية العامل/المهمة |
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. قم بإنشاء برنامج نصي لـ laravels_bootstrap
ومنحه executable permission
.
#! /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
لتنظيف حالة الكائن المفرد. تطبق هذه الفئة الواجهة Hhxsv5LaravelSIlluminateCleanersCleanerInterface
ثم تسجلها في cleaners
laravels.php
.
Reset
حالة المثيلات المفردة بواسطة Middleware
.
أعد تسجيل ServiceProvider
، وأضف XxxServiceProvider
إلى register_providers
في الملف laravels.php
. بحيث يتم إعادة تهيئة المثيلات المفردة في كل طلب مرجع.
منظفات التكوين
المشكلات المعروفة: حزمة من المشكلات والحلول المعروفة.
التسجيل؛ إذا كنت تريد الإخراج إلى وحدة التحكم، فيمكنك استخدام stderr
و Log::channel('stderr')->debug('debug message').
Laravel Dump Server (تم دمج Laravel 5.7 افتراضيًا).
قراءة الطلب بواسطة IlluminateHttpRequest
Object، $_ENV قابل للقراءة، $_SERVER قابل للقراءة جزئيًا، CANNOT USE
$_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 ();
//...
}
الرد بواسطة كائن IlluminateHttpResponse
، المتوافق مع echo/vardump()/print_r()، CANNOT USE
الوظائف dd()/exit()/die()/header()/setcookie()/http_response_code().
public function json ()
{
return response ()-> json ([ ' time ' => time ()])-> header ( ' header1 ' , ' value1 ' )-> withCookie ( ' c1 ' , ' v1 ' );
}
سيكون Singleton connection
موجودًا في الذاكرة، ومن المستحسن تشغيل persistent connection
للحصول على أداء أفضل.
will
إعادة الاتصال تلقائيًا immediately
قطع الاتصال. // 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 ,
],
],
],
won't
إعادة الاتصال تلقائيًا بعد قطع الاتصال immediately
، وسيطرح استثناءً بشأن فقدان الاتصال، وأعد الاتصال في المرة القادمة. تحتاج إلى التأكد من SELECT DB
بشكل صحيح قبل تشغيل Redis في كل مرة. // 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 Framework
؛
بعد إكمال Step 3
، اطلب مسارات الأعمال و /debug-memory-leak
alternately
(يوصى باستخدام ab
/ wrk
لتقديم عدد كبير من الطلبات لمسارات الأعمال)، وتكون الزيادة الأولية في الذاكرة أمرًا طبيعيًا. بعد عدد كبير من طلبات مسارات الأعمال، إذا كان diff_mem
دائمًا أكبر من الصفر ويستمر curr_mem
في الزيادة، فهناك احتمال كبير لتسرب الذاكرة؛ إذا تغير curr_mem
دائمًا ضمن نطاق معين ولم يستمر في الزيادة، فهناك احتمال منخفض لتسرب الذاكرة.
إذا كنت لا تزال غير قادر على حل المشكلة، فإن max_request هو الضمان الأخير.
تعديل معلمة Linux kernel
اختبار الضغط
باي بال
بيتكوين
جيتي
معهد ماساتشوستس للتكنولوجيا