เอกสารภาษาอังกฤษ | 中文档
LaravelS เป็นอะแดปเตอร์ที่แกะกล่องระหว่าง Laravel/Lumen และ Swoole
Watch
พื้นที่เก็บข้อมูลนี้เพื่อรับการอัปเดตล่าสุดเซิร์ฟเวอร์ Http/WebSocket ในตัว
โปรโตคอลผสมหลายพอร์ต
กระบวนการที่กำหนดเอง
ถิ่นที่อยู่ของหน่วยความจำ
การฟังเหตุการณ์แบบอะซิงโครนัส
คิวงานแบบอะซิงโครนัส
งาน cron มิลลิวินาที
ส่วนประกอบทั่วไป
โหลดซ้ำอย่างสง่างาม
โหลดซ้ำโดยอัตโนมัติหลังจากแก้ไขโค้ด
รองรับทั้ง Laravel/Lumen เข้ากันได้ดี
เรียบง่ายและนอกกรอบ
กรอบงานเว็บที่เร็วที่สุดคืออะไร?
เกณฑ์มาตรฐานกรอบงาน TechEmpower
การพึ่งพาอาศัยกัน | ความต้องการ |
---|---|
PHP | >=8.2 Recommend 8.2 |
สวูล | >=5.0 Recommend 5.1.1 |
ลาราเวล/ลูเมน | >=10 Recommend 10 |
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+ 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
: Listen_ip, Listen_port, อ้างอิงการตั้งค่า
5. การปรับแต่งประสิทธิภาพ
ปรับพารามิเตอร์เคอร์เนล
จำนวนพนักงาน: LaravelS ใช้โหมด Synchronous IO
ของ Swoole ยิ่งการตั้งค่า worker_num
มากเท่าใด ประสิทธิภาพการทำงานพร้อมกันก็จะยิ่งดีขึ้นเท่านั้น แต่จะทำให้มีการใช้หน่วยความจำมากขึ้นและโอเวอร์เฮดการสลับกระบวนการ หากคำขอหนึ่งรายการใช้เวลา 100 มิลลิวินาที เพื่อให้เกิดการทำงานพร้อมกัน 1,000QPS จะต้องกำหนดค่ากระบวนการของผู้ปฏิบัติงานอย่างน้อย 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 จนกว่าการเริ่มต้นจะเสร็จสมบูรณ์ |
โหลดซ้ำ | รีโหลดกระบวนการ Task/Worker/Timer ทั้งหมดซึ่งมีรหัสธุรกิจของคุณ และทริกเกอร์วิธี onReload กระบวนการที่กำหนดเอง ไม่สามารถโหลดกระบวนการ Master/Manger ใหม่ได้ หลังจากแก้ไข config/laravels.php คุณจะต้องเรียก restart เพื่อรีสตาร์ท only |
ข้อมูล | แสดงข้อมูลเวอร์ชันส่วนประกอบ |
ช่วย | แสดงข้อมูลความช่วยเหลือ |
start
และ restart
ตัวเลือก | คำอธิบาย |
---|---|
-d|--ปีศาจ | ทำงานเป็น daemon ตัวเลือกนี้จะแทนที่การตั้งค่า 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 >
ที่อยู่ Listening ของ WebSocket Sever เหมือนกับ Http Server
1. สร้างคลาส WebSocket Handler และใช้อินเทอร์เฟซ 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 นอกจากนี้ คุณยังสามารถใช้บริการจัดเก็บข้อมูลส่วนกลางอื่นๆ ได้ เช่น Redis/Memcached/MySQL แต่ระวังว่า FD อาจขัดแย้งกันระหว่าง Swoole Servers
หลายเครื่อง
4. ร่วมมือกับ Nginx (แนะนำ)
อ้างอิง WebSocket Proxy
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 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
to 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
งาน cron ของ Wrapper บน Millisecond Timer ของ Swoole แทนที่
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
// 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
เริ่มรองรับกระบวนการ hot restart [Reload] 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
เมื่อวิธีการข้างต้นไม่ได้ผล วิธีแก้ปัญหาขั้นสูงสุด: set 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 ,
//...
];
ทดสอบ.
TCP: 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 อย่างไรก็ตาม มีแอตทริบิวต์ singleton และ static มากมายใน Laravel/Lumen ข้อมูลระหว่างคำขอที่ต่างกันจะส่งผลต่อกันและกัน นั่นคือ Unsafe
ตัวอย่างเช่น การเชื่อมต่อฐานข้อมูลเป็นแบบซิงเกิลตัน การเชื่อมต่อฐานข้อมูลเดียวกันจะใช้ทรัพยากร PDO เดียวกันร่วมกัน ซึ่งเป็นเรื่องปกติในโหมดการบล็อกแบบซิงโครนัส แต่จะไม่ทำงานในโหมดโครูทีนแบบอะซิงโครนัส แบบสอบถามแต่ละรายการจำเป็นต้องสร้างการเชื่อมต่อที่แตกต่างกันและรักษาสถานะ IO ของการเชื่อมต่อที่แตกต่างกัน ซึ่งต้องใช้พูลการเชื่อมต่อ
DO NOT
เปิดใช้งาน Coroutine เฉพาะกระบวนการที่กำหนดเองเท่านั้นที่สามารถใช้ Coroutine ได้
สนับสนุนนักพัฒนาเพื่อสร้างกระบวนการทำงานพิเศษสำหรับการติดตาม รายงาน หรืองานพิเศษอื่นๆ อ้างถึงกระบวนการเพิ่ม
สร้างคลาส Process ใช้ 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
],
],
หมายเหตุ: callback() ไม่สามารถออกได้ หากออก กระบวนการของผู้จัดการจะสร้างกระบวนการขึ้นมาใหม่
ตัวอย่าง: เขียนข้อมูลไปยังกระบวนการแบบกำหนดเอง
// 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-เซิร์ฟเวอร์=http://127.0.0.1:8080 |
อพอลโล-app-id | รหัสแอปอพอลโล | - | --apollo-app-id=LARAVEL-S-TEST |
อพอลโล-เนมสเปซ | เนมสเปซที่ APP อยู่ รองรับการระบุหลายรายการ | แอปพลิเคชัน | --apollo-namespaces=application --apollo-namespaces=env |
อพอลโลคลัสเตอร์ | คลัสเตอร์ที่ APP อยู่ | ค่าเริ่มต้น | --apollo-cluster=default |
อพอลโล-ไคลเอนต์-ip | IP ของอินสแตนซ์ปัจจุบันสามารถใช้สำหรับการเผยแพร่ระดับสีเทาได้เช่นกัน | IP อินทราเน็ตท้องถิ่น | --apollo-ไคลเอนต์-ip=10.2.1.83 |
อพอลโลดึงหมดเวลา | เวลาหมดเวลา (วินาที) เมื่อดึงการกำหนดค่า | 5 | --apollo-pull-หมดเวลา=5 |
อพอลโลสำรอง-เก่า-env | ไม่ว่าจะสำรองไฟล์การกำหนดค่าเก่าเมื่อทำการอัพเดตไฟล์การกำหนดค่า .env | เท็จ | --apollo-สำรอง-เก่า-env |
รองรับการตรวจสอบและการเตือนของ 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
คุณจะต้องโหลดการกำหนดค่าด้วยตนเอง $app->configure('prometheus');
ใน bootstrap/app.php
กำหนดค่ามิดเดิลแวร์ global
: Hhxsv5LaravelSComponentsPrometheusRequestMiddleware::class
เพื่อที่จะนับการใช้เวลาของคำขอได้อย่างแม่นยำที่สุดเท่าที่จะเป็นไปได้ RequestMiddleware
จะต้องเป็นมิดเดิลแวร์ระดับโลก first
ซึ่งจะต้องวางไว้หน้ามิดเดิลแวร์อื่น
ลงทะเบียน ServiceProvider: Hhxsv5LaravelSComponentsPrometheusServiceProvider::class
กำหนดค่า CollectorProcess ใน config/laravels.php
เพื่อรวบรวมตัววัดของกระบวนการ Swole 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 |
ผู้ปฏิบัติงานเริ่มต้น | Hhxsv5LaravelSSwooleEventsWorkerStartInterface | เกิดขึ้นหลังจากที่กระบวนการ Worker/Task เริ่มต้นขึ้น และการเริ่มต้น Laravel เสร็จสมบูรณ์ |
คนงานหยุด | 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 อินสแตนซ์ซิงเกิลตันจะถูกสร้างอินสแตนซ์และรีไซเคิลในทุกคำขอ โดยร้องขอ start=>instantiate instance=>request end=>recycled instance
ภายใต้ Swoole Server อินสแตนซ์ซิงเกิลตันทั้งหมดจะถูกเก็บไว้ในหน่วยความจำ โดยมีอายุการใช้งานที่แตกต่างจาก FPM คำขอ start=>อินสแตนซ์อินสแตนซ์=>คำขอสิ้นสุด=>อย่ารีไซเคิลอินสแตนซ์ซิงเกิลตัน ดังนั้นต้องการให้นักพัฒนารักษาสถานะของอินสแตนซ์เดี่ยวในทุกคำขอ
วิธีแก้ปัญหาทั่วไป:
เขียนคลาส 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
Object เข้ากันได้กับ 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
การทดสอบความดัน
เพย์พาล
บีทีซี
กีตี
เอ็มไอที