Documents en anglais | 中文文档
LaravelS est un adaptateur prêt à l'emploi entre Laravel/Lumen et Swoole
Watch
ce référentiel pour obtenir les dernières mises à jour.Serveur Http/WebSocket intégré
Protocole mixte multiport
Processus personnalisé
Résident de mémoire
Écoute d'événements asynchrones
File d'attente de tâches asynchrone
Tâche cron en milliseconde
Composants communs
Recharger gracieusement
Recharger automatiquement après avoir modifié le code
Supporte Laravel/Lumen les deux, bonne compatibilité
Simple et prêt à l'emploi
Quel est le framework Web le plus rapide ?
Benchmarks du cadre TechEmpower
Dépendance | Exigence |
---|---|
PHP | >=8.2 Recommend 8.2 |
Swoole | >=5.0 Recommend 5.1.1 |
Laravel/Lumen | >=10 Recommend 10 |
1. Nécessite un package via 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.Enregistrez le fournisseur de services (choisissez-en un parmi deux).
Laravel
: dans le fichier config/app.php
, Laravel 5.5+ supports package discovery automatically, you should skip this step
' providers ' => [
//...
Hhxsv5 LaravelS Illuminate LaravelSServiceProvider::class,
],
Lumen
: dans le fichier bootstrap/app.php
$ app -> register ( Hhxsv5 LaravelS Illuminate LaravelSServiceProvider::class);
3.Publiez la configuration et les binaires.
Après la mise à niveau de LaravelS, vous devez republier ; cliquez ici pour voir les notes de modification de chaque version.
php artisan laravels publish
# Configuration: config/laravels.php
# Binary: bin/laravels bin/fswatch bin/inotify
4.Modifiez config/laravels.php
: Listen_ip, Listen_port, reportez-vous aux paramètres.
5. Réglage des performances
Ajuster les paramètres du noyau
Nombre de travailleurs : LaravelS utilise le mode Synchronous IO
de Swoole. Plus le paramètre worker_num
est grand, meilleures sont les performances de concurrence, mais cela entraînera une utilisation plus importante de la mémoire et une surcharge de commutation de processus. Si une requête prend 100 ms, afin de fournir une simultanéité de 1 000 QPS, au moins 100 processus Worker doivent être configurés. La méthode de calcul est la suivante : travailleur_num = 1000QPS/(1s/1ms) = 100, des tests de pression incrémentiels sont donc nécessaires pour calculer le meilleur worker_num
.
Nombre de travailleurs
Please read the notices carefully before running
, Avis importants (IMPORTANT).
php bin/laravels {start|stop|restart|reload|info|help}
.Commande | Description |
---|---|
commencer | Démarrez LaravelS, listez les processus par " ps -ef|grep laravels " |
arrêt | Arrêtez LaravelS et déclenchez la méthode onStop of Custom process |
redémarrage | Redémarrez LaravelS : arrêtez-vous gracieusement avant de commencer ; Le service unavailable tant que le démarrage n'est pas terminé |
recharger | Rechargez tous les processus Tâche/Travailleur/Minuteur qui contiennent vos codes métier et déclenchez la méthode onReload du processus personnalisé, NE PEUT PAS recharger les processus Maître/Gérant. Après avoir modifié config/laravels.php , il only d'appeler restart pour redémarrer |
infos | Afficher les informations sur la version du composant |
aide | Afficher les informations d'aide |
start
et restart
.Option | Description |
---|---|
-d|--démoniser | Exécuté en tant que démon, cette option remplacera le paramètre swoole.daemonize dans laravels.php |
-e|--env | L'environnement dans lequel la commande doit s'exécuter, tel que --env=testing utilisera d'abord le fichier de configuration .env.testing , cette fonctionnalité nécessite Laravel 5.2+ |
-i|--ignorer | Ignorer la vérification du fichier PID du processus maître |
-x|--x-version | La version(branche) du projet en cours, stockée dans $_ENV/$_SERVER, accès via $_ENV['X_VERSION'] $_SERVER['X_VERSION'] $request->server->get('X_VERSION') |
Runtime
: start
exécutera automatiquement php artisan laravels config
et générera ces fichiers, les développeurs n'ont généralement pas besoin d'y prêter attention, il est recommandé de les ajouter à .gitignore
.Déposer | Description |
---|---|
stockage/laravels.conf | Fichier de configuration runtime de LaravelS |
stockage/laravels.pid | Fichier PID du processus maître |
stockage/laravels-timer-process.pid | Fichier PID du processus Timer |
stockage/laravels-custom-processes.pid | Fichier PID de tous les processus personnalisés |
Il est recommandé de superviser le processus principal via Supervisord, le principe est sans option
-d
et de définirswoole.daemonize
surfalse
.
[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
Démo.
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 >
L'adresse d'écoute de WebSocket Server est la même que celle du serveur Http.
1.Créez la classe WebSocket Handler et implémentez l'interface WebSocketHandlerInterface
. L'instantané est automatiquement instancié au démarrage, vous n'avez pas besoin de le créer manuellement.
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.Modifiez 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.Utilisez SwooleTable
pour lier FD et UserId, en option, Swoole Table Demo. Vous pouvez également utiliser les autres services de stockage globaux, comme Redis/Memcached/MySQL, mais veillez à ce que FD puisse entrer en conflit entre plusieurs Swoole Servers
.
4.Coopérer avec Nginx (recommandé)
Référer le proxy 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. Réglage du rythme cardiaque
Réglage du rythme cardiaque de 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 ,
//...
],
Délai d'expiration de lecture du proxy de 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 données dans le contrôleur
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 );
}
}
Habituellement, vous pouvez réinitialiser/détruire certaines variables
global/static
, ou modifier l'objetRequest/Response
actuel.
laravels.received_request
Après que LaravelS ait analysé SwooleHttpRequest
en IlluminateHttpRequest
, avant que le noyau de Laravel ne gère cette requête.
// 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
Après que le noyau de Laravel ait traité la requête, avant que LaravelS n'analyse IlluminateHttpResponse
en 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
});
Cette fonctionnalité dépend de
AsyncTask
deSwoole
, vous devez d'abord définirswoole.task_worker_num
dansconfig/laravels.php
. Les performances du traitement des événements asynchrones sont influencées par le nombre de processus de tâches Swoole, vous devez définir task_worker_num de manière appropriée.
1.Créez une classe d’événement.
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.Créez une classe d’écoute.
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.Événement d'incendie.
// 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
Cette fonctionnalité dépend de
AsyncTask
deSwoole
, vous devez d'abord définirswoole.task_worker_num
dansconfig/laravels.php
. Les performances du traitement des tâches sont influencées par le nombre de processus de tâches Swoole, vous devez définir task_worker_num de manière appropriée.
1.Créez une classe de tâches.
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.Livrer la tâche.
// 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
Base de tâches cron Wrapper sur Millisecond Timer de Swoole, remplace
Linux
Crontab
.
1.Créez une classe de tâches 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. Enregistrez la tâche 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.Remarque : il lancera plusieurs minuteries lors de la construction du cluster de serveurs. Vous devez donc vous assurer de lancer une seule minuterie pour éviter d'exécuter des tâches répétitives.
4.LaravelS v3.4.0
commence à prendre en charge le processus Timer
de redémarrage à chaud [Reload]. Une fois que LaravelS a reçu le signal SIGUSR1
, il attend max_wait_time
(par défaut 5) secondes pour terminer le processus, puis le processus Manager
relancera le processus Timer
.
5.Si vous n'avez besoin d'utiliser que des tâches planifiées minute-level
, il est recommandé d'activer Hhxsv5LaravelSIlluminateLaravelScheduleJob
au lieu de Linux Crontab, afin que vous puissiez suivre les habitudes de codage de la planification des tâches Laravel et configurer 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 ();
}
Via inotify
, prend en charge Linux uniquement.
1.Installez l'extension inotify.
2.Activez le commutateur dans Paramètres.
3.Remarque : modifiez le fichier uniquement sous Linux
pour recevoir les événements de modification de fichier. Il est recommandé d'utiliser la dernière version de Docker. Solution vagabonde.
Via fswatch
, prend en charge OS X/Linux/Windows.
1.Installez fswatch.
2.Exécutez la commande dans le répertoire racine de votre projet.
# Watch current directory
./bin/fswatch
# Watch app directory
./bin/fswatch ./app
Via inotifywait
, prenez en charge Linux.
1.Installez les outils inotify.
2.Exécutez la commande dans le répertoire racine de votre projet.
# Watch current directory
./bin/inotify
# Watch app directory
./bin/inotify ./app
Lorsque les méthodes ci-dessus ne fonctionnent pas, la solution ultime : définissez max_request=1,worker_num=1
, afin que le processus Worker
redémarre après le traitement d'une demande. Les performances de cette méthode sont très mauvaises, so only development environment use
.
SwooleServer
dans votre projet /**
* $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. Définir le tableau, prendre en charge plusieurs.
Toutes les tables définies seront créées avant le démarrage de 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
: toutes les instances de table seront liées à SwooleServer
, accès par 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 }" );
}
}
Pour plus d'informations, veuillez vous référer à Swoole Server AddListener
Pour que notre serveur principal prenne en charge davantage de protocoles, pas seulement Http et WebSocket, nous apportons la fonctionnalité multi-port mixed protocol
de Swoole dans LaravelS et la nommons Socket
. Désormais, vous pouvez facilement créer des applications TCP/UDP
sur Laravel.
Créez une classe de gestionnaire Socket
et étendez 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 ' );
}
}
Ces connexions Socket
partagent les mêmes processus de travail avec vos connexions HTTP
/ WebSocket
. Ce ne sera donc pas du tout un problème si vous souhaitez effectuer des tâches, utilisez SwooleTable
, même des composants Laravel tels que DB, Eloquent, etc. Dans le même temps, vous pouvez accéder à l'objet SwooleServerPort
directement par la propriété membre 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');
// }
}
}
}
Enregistrez les sockets.
// Edit `config/laravels.php`
//...
' sockets ' => [
[
' host ' => ' 127.0.0.1 ' ,
' port ' => 5291 ,
' type ' => SWOOLE_SOCK_TCP , // Socket type: SWOOLE_SOCK_TCP/SWOOLE_SOCK_TCP6/SWOOLE_SOCK_UDP/SWOOLE_SOCK_UDP6/SWOOLE_UNIX_DGRAM/SWOOLE_UNIX_STREAM
' settings ' => [ // Swoole settings:https://www.swoole.co.uk/docs/modules/swoole-server-methods#swoole_server-addlistener
' open_eof_check ' => true ,
' package_eof ' => "rn" ,
],
' handler ' => App Sockets TestTcpSocket::class,
' enable ' => true , // whether to enable, default true
],
],
Concernant la configuration du rythme cardiaque, elle ne peut être définie que sur le main server
et ne peut pas être configurée sur Socket
, mais le Socket
hérite de la configuration du rythme cardiaque du main server
.
Pour le socket TCP, les événements onConnect
et onClose
seront bloqués lorsque dispatch_mode
de Swoole est 1/3
, donc si vous souhaitez débloquer ces deux événements, veuillez définir dispatch_mode
sur 2/4/5
.
' swoole ' => [
//...
' dispatch_mode ' => 2 ,
//...
];
Test.
TCP : telnet 127.0.0.1 5291
UDP : [Linux] echo "Hello LaravelS" > /dev/udp/127.0.0.1/5292
Enregistrez un exemple d’autres protocoles.
' 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
, c'est-à-dire définir websocket.enable
sur 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,
],
],
Swoole Coroutine
Attention : l'ordre d'exécution du code dans la coroutine est dans le désordre. Les données du niveau de requête doivent être isolées par l'ID de coroutine. Cependant, il existe de nombreux attributs singleton et statiques dans Laravel/Lumen, les données entre les différentes requêtes s'affecteront mutuellement, c'est Unsafe
. Par exemple, la connexion à la base de données est un singleton, la même connexion à la base de données partage la même ressource PDO. C'est bien en mode de blocage synchrone, mais cela ne fonctionne pas en mode coroutine asynchrone. Chaque requête doit créer différentes connexions et maintenir l'état IO de différentes connexions, ce qui nécessite un pool de connexions.
DO NOT
la coroutine, seul le processus personnalisé peut utiliser la coroutine.
Aidez les développeurs à créer des processus de travail spéciaux pour la surveillance, le reporting ou d'autres tâches spéciales. Reportez-vous à addProcess.
Créer une classe Process, implémente 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
}
}
Enregistrez TestProcess.
// Edit `config/laravels.php`
// ...
' processes ' => [
' test ' => [ // Key name is process name
' class ' => App Processes TestProcess::class,
' redirect ' => false , // Whether redirect stdin/stdout, true or false
' pipe ' => 0 , // The type of pipeline, 0: no pipeline 1: SOCK_STREAM 2: SOCK_DGRAM
' enable ' => true , // Whether to enable, default true
//'num' => 3 // To create multiple processes of this class, default is 1
//'queue' => [ // Enable message queue as inter-process communication, configure empty array means use default parameters
// 'msg_key' => 0, // The key of the message queue. Default: ftok(__FILE__, 1).
// 'mode' => 2, // Communication mode, default is 2, which means contention mode
// 'capacity' => 8192, // The length of a single message, is limited by the operating system kernel parameters. The default is 8192, and the maximum is 65536
//],
//'restart_interval' => 5, // After the process exits abnormally, how many seconds to wait before restarting the process, default 5 seconds
],
],
Remarque : Le callback() ne peut pas se fermer. S'il est arrêté, le processus Manager recréera le processus.
Exemple : Écrivez des données dans un processus personnalisé.
// 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
extraira la configurationApollo
et l'écrira dans le fichier.env
au démarrage. Dans le même temps,LaravelS
démarrera le processus personnaliséapollo
pour surveiller la configuration etreload
automatiquement lorsque la configuration change.
Activer Apollo : ajoutez les paramètres --enable-apollo
et Apollo aux paramètres de démarrage.
php bin/laravels start --enable-apollo --apollo-server=http://127.0.0.1:8080 --apollo-app-id=LARAVEL-S-TEST
Prise en charge des mises à jour à chaud (facultatif).
// 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 (),
Liste des paramètres disponibles.
Paramètre | Description | Défaut | Démo |
---|---|---|---|
serveur Apollo | URL du serveur Apollo | - | --apollo-server=http://127.0.0.1:8080 |
apollo-app-id | Identifiant de l'application Apollo | - | --apollo-app-id=LARAVEL-S-TEST |
espaces de noms Apollo | L'espace de noms auquel appartient l'APP, prend en charge la spécification du multiple | application | --apollo-namespaces=application --apollo-namespaces=env |
amas d'Apollon | Le cluster auquel appartient l'APP | défaut | --apollo-cluster=par défaut |
apollo-client-ip | IP de l'instance actuelle, peut également être utilisée pour la publication en niveaux de gris | IP de l'intranet local | --apollo-client-ip=10.2.1.83 |
apollo-pull-timeout | Délai d'expiration (secondes) lors de l'extraction de la configuration | 5 | --apollo-pull-timeout=5 |
apollo-sauvegarde-ancien-env | S'il faut sauvegarder l'ancien fichier de configuration lors de la mise à jour du fichier de configuration .env | FAUX | --apollo-backup-old-env |
Prend en charge la surveillance et l'alarme Prometheus, Grafana affiche visuellement les mesures de surveillance. Veuillez vous référer à Docker Compose pour la construction de l'environnement de Prometheus et Grafana.
Nécessite l'extension APCu >= 5.0.0, veuillez l'installer par pecl install apcu
.
Copiez le fichier de configuration prometheus.php
dans le répertoire config
de votre projet. Modifiez la configuration selon vos besoins.
# Execute commands in the project root directory
cp vendor/hhxsv5/laravel-s/config/prometheus.php config/
Si votre projet est Lumen
, vous devez également charger manuellement la configuration $app->configure('prometheus');
dans bootstrap/app.php
.
Configurez le middleware global
: Hhxsv5LaravelSComponentsPrometheusRequestMiddleware::class
. Afin de compter le plus précisément possible la consommation du temps de requête, RequestMiddleware
doit être le first
middleware global, qui doit être placé devant les autres middleware.
Enregistrez ServiceProvider : Hhxsv5LaravelSComponentsPrometheusServiceProvider::class
.
Configurez CollectorProcess dans config/laravels.php
pour collecter régulièrement les métriques des processus Swoole Worker/Task/Timer.
' processes ' => Hhxsv5 LaravelS Components Prometheus CollectorProcess:: getDefinition (),
Créez la route vers les métriques de sortie.
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 ]);
});
Terminez la configuration de Prometheus et démarrez-le.
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
Démarrez Grafana, puis importez le panel json.
Événements pris en charge :
Événement | Interface | Quand est-il arrivé |
---|---|---|
Démarrage du serveur | Hhxsv5LaravelSSwooleEventsServerStartInterface | Se produit au démarrage du processus maître, this event should not handle complex business logic, and can only do some simple work of initialization . |
Arrêt du serveur | Hhxsv5LaravelSSwooleEventsServerStopInterface | Se produit lorsque le serveur se termine normalement, CANNOT use async or coroutine related APIs in this event . |
Début du travailleur | Hhxsv5LaravelSSwooleEventsWorkerStartInterface | Se produit après le démarrage du processus Worker/Task et l'initialisation de Laravel terminée. |
Arrêt des travailleurs | Hhxsv5LaravelSSwooleEventsWorkerStopInterface | Se produit après la fin normale du processus Worker/Task |
Erreur de travailleur | Hhxsv5LaravelSSwooleEventsWorkerErrorInterface | Se produit lorsqu'une exception ou une erreur fatale se produit dans le processus Worker/Tâche |
1.Créez une classe d'événements pour implémenter l'interface correspondante.
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.Configuration.
// Edit `config/laravels.php`
' event_handlers ' => [
' ServerStart ' => [ App Events ServerStartEvent::class], // Trigger events in array order
' WorkerStart ' => [ App Events WorkerStartEvent::class],
],
Fonction Calcul.
1.Modifiez bootstrap/app.php
et définissez le répertoire de stockage. Le répertoire du projet étant en lecture seule, le répertoire /tmp
ne peut être lu et écrit que.
$ app -> useStoragePath ( env ( ' APP_STORAGE_PATH ' , ' /tmp/storage ' ));
2.Créez un script shell laravels_bootstrap
et accordez 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.Configurez 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
En mode FPM, les instances singleton seront instanciées et recyclées dans chaque requête, request start=>instantiate instance=>request end=>recycled instance.
Sous Swoole Server, toutes les instances singleton seront conservées en mémoire, durée de vie différente de celle de FPM, request start=>instancier l'instance=>request end=>ne pas recycler l'instance singleton. Il faut donc que le développeur conserve le statut des instances singleton dans chaque requête.
Solutions courantes :
Écrivez une classe XxxCleaner
pour nettoyer l'état de l'objet singleton. Cette classe implémente l'interface Hhxsv5LaravelSIlluminateCleanersCleanerInterface
puis l'enregistre dans cleaners
de laravels.php
.
Reset
l'état des instances singleton par Middleware
.
Réenregistrez ServiceProvider
, ajoutez XxxServiceProvider
dans register_providers
du fichier laravels.php
. Cela réinitialise donc les instances singleton à chaque requête. Reportez-vous.
Nettoyeurs de configuration.
Problèmes connus : un ensemble de problèmes connus et de solutions.
Enregistrement; si vous souhaitez afficher la console, vous pouvez utiliser stderr
, Log::channel('stderr')->debug('debug message').
Laravel Dump Server (Laravel 5.7 a été intégré par défaut).
Demande de lecture par l'objet IlluminateHttpRequest
, $_ENV est lisible, $_SERVER est partiellement lisible, 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 ();
//...
}
Répondez par IlluminateHttpResponse
Object, compatible avec echo/vardump()/print_r(), CANNOT USE
les fonctions dd()/exit()/die()/header()/setcookie()/http_response_code().
public function json ()
{
return response ()-> json ([ ' time ' => time ()])-> header ( ' header1 ' , ' value1 ' )-> withCookie ( ' c1 ' , ' v1 ' );
}
Singleton connection
résidera en mémoire, il est recommandé d'activer persistent connection
pour de meilleures performances.
will
reconnectera automatiquement immediately
après la déconnexion. // 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
se reconnectera pas automatiquement immediately
après la déconnexion et lèvera une exception concernant la perte de connexion, reconnectez-vous la prochaine fois. Vous devez vous assurer que SELECT DB
est correctement exécuté avant d'utiliser Redis à chaque fois. // 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
],
],
Évitez d'utiliser des variables globales. Si nécessaire, veuillez les nettoyer ou les réinitialiser manuellement.
L'ajout d'un élément à l'infini dans une variable static
/ global
entraînera un MOO (mémoire insuffisante).
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 ' );
}
Méthode de détection des fuites de mémoire
Modifiez config/laravels.php
: worker_num=1, max_request=1000000
, n'oubliez pas de le modifier après le test ;
Ajoutez un routage /debug-memory-leak
sans route middleware
pour observer les changements de mémoire du processus 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 ;
});
Démarrez LaravelS
et demandez /debug-memory-leak
jusqu'à ce que diff_mem
soit inférieur ou égal à zéro ; si diff_mem
est toujours supérieur à zéro, cela signifie qu'il peut y avoir une fuite de mémoire dans Global Middleware
ou Laravel Framework
;
Après avoir terminé Step 3
, demandez alternately
les routes commerciales et /debug-memory-leak
(il est recommandé d'utiliser ab
/ wrk
pour effectuer un grand nombre de demandes de routes commerciales), l'augmentation initiale de la mémoire est normale. Après un grand nombre de requêtes pour les routes métiers, si diff_mem
est toujours supérieur à zéro et curr_mem
continue d'augmenter, il existe une forte probabilité de fuite de mémoire ; Si curr_mem
change toujours dans une certaine plage et ne continue pas à augmenter, il existe une faible probabilité de fuite de mémoire.
Si vous ne parvenez toujours pas à le résoudre, max_request est la dernière garantie.
Ajustement des paramètres du noyau Linux
Essai de pression
Paypal
BTC
Gîte
MIT