Documentos em inglês | 中文文档
LaravelS é um adaptador pronto para uso entre Laravel/Lumen e Swoole
Watch
a este repositório para obter as atualizações mais recentes.Servidor Http/WebSocket integrado
Protocolo misto multiportas
Processo personalizado
Residente na memória
Escuta de eventos assíncronos
Fila de tarefas assíncronas
Tarefa cron de milissegundos
Componentes Comuns
Recarregue normalmente
Recarregar automaticamente após modificar o código
Suporta Laravel/Lumen ambos, boa compatibilidade
Simples e fora da caixa
Qual é o framework web mais rápido?
Benchmarks da estrutura TechEmpower
Dependência | Exigência |
---|---|
PHP | >=8.2 Recommend 8.2 |
Swoole | >=5.0 Recommend 5.1.1 |
Laravel/Lúmen | >=10 Recommend 10 |
1.Exigir pacote 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.Registre o provedor de serviços (escolha um dos dois).
Laravel
: no arquivo config/app.php
, Laravel 5.5+ supports package discovery automatically, you should skip this step
' providers ' => [
//...
Hhxsv5 LaravelS Illuminate LaravelSServiceProvider::class,
],
Lumen
: no arquivo bootstrap/app.php
$ app -> register ( Hhxsv5 LaravelS Illuminate LaravelSServiceProvider::class);
3.Publique configuração e binários.
Após atualizar o LaravelS, você precisa republicar; clique aqui para ver as notas de alteração de cada versão.
php artisan laravels publish
# Configuration: config/laravels.php
# Binary: bin/laravels bin/fswatch bin/inotify
4.Altere config/laravels.php
: listen_ip, listen_port, consulte Configurações.
5. Ajuste de desempenho
Ajustar os parâmetros do kernel
Número de trabalhadores: LaravelS usa o modo Synchronous IO
do Swoole, quanto maior a configuração worker_num
, melhor o desempenho de simultaneidade, mas causará mais uso de memória e sobrecarga de troca de processo. Se uma solicitação levar 100ms, para fornecer simultaneidade de 1000QPS, pelo menos 100 processos de trabalho precisarão ser configurados. O método de cálculo é: trabalhador_num = 1000QPS/(1s/1ms) = 100, portanto, testes de pressão incrementais são necessários para calcular o melhor worker_num
.
Número de trabalhadores de tarefas
Please read the notices carefully before running
. Avisos importantes (IMPORTANTE).
php bin/laravels {start|stop|restart|reload|info|help}
.Comando | Descrição |
---|---|
começar | Inicie o LaravelS, liste os processos por " ps -ef|grep laravels " |
parar | Pare o LaravelS e acione o método onStop do processo personalizado |
reiniciar | Reinicie o LaravelS: Pare normalmente antes de começar; O serviço fica unavailable até que a inicialização seja concluída |
recarregar | Recarregue todos os processos de Tarefa/Trabalhador/Temporizador que contêm seus códigos de negócios e acione o método onReload do processo Personalizado, NÃO PODE recarregar processos Mestre/Gerenciador. Depois de modificar config/laravels.php , você only precisa chamar restart para reiniciar |
informações | Exibir informações de versão do componente |
ajuda | Exibir informações de ajuda |
start
e restart
.Opção | Descrição |
---|---|
-d|--daemonizar | Executado como um daemon, esta opção substituirá a configuração swoole.daemonize em laravels.php |
-e|--env | O ambiente no qual o comando deve ser executado, como --env=testing usará o arquivo de configuração .env.testing primeiro, este recurso requer Laravel 5.2+ |
-eu|--ignorar | Ignorar a verificação do arquivo PID do processo mestre |
-x|--x-versão | A versão(branch) do projeto atual, armazenada em $_ENV/$_SERVER, acessada via $_ENV['X_VERSION'] $_SERVER['X_VERSION'] $request->server->get('X_VERSION') |
Runtime
: start
executará automaticamente php artisan laravels config
e gerará esses arquivos, os desenvolvedores geralmente não precisam prestar atenção a eles, é recomendado adicioná-los a .gitignore
.Arquivo | Descrição |
---|---|
armazenamento/laravels.conf | Arquivo de configuração runtime do LaravelS |
armazenamento/laravels.pid | Arquivo PID do processo mestre |
armazenamento/laravels-timer-process.pid | Arquivo PID do processo Timer |
armazenamento/laravels-custom-processes.pid | Arquivo PID de todos os processos personalizados |
Recomenda-se supervisionar o processo principal através do Supervisord, a premissa é sem a opção
-d
e definirswoole.daemonize
comofalse
.
[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
Demonstração.
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 >
O endereço de escuta do WebSocket Server é o mesmo do servidor Http.
1.Crie a classe WebSocket Handler e implemente a interface WebSocketHandlerInterface
. O instante é instanciado automaticamente ao iniciar, você não precisa criá-lo manualmente.
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.Modifique 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.Use SwooleTable
para vincular FD e UserId, opcional, Swoole Table Demo. Além disso, você pode usar outros serviços de armazenamento global, como Redis/Memcached/MySQL, mas tome cuidado, pois o FD poderá entrar em conflito entre vários Swoole Servers
.
4.Coopere com Nginx (recomendado)
Consulte o 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. Configuração de batimento cardíaco
Configuração de batimento cardíaco 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 ,
//...
],
Tempo limite de leitura do proxy do Nginx
# Nginx will close the connection if the proxied server does not send data to Nginx in 60 seconds
proxy_read_timeout 60s ;
6.Enviar dados no controlador
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 );
}
}
Normalmente, você pode redefinir/destruir algumas variáveis
global/static
ou alterar o objetoRequest/Response
atual.
laravels.received_request
Depois que o LaravelS analisou SwooleHttpRequest
para IlluminateHttpRequest
, antes que o Kernel do Laravel lide com essa solicitação.
// 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
Depois que o Kernel do Laravel tratou a solicitação, antes que o LaravelS analise IlluminateHttpResponse
para 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
});
Este recurso depende de
AsyncTask
deSwoole
, você precisa definirswoole.task_worker_num
emconfig/laravels.php
primeiro. O desempenho do processamento de eventos assíncronos é influenciado pelo número de processos de tarefa Swoole, você precisa definir task_worker_num apropriadamente.
1.Crie uma classe de evento.
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.Crie uma classe de ouvinte.
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. Evento de incêndio.
// 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
Este recurso depende de
AsyncTask
deSwoole
, você precisa definirswoole.task_worker_num
emconfig/laravels.php
primeiro. O desempenho do processamento de tarefas é influenciado pelo número de processos de tarefas Swoole, você precisa definir task_worker_num apropriadamente.
1.Crie uma classe de tarefa.
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. Entregar tarefa.
// Create instance of TestTask and deliver it, "deliver" is asynchronous.
use Hhxsv5 LaravelS Swoole Task Task ;
$ task = new TestTask ( ' task data ' );
// $task->delay(3);// delay 3 seconds to deliver task
// $task->setTries(3); // When an error occurs, try 3 times in total
$ ret = Task:: deliver ( $ task );
var_dump ( $ ret ); // Return true if sucess, otherwise false
Wrapper cron job baseado no temporizador de milissegundos do Swoole, substitua
Linux
Crontab
.
1.Crie uma classe de trabalho 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.Registre o cron job.
// 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.Observação: ele iniciará vários cronômetros ao construir o cluster de servidor, portanto, você precisa ter certeza de iniciar apenas um cronômetro para evitar a execução de tarefas repetitivas.
4.LaravelS v3.4.0
começa a suportar o processo de reinicialização a quente [Reload] Timer
. Após o LaravelS receber o sinal SIGUSR1
, ele aguarda max_wait_time
(padrão 5) segundos para finalizar o processo, então o processo Manager
irá puxar o processo Timer
novamente.
5.Se você precisar usar apenas tarefas agendadas minute-level
, é recomendado habilitar Hhxsv5LaravelSIlluminateLaravelScheduleJob
em vez do Linux Crontab, para que você possa seguir os hábitos de codificação do agendamento de tarefas do Laravel e configurar 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
, suporte apenas para Linux.
1.Instale a extensão inotify.
2.Ligue o interruptor em Configurações.
3.Aviso: Modifique o arquivo apenas no Linux
para receber os eventos de alteração do arquivo. É recomendado usar o Docker mais recente. Solução vagabunda.
Via fswatch
, suporte OS X/Linux/Windows.
1.Instale o fswatch.
2.Execute o comando no diretório raiz do seu projeto.
# Watch current directory
./bin/fswatch
# Watch app directory
./bin/fswatch ./app
Via inotifywait
, suporte Linux.
1.Instale ferramentas inotify.
2.Execute o comando no diretório raiz do seu projeto.
# Watch current directory
./bin/inotify
# Watch app directory
./bin/inotify ./app
Quando os métodos acima não funcionam, a solução final: defina max_request=1,worker_num=1
, para que o processo Worker
seja reiniciado após processar uma solicitação. O desempenho deste método é muito ruim, so only development environment use
.
SwooleServer
em seu projeto /**
* $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. Definir tabela, suporte múltiplo.
Todas as tabelas definidas serão criadas antes do início do 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
: todas as instâncias da tabela serão vinculadas em SwooleServer
, acesso por 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 }" );
}
}
Para obter mais informações, consulte Swoole Server AddListener
Para fazer com que nosso servidor principal suporte mais protocolos não apenas Http e WebSocket, trazemos o recurso multi-port mixed protocol
do Swoole no LaravelS e o nomeamos Socket
. Agora, você pode construir aplicativos TCP/UDP
facilmente no Laravel.
Crie uma classe manipuladora Socket
e estenda 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 ' );
}
}
Essas conexões Socket
compartilham os mesmos processos de trabalho com suas conexões HTTP
/ WebSocket
. Então não será problema nenhum se você quiser entregar tarefas, usar SwooleTable
, até mesmo componentes do Laravel como DB, Eloquent e assim por diante. Ao mesmo tempo, você pode acessar o objeto SwooleServerPort
diretamente pela propriedade do membro 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');
// }
}
}
}
Registrar soquetes.
// 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
],
],
Sobre a configuração de pulsação, ela só pode ser definida no main server
e não pode ser configurada no Socket
, mas o Socket
herda a configuração de pulsação do main server
.
Para o soquete TCP, os eventos onConnect
e onClose
serão bloqueados quando dispatch_mode
do Swoole for 1/3
, portanto, se você deseja desbloquear esses dois eventos, defina dispatch_mode
como 2/4/5
.
' swoole ' => [
//...
' dispatch_mode ' => 2 ,
//...
];
Teste.
TCP: telnet 127.0.0.1 5291
UDP: [Linux] echo "Hello LaravelS" > /dev/udp/127.0.0.1/5292
Cadastre exemplo de outros protocolos.
' 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
, ou seja, definir websocket.enable
como 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,
],
],
Corotina Swoole
Aviso: a ordem de execução do código na corrotina está fora de ordem. Os dados do nível de solicitação devem ser isolados pelo ID da corrotina. No entanto, existem muitos atributos singleton e estáticos no Laravel/Lumen, os dados entre diferentes solicitações afetarão uns aos outros, é Unsafe
. Por exemplo, a conexão com o banco de dados é singleton, a mesma conexão com o banco de dados compartilha o mesmo recurso PDO. Isso é bom no modo de bloqueio síncrono, mas não funciona no modo de co-rotina assíncrona. Cada consulta precisa criar conexões diferentes e manter o estado de E/S de conexões diferentes, o que requer um pool de conexões.
DO NOT
habilite a corrotina, apenas o processo personalizado pode usar a corrotina.
Apoie os desenvolvedores na criação de processos de trabalho especiais para monitoramento, relatórios ou outras tarefas especiais. Consulte addProcess.
Crie a classe Process, implemente 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
}
}
Registre-se 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
],
],
Nota: O callback() não pode ser encerrado. Se for encerrado, o processo Manager recriará o processo.
Exemplo: Grave dados em um processo personalizado.
// 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
irá extrair a configuraçãoApollo
e gravá-la no arquivo.env
ao iniciar. Ao mesmo tempo,LaravelS
iniciará o processo personalizadoapollo
para monitorar a configuração ereload
automaticamente quando a configuração for alterada.
Habilite o Apollo: adicione os parâmetros --enable-apollo
e Apollo aos parâmetros de inicialização.
php bin/laravels start --enable-apollo --apollo-server=http://127.0.0.1:8080 --apollo-app-id=LARAVEL-S-TEST
Suporta atualizações quentes (opcional).
// 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 (),
Lista de parâmetros disponíveis.
Parâmetro | Descrição | Padrão | Demonstração |
---|---|---|---|
servidor Apollo | URL do servidor Apollo | - | --apollo-server=http://127.0.0.1:8080 |
apollo-app-id | ID do aplicativo Apollo | - | --apollo-app-id=LARAVEL-S-TESTE |
namespaces Apollo | O namespace ao qual o APP pertence, suporte para especificar os múltiplos | aplicativo | --apollo-namespaces=aplicativo --apollo-namespaces=env |
aglomerado Apollo | O cluster ao qual o APP pertence | padrão | --apollo-cluster=padrão |
apollo-cliente-ip | IP da instância atual, também pode ser usado para publicação em escala de cinza | IP da intranet local | --apollo-client-ip=10.2.1.83 |
apolo-pull-timeout | Tempo limite (segundos) ao extrair a configuração | 5 | --apollo-pull-timeout=5 |
apollo-backup-old-env | Se deve fazer backup do arquivo de configuração antigo ao atualizar o arquivo de configuração .env | falso | --apollo-backup-old-env |
Suporta monitoramento e alarme do Prometheus, Grafana visualiza visualmente as métricas de monitoramento. Consulte o Docker Compose para a construção do ambiente de Prometheus e Grafana.
Requer extensão APCu >= 5.0.0, instale-a por pecl install apcu
.
Copie o arquivo de configuração prometheus.php
para o diretório config
do seu projeto. Modifique a configuração conforme apropriado.
# Execute commands in the project root directory
cp vendor/hhxsv5/laravel-s/config/prometheus.php config/
Se o seu projeto for Lumen
, você também precisará carregar manualmente a configuração $app->configure('prometheus');
em bootstrap/app.php
.
Configure o middleware global
: Hhxsv5LaravelSComponentsPrometheusRequestMiddleware::class
. Para contar o consumo de tempo de solicitação com a maior precisão possível, RequestMiddleware
deve ser o first
middleware global, que precisa ser colocado na frente de outros middlewares.
Registre ServiceProvider: Hhxsv5LaravelSComponentsPrometheusServiceProvider::class
.
Configure o CollectorProcess em config/laravels.php
para coletar as métricas dos processos Swoole Worker/Task/Timer regularmente.
' processes ' => Hhxsv5 LaravelS Components Prometheus CollectorProcess:: getDefinition (),
Crie a rota para gerar métricas.
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 ]);
});
Conclua a configuração do Prometheus e inicie-o.
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
Inicie o Grafana e importe o painel json.
Eventos suportados:
Evento | Interface | Quando aconteceu |
---|---|---|
Início do servidor | Hhxsv5LaravelSSwooleEventsServerStartInterface | Ocorre quando o processo Master está sendo iniciado, this event should not handle complex business logic, and can only do some simple work of initialization . |
Parada do servidor | Hhxsv5LaravelSSwooleEventsServerStopInterface | Ocorre quando o servidor sai normalmente, CANNOT use async or coroutine related APIs in this event . |
TrabalhadorStart | Hhxsv5LaravelSSwooleEventsWorkerStartInterface | Ocorre após o processo Worker/Task ser iniciado e a inicialização do Laravel ter sido concluída. |
Parada de trabalho | Hhxsv5LaravelSSwooleEventsWorkerStopInterface | Ocorre depois que o processo Trabalhador/Tarefa termina normalmente |
Erro do trabalhador | Hhxsv5LaravelSSwooleEventsWorkerErrorInterface | Ocorre quando ocorre uma exceção ou erro fatal no processo Trabalhador/Tarefa |
1.Crie uma classe de evento para implementar a interface correspondente.
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.Configuração.
// Edit `config/laravels.php`
' event_handlers ' => [
' ServerStart ' => [ App Events ServerStartEvent::class], // Trigger events in array order
' WorkerStart ' => [ App Events WorkerStartEvent::class],
],
Função de cálculo.
1.Modifique bootstrap/app.php
e defina o diretório de armazenamento. Como o diretório do projeto é somente leitura, o diretório /tmp
só pode ser lido e gravado.
$ app -> useStoragePath ( env ( ' APP_STORAGE_PATH ' , ' /tmp/storage ' ));
2.Crie um script de shell laravels_bootstrap
e conceda 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.Configurar 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
No modo FPM, as instâncias singleton serão instanciadas e recicladas em cada solicitação, request start=>instantiate instance=>request end=>recycled instance.
No Swoole Server, todas as instâncias singleton serão mantidas na memória, tempo de vida diferente do FPM, solicitação start=>instanciar instância=>solicitação final=>não reciclar instância singleton. Portanto, o desenvolvedor precisa manter o status das instâncias singleton em cada solicitação.
Soluções comuns:
Escreva uma classe XxxCleaner
para limpar o estado do objeto singleton. Esta classe implementa a interface Hhxsv5LaravelSIlluminateCleanersCleanerInterface
e então a registra nos cleaners
de laravels.php
.
Reset
o status de instâncias singleton por Middleware
.
Registre novamente ServiceProvider
, adicione XxxServiceProvider
em register_providers
do arquivo laravels.php
. Para que reinicialize instâncias singleton em cada referência de solicitação.
Limpadores de configuração.
Problemas conhecidos: um pacote de problemas e soluções conhecidos.
Registro; se quiser enviar para o console, você pode usar stderr
, Log::channel('stderr')->debug('debug message').
Laravel Dump Server (Laravel 5.7 foi integrado por padrão).
Solicitação de leitura por objeto IlluminateHttpRequest
, $_ENV é legível, $_SERVER é parcialmente legível, 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 ();
//...
}
Responder por IlluminateHttpResponse
Object, compatível com echo/vardump()/print_r(), CANNOT USE
funções dd()/exit()/die()/header()/setcookie()/http_response_code().
public function json ()
{
return response ()-> json ([ ' time ' => time ()])-> header ( ' header1 ' , ' value1 ' )-> withCookie ( ' c1 ' , ' v1 ' );
}
Singleton connection
será residente na memória; é recomendável ativar persistent connection
para melhor desempenho.
will
reconectado automaticamente immediately
após a desconexão. // 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 reconectará automaticamente immediately
após a desconexão e lançará uma exceção sobre a conexão perdida, reconecte na próxima vez. Você precisa ter certeza de que SELECT DB
está correto antes de operar o Redis todas as vezes. // 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
],
],
Evite usar variáveis globais. Se necessário, limpe-os ou reinicie-os manualmente.
Anexar infinitamente o elemento à variável static
/ global
levará a OOM (Sem memória).
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étodo de detecção de vazamento de memória
Modifique config/laravels.php
: worker_num=1, max_request=1000000
, lembre-se de alterá-lo novamente após o teste;
Adicione roteamento /debug-memory-leak
sem route middleware
para observar as alterações de memória do processo 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 ;
});
Inicie LaravelS
e solicite /debug-memory-leak
até que diff_mem
seja menor ou igual a zero; se diff_mem
for sempre maior que zero, significa que pode haver vazamento de memória no Global Middleware
ou Laravel Framework
;
Após concluir Step 3
, solicite alternately
as rotas de negócios e /debug-memory-leak
(recomenda-se usar ab
/ wrk
para fazer um grande número de solicitações de rotas de negócios), o aumento inicial de memória é normal. Após um grande número de solicitações de rotas de negócios, se diff_mem
for sempre maior que zero e curr_mem
continuar a aumentar, há uma alta probabilidade de vazamento de memória; Se curr_mem
sempre mudar dentro de um determinado intervalo e não continuar a aumentar, há uma baixa probabilidade de vazamento de memória.
Se ainda não conseguir resolver, max_request é a última garantia.
Ajuste de parâmetros do kernel Linux
Teste de pressão
PayPal
Bitcoin
Turismo Rural
MIT