영어 문서 | 중국어(중국어)
LaravelS는 Laravel/Lumen과 Swoole 간의 기본 어댑터입니다.
Watch
하세요.내장 Http/WebSocket 서버
다중 포트 혼합 프로토콜
맞춤형 프로세스
메모리 상주
비동기 이벤트 수신
비동기 작업 대기열
밀리초 크론 작업
공통 구성요소
정상적으로 새로고침
코드 수정 후 자동으로 다시 로드
Laravel/Lumen을 모두 지원하고 호환성이 좋습니다.
간단하고 즉시 사용 가능
가장 빠른 웹 프레임워크는 무엇입니까?
TechEmpower 프레임워크 벤치마크
의존 | 요구 사항 |
---|---|
PHP | >=8.2 Recommend 8.2 |
스울 | >=5.0 Recommend 5.1.1 |
라라벨/루멘 | >=10 Recommend 10 |
1. Composer(packagist)를 통해 패키지가 필요합니다.
# PHP >=8.2
composer require " hhxsv5/laravel-s:~3.8.0 "
# PHP >=5.5.9,<=7.4.33
# composer require "hhxsv5/laravel-s:~3.7.0"
# Make sure that your composer.lock file is under the VCS
2.서비스 제공자를 등록합니다(둘 중 하나 선택).
Laravel
: config/app.php
파일에서 Laravel 5.5+ supports package discovery automatically, you should skip this step
' providers ' => [
//...
Hhxsv5 LaravelS Illuminate LaravelSServiceProvider::class,
],
Lumen
: bootstrap/app.php
파일에 있음
$ app -> register ( Hhxsv5 LaravelS Illuminate LaravelSServiceProvider::class);
3. 구성 및 바이너리를 게시합니다.
LaravelS를 업그레이드한 후에는 다시 게시해야 합니다. 각 버전의 변경 사항을 보려면 여기를 클릭하세요.
php artisan laravels publish
# Configuration: config/laravels.php
# Binary: bin/laravels bin/fswatch bin/inotify
config/laravels.php
변경: listening_ip, listening_port, 설정 참조.
5.성능 튜닝
커널 매개변수 조정
작업자 수: LaravelS는 Swoole의 Synchronous IO
모드를 사용합니다. worker_num
설정이 클수록 동시성 성능이 향상되지만 메모리 사용량과 프로세스 전환 오버헤드가 더 많이 발생합니다. 하나의 요청에 100ms가 걸리는 경우 1000QPS 동시성을 제공하려면 최소 100개의 작업자 프로세스를 구성해야 합니다. 계산 방법은 Worker_num = 1000QPS/(1s/1ms) = 100이므로 최상의 worker_num
계산하려면 증분 압력 테스트가 필요합니다.
업무근로자 수
Please read the notices carefully before running
.
php bin/laravels {start|stop|restart|reload|info|help}
.명령 | 설명 |
---|---|
시작 | LaravelS를 시작하고 " ps -ef|grep laravels "로 프로세스를 나열합니다. |
멈추다 | LaravelS를 중지하고 사용자 정의 프로세스의 onStop 메소드를 트리거하십시오. |
다시 시작하다 | LaravelS를 다시 시작하세요: 시작하기 전에 정상적으로 중지하세요. 시작이 완료될 때까지 서비스를 unavailable . |
다시 장전하다 | 비즈니스 코드가 포함된 모든 작업/작업자/타이머 프로세스를 다시 로드하고 사용자 정의 프로세스의 onReload 메소드를 트리거합니다. 마스터/관리자 프로세스를 다시 로드할 수 없습니다. config/laravels.php 수정한 후 restart 호출하기 only 하면 됩니다. |
정보 | 구성 요소 버전 정보 표시 |
돕다 | 도움말 정보 표시 |
start
및 restart
명령에 대한 부팅 옵션입니다.옵션 | 설명 |
---|---|
-d|--데몬화 | 데몬으로 실행하면 이 옵션은 laravels.php 의 swoole.daemonize 설정을 재정의합니다. |
-e|--env | --env=testing 과 같이 명령이 실행되어야 하는 환경에서는 먼저 .env.testing 구성 파일을 사용합니다. 이 기능을 사용하려면 Laravel 5.2 이상이 필요합니다. |
-i|--무시 | 마스터 프로세스의 PID 파일 검사를 무시합니다. |
-x|--x-버전 | $_ENV/$_SERVER에 저장된 현재 프로젝트의 버전(브랜치), $_ENV['X_VERSION'] $_SERVER['X_VERSION'] $request->server->get('X_VERSION') 통해 액세스 |
Runtime
파일: start
자동으로 php artisan laravels config
실행하고 이러한 파일을 생성합니다. 개발자는 일반적으로 해당 파일에 주의할 필요가 없으며 .gitignore
에 추가하는 것이 좋습니다.파일 | 설명 |
---|---|
저장소/laravels.conf | LaravelS의 runtime 구성 파일 |
저장소/laravels.pid | 마스터 프로세스의 PID 파일 |
저장소/laravels-timer-process.pid | 타이머 프로세스의 PID 파일 |
저장소/laravels-custom-processes.pid | 모든 사용자 정의 프로세스의 PID 파일 |
Supervisord를 통해 기본 프로세스를 감독하는 것이 좋습니다. 전제는
-d
옵션이 없고swoole.daemonize
false
로 설정하는 것입니다.
[program:laravel-s-test]
directory=/var/www/laravel-s-test
command=/usr/local/bin/php bin/laravels start -i
numprocs=1
autostart=true
autorestart=true
startretries=3
user=www-data
redirect_stderr=true
stdout_logfile=/var/log/supervisor/%(program_name)s.log
데모.
gzip on ;
gzip_min_length 1024 ;
gzip_comp_level 2 ;
gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml application/x-httpd-php image/jpeg image/gif image/png font/ttf font/otf image/svg+xml;
gzip_vary on ;
gzip_disable "msie6" ;
upstream swoole {
# Connect IP:Port
server 127.0.0.1:5200 weight=5 max_fails=3 fail_timeout=30s;
# Connect UnixSocket Stream file, tips: put the socket file in the /dev/shm directory to get better performance
#server unix:/yourpath/laravel-s-test/storage/laravels.sock weight=5 max_fails=3 fail_timeout=30s;
#server 192.168.1.1:5200 weight=3 max_fails=3 fail_timeout=30s;
#server 192.168.1.2:5200 backup;
keepalive 16;
}
server {
listen 80 ;
# Don't forget to bind the host
server_name laravels.com;
root /yourpath/laravel-s-test/public;
access_log /yourpath/log/nginx/ $server_name .access.log main ;
autoindex off ;
index index.html index.htm;
# Nginx handles the static resources(recommend enabling gzip), LaravelS handles the dynamic resource.
location / {
try_files $uri @laravels;
}
# Response 404 directly when request the PHP file, to avoid exposing public/*.php
#location ~* .php$ {
# return 404;
#}
location @laravels {
# proxy_connect_timeout 60s;
# proxy_send_timeout 60s;
# proxy_read_timeout 120s;
proxy_http_version 1.1 ;
proxy_set_header Connection "" ;
proxy_set_header X-Real-IP $remote_addr ;
proxy_set_header X-Real-PORT $remote_port ;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ;
proxy_set_header Host $http_host ;
proxy_set_header Scheme $scheme ;
proxy_set_header Server-Protocol $server_protocol ;
proxy_set_header Server-Name $server_name ;
proxy_set_header Server-Addr $server_addr ;
proxy_set_header Server-Port $server_port ;
# "swoole" is the upstream
proxy_pass http://swoole;
}
}
LoadModule proxy_module /yourpath/modules/mod_proxy.so
LoadModule proxy_balancer_module /yourpath/modules/mod_proxy_balancer.so
LoadModule lbmethod_byrequests_module /yourpath/modules/mod_lbmethod_byrequests.so
LoadModule proxy_http_module /yourpath/modules/mod_proxy_http.so
LoadModule slotmem_shm_module /yourpath/modules/mod_slotmem_shm.so
LoadModule rewrite_module /yourpath/modules/mod_rewrite.so
LoadModule remoteip_module /yourpath/modules/mod_remoteip.so
LoadModule deflate_module /yourpath/modules/mod_deflate.so
< IfModule deflate_module>
SetOutputFilter DEFLATE
DeflateCompressionLevel 2
AddOutputFilterByType DEFLATE text/html text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml application/x-httpd-php image/jpeg image/gif image/png font/ttf font/otf image/svg+xml
</ IfModule >
< VirtualHost *:80>
# Don't forget to bind the host
ServerName www.laravels.com
ServerAdmin [email protected]
DocumentRoot /yourpath/laravel-s-test/public;
DirectoryIndex index.html index.htm
< Directory "/">
AllowOverride None
Require all granted
</ Directory >
RemoteIPHeader X-Forwarded-For
ProxyRequests Off
ProxyPreserveHost On
< Proxy balancer://laravels>
BalancerMember http://192.168.1.1:5200 loadfactor=7
# BalancerMember http://192.168.1.2:5200 loadfactor=3
# BalancerMember http://192.168.1.3:5200 loadfactor=1 status=+H
ProxySet lbmethod=byrequests
</ Proxy >
# ProxyPass / balancer://laravels/
# ProxyPassReverse / balancer://laravels/
# Apache handles the static resources, LaravelS handles the dynamic resource.
RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f
RewriteRule ^/(.*)$ balancer://laravels %{REQUEST_URI} [P,L]
ErrorLog ${APACHE_LOG_DIR}/www.laravels.com.error.log
CustomLog ${APACHE_LOG_DIR}/www.laravels.com.access.log combined
</ VirtualHost >
WebSocket Sever의 Listening 주소는 Http Server와 동일합니다.
1. WebSocket Handler 클래스를 생성하고 WebSocketHandlerInterface
인터페이스를 구현합니다. 인스턴트는 시작 시 자동으로 인스턴스화되므로 수동으로 생성할 필요가 없습니다.
namespace App Services ;
use Hhxsv5 LaravelS Swoole WebSocketHandlerInterface ;
use Swoole Http Request ;
use Swoole Http Response ;
use Swoole WebSocket Frame ;
use Swoole WebSocket Server ;
/**
* @see https://www.swoole.co.uk/docs/modules/swoole-websocket-server
*/
class WebSocketService implements WebSocketHandlerInterface
{
// Declare constructor without parameters
public function __construct ()
{
}
// public function onHandShake(Request $request, Response $response)
// {
// Custom handshake: https://www.swoole.co.uk/docs/modules/swoole-websocket-server-on-handshake
// The onOpen event will be triggered automatically after a successful handshake
// }
public function onOpen ( Server $ server , Request $ request )
{
// Before the onOpen event is triggered, the HTTP request to establish the WebSocket has passed the Laravel route,
// so Laravel's Request, Auth information are readable, Session is readable and writable, but only in the onOpen event.
// Log::info('New WebSocket connection', [$request->fd, request()->all(), session()->getId(), session('xxx'), session(['yyy' => time()])]);
// The exceptions thrown here will be caught by the upper layer and recorded in the Swoole log. Developers need to try/catch manually.
$ server -> push ( $ request -> fd , ' Welcome to LaravelS ' );
}
public function onMessage ( Server $ server , Frame $ frame )
{
// Log::info('Received message', [$frame->fd, $frame->data, $frame->opcode, $frame->finish]);
// The exceptions thrown here will be caught by the upper layer and recorded in the Swoole log. Developers need to try/catch manually.
$ server -> push ( $ frame -> fd , date ( ' Y-m-d H:i:s ' ));
}
public function onClose ( Server $ server , $ fd , $ reactorId )
{
// The exceptions thrown here will be caught by the upper layer and recorded in the Swoole log. Developers need to try/catch manually.
}
}
config/laravels.php
수정하세요.
// ...
' websocket ' => [
' enable ' => true , // Note: set enable to true
' handler ' => App Services WebSocketService::class,
],
' swoole ' => [
//...
// Must set dispatch_mode in (2, 4, 5), see https://www.swoole.co.uk/docs/modules/swoole-server/configuration
' dispatch_mode ' => 2 ,
//...
],
// ...
3. SwooleTable
사용하여 FD 및 UserId(선택 사항), Swoole Table Demo를 바인딩합니다. 또한 Redis/Memcached/MySQL과 같은 다른 글로벌 스토리지 서비스를 사용할 수 있지만 FD가 여러 Swoole Servers
간에 충돌할 수 있다는 점에 주의하세요.
4.Nginx와 협력(권장)
WebSocket 프록시 참조
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream swoole {
# Connect IP:Port
server 127.0.0.1:5200 weight=5 max_fails=3 fail_timeout=30s;
# Connect UnixSocket Stream file, tips: put the socket file in the /dev/shm directory to get better performance
#server unix:/yourpath/laravel-s-test/storage/laravels.sock weight=5 max_fails=3 fail_timeout=30s;
#server 192.168.1.1:5200 weight=3 max_fails=3 fail_timeout=30s;
#server 192.168.1.2:5200 backup;
keepalive 16;
}
server {
listen 80 ;
# Don't forget to bind the host
server_name laravels.com;
root /yourpath/laravel-s-test/public;
access_log /yourpath/log/nginx/ $server_name .access.log main ;
autoindex off ;
index index.html index.htm;
# Nginx handles the static resources(recommend enabling gzip), LaravelS handles the dynamic resource.
location / {
try_files $uri @laravels;
}
# Response 404 directly when request the PHP file, to avoid exposing public/*.php
#location ~* .php$ {
# return 404;
#}
# Http and WebSocket are concomitant, Nginx identifies them by "location"
# !!! The location of WebSocket is "/ws"
# Javascript: var ws = new WebSocket("ws://laravels.com/ws");
location =/ws {
# proxy_connect_timeout 60s;
# proxy_send_timeout 60s;
# proxy_read_timeout: Nginx will close the connection if the proxied server does not send data to Nginx in 60 seconds; At the same time, this close behavior is also affected by heartbeat setting of Swoole.
# proxy_read_timeout 60s;
proxy_http_version 1.1 ;
proxy_set_header X-Real-IP $remote_addr ;
proxy_set_header X-Real-PORT $remote_port ;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ;
proxy_set_header Host $http_host ;
proxy_set_header Scheme $scheme ;
proxy_set_header Server-Protocol $server_protocol ;
proxy_set_header Server-Name $server_name ;
proxy_set_header Server-Addr $server_addr ;
proxy_set_header Server-Port $server_port ;
proxy_set_header Upgrade $http_upgrade ;
proxy_set_header Connection $connection_upgrade ;
proxy_pass http://swoole;
}
location @laravels {
# proxy_connect_timeout 60s;
# proxy_send_timeout 60s;
# proxy_read_timeout 60s;
proxy_http_version 1.1 ;
proxy_set_header Connection "" ;
proxy_set_header X-Real-IP $remote_addr ;
proxy_set_header X-Real-PORT $remote_port ;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ;
proxy_set_header Host $http_host ;
proxy_set_header Scheme $scheme ;
proxy_set_header Server-Protocol $server_protocol ;
proxy_set_header Server-Name $server_name ;
proxy_set_header Server-Addr $server_addr ;
proxy_set_header Server-Port $server_port ;
proxy_pass http://swoole;
}
}
5.심박 설정
Swoole의 하트비트 설정
// config/laravels.php
' swoole ' => [
//...
// All connections are traversed every 60 seconds. If a connection does not send any data to the server within 600 seconds, the connection will be forced to close.
' heartbeat_idle_time ' => 600 ,
' heartbeat_check_interval ' => 60 ,
//...
],
Nginx의 프록시 읽기 시간 초과
# Nginx will close the connection if the proxied server does not send data to Nginx in 60 seconds
proxy_read_timeout 60s ;
6. 컨트롤러에 데이터 푸시
namespace App Http Controllers ;
class TestController extends Controller
{
public function push ()
{
$ fd = 1 ; // Find fd by userId from a map [userId=>fd].
/**@var SwooleWebSocketServer $swoole */
$ swoole = app ( ' swoole ' );
$ success = $ swoole -> push ( $ fd , ' Push data to fd#1 in Controller ' );
var_dump ( $ success );
}
}
일반적으로 일부
global/static
변수를 재설정/파기하거나 현재Request/Response
개체를 변경할 수 있습니다.
laravels.received_request
LaravelS가 SwooleHttpRequest
IlluminateHttpRequest
로 구문 분석한 후, Laravel의 커널이 이 요청을 처리하기 전입니다.
// Edit file `app/Providers/EventServiceProvider.php`, add the following code into method `boot`
// If no variable $events, you can also call Facade Event::listen().
$ events -> listen ( ' laravels.received_request ' , function ( Illuminate Http Request $ req , $ app ) {
$ req -> query -> set ( ' get_key ' , ' hhxsv5 ' ); // Change query of request
$ req -> request -> set ( ' post_key ' , ' hhxsv5 ' ); // Change post of request
});
laravels.generated_response
Laravel의 커널이 요청을 처리한 후 LaravelS가 IlluminateHttpResponse
SwooleHttpResponse
로 구문 분석하기 전입니다.
// Edit file `app/Providers/EventServiceProvider.php`, add the following code into method `boot`
// If no variable $events, you can also call Facade Event::listen().
$ events -> listen ( ' laravels.generated_response ' , function ( Illuminate Http Request $ req , Symfony Component HttpFoundation Response $ rsp , $ app ) {
$ rsp -> headers -> set ( ' header-key ' , ' hhxsv5 ' ); // Change header of response
});
이 기능은
Swoole
의AsyncTask
에 따라 다르므로 먼저config/laravels.php
에서swoole.task_worker_num
설정해야 합니다. 비동기 이벤트 처리 성능은 Swoole 작업 프로세스 수에 영향을 받으므로 task_worker_num을 적절하게 설정해야 합니다.
1. 이벤트 클래스를 생성합니다.
use Hhxsv5 LaravelS Swoole Task Event ;
class TestEvent extends Event
{
protected $ listeners = [
// Listener list
TestListener1::class,
// TestListener2::class,
];
private $ data ;
public function __construct ( $ data )
{
$ this -> data = $ data ;
}
public function getData ()
{
return $ this -> data ;
}
}
2.리스너 클래스를 생성합니다.
use Hhxsv5 LaravelS Swoole Task Event ;
use Hhxsv5 LaravelS Swoole Task Task ;
use Hhxsv5 LaravelS Swoole Task Listener ;
class TestListener1 extends Listener
{
public function handle ( Event $ event )
{
Log:: info ( __CLASS__ . ' :handle start ' , [ $ event -> getData ()]);
sleep ( 2 ); // Simulate the slow codes
// Deliver task in CronJob, but NOT support callback finish() of task.
// Note: Modify task_ipc_mode to 1 or 2 in config/laravels.php, see https://www.swoole.co.uk/docs/modules/swoole-server/configuration
$ ret = Task:: deliver ( new TestTask ( ' task data ' ));
var_dump ( $ ret );
// The exceptions thrown here will be caught by the upper layer and recorded in the Swoole log. Developers need to try/catch manually.
// return false; // Stop propagating this event to subsequent listeners
}
}
3. 화재 이벤트.
// Create instance of event and fire it, "fire" is asynchronous.
use Hhxsv5 LaravelS Swoole Task Event ;
$ event = new TestEvent ( ' event data ' );
// $event->delay(10); // Delay 10 seconds to fire event
// $event->setTries(3); // When an error occurs, try 3 times in total
$ success = Event:: fire ( $ event );
var_dump ( $ success ); // Return true if sucess, otherwise false
이 기능은
Swoole
의AsyncTask
에 따라 다르므로 먼저config/laravels.php
에서swoole.task_worker_num
설정해야 합니다. 작업 처리 성능은 Swoole 작업 처리 개수에 따라 영향을 받으므로 task_worker_num을 적절하게 설정해야 합니다.
1. 태스크 클래스를 생성합니다.
use Hhxsv5 LaravelS Swoole Task Task ;
class TestTask extends Task
{
private $ data ;
private $ result ;
public function __construct ( $ data )
{
$ this -> data = $ data ;
}
// The logic of task handling, run in task process, CAN NOT deliver task
public function handle ()
{
Log:: info ( __CLASS__ . ' :handle start ' , [ $ this -> data ]);
sleep ( 2 ); // Simulate the slow codes
// The exceptions thrown here will be caught by the upper layer and recorded in the Swoole log. Developers need to try/catch manually.
$ this -> result = ' the result of ' . $ this -> data ;
}
// Optional, finish event, the logic of after task handling, run in worker process, CAN deliver task
public function finish ()
{
Log:: info ( __CLASS__ . ' :finish start ' , [ $ this -> result ]);
Task:: deliver ( new TestTask2 ( ' task2 data ' )); // Deliver the other task
}
}
2.작업을 전달합니다.
// Create instance of TestTask and deliver it, "deliver" is asynchronous.
use Hhxsv5 LaravelS Swoole Task Task ;
$ task = new TestTask ( ' task data ' );
// $task->delay(3);// delay 3 seconds to deliver task
// $task->setTries(3); // When an error occurs, try 3 times in total
$ ret = Task:: deliver ( $ task );
var_dump ( $ ret ); // Return true if sucess, otherwise false
Swoole의 Millisecond Timer에 대한 래퍼 cron 작업 기반으로
Linux
Crontab
대체합니다.
1.크론 작업 클래스를 생성합니다.
namespace App Jobs Timer ;
use App Tasks TestTask ;
use Swoole Coroutine ;
use Hhxsv5 LaravelS Swoole Task Task ;
use Hhxsv5 LaravelS Swoole Timer CronJob ;
class TestCronJob extends CronJob
{
protected $ i = 0 ;
// !!! The `interval` and `isImmediate` of cron job can be configured in two ways(pick one of two): one is to overload the corresponding method, and the other is to pass parameters when registering cron job.
// --- Override the corresponding method to return the configuration: begin
public function interval ()
{
return 1000 ; // Run every 1000ms
}
public function isImmediate ()
{
return false ; // Whether to trigger `run` immediately after setting up
}
// --- Override the corresponding method to return the configuration: end
public function run ()
{
Log:: info ( __METHOD__ , [ ' start ' , $ this -> i , microtime ( true )]);
// do something
// sleep(1); // Swoole < 2.1
Coroutine:: sleep ( 1 ); // Swoole>=2.1 Coroutine will be automatically created for run().
$ this -> i ++;
Log:: info ( __METHOD__ , [ ' end ' , $ this -> i , microtime ( true )]);
if ( $ this -> i >= 10 ) { // Run 10 times only
Log:: info ( __METHOD__ , [ ' stop ' , $ this -> i , microtime ( true )]);
$ this -> stop (); // Stop this cron job, but it will run again after restart/reload.
// Deliver task in CronJob, but NOT support callback finish() of task.
// Note: Modify task_ipc_mode to 1 or 2 in config/laravels.php, see https://www.swoole.co.uk/docs/modules/swoole-server/configuration
$ ret = Task:: deliver ( new TestTask ( ' task data ' ));
var_dump ( $ ret );
}
// The exceptions thrown here will be caught by the upper layer and recorded in the Swoole log. Developers need to try/catch manually.
}
}
2.크론 작업을 등록합니다.
// Register cron jobs in file "config/laravels.php"
[
// ...
' timer ' => [
' enable ' => true , // Enable Timer
' jobs ' => [ // The list of cron job
// Enable LaravelScheduleJob to run `php artisan schedule:run` every 1 minute, replace Linux Crontab
// Hhxsv5LaravelSIlluminateLaravelScheduleJob::class,
// Two ways to configure parameters:
// [AppJobsTimerTestCronJob::class, [1000, true]], // Pass in parameters when registering
App Jobs Timer TestCronJob::class, // Override the corresponding method to return the configuration
],
' max_wait_time ' => 5 , // Max waiting time of reloading
// Enable the global lock to ensure that only one instance starts the timer when deploying multiple instances. This feature depends on Redis, please see https://laravel.com/docs/7.x/redis
' global_lock ' => false ,
' global_lock_key ' => config ( ' app.name ' , ' Laravel ' ),
],
// ...
];
3.참고: 서버 클러스터를 구축할 때 여러 개의 타이머가 실행되므로 반복적인 작업 실행을 피하기 위해 하나의 타이머만 실행해야 합니다.
4.LaravelS v3.4.0
에서는 핫 리스타트 [Reload] Timer
프로세스를 지원하기 시작합니다. LaravelS는 SIGUSR1
신호를 받은 후 max_wait_time
(기본값 5)초 동안 프로세스를 종료한 후, Manager
프로세스가 Timer
프로세스를 다시 시작합니다.
5. minute-level
예약된 작업만 사용해야 하는 경우 Laravel 작업 예약의 코딩 습관을 따르고 Kernel
구성할 수 있도록 Linux Crontab 대신 Hhxsv5LaravelSIlluminateLaravelScheduleJob
활성화하는 것이 좋습니다.
// app/Console/Kernel.php
protected function schedule ( Schedule $ schedule )
{
// runInBackground() will start a new child process to execute the task. This is asynchronous and will not affect the execution timing of other tasks.
$ schedule -> command (TestCommand::class)-> runInBackground ()-> everyMinute ();
}
inotify
통해 Linux만 지원합니다.
1. inotify 확장을 설치합니다.
2.설정에서 스위치를 켭니다.
3.주의사항: 파일 변경 이벤트를 수신하려면 Linux
에서만 파일을 수정하세요. 최신 Docker를 사용하는 것이 좋습니다. 방랑 솔루션.
fswatch
통해 OS X/Linux/Windows를 지원합니다.
1.fswatch를 설치합니다.
2. 프로젝트 루트 디렉터리에서 명령을 실행합니다.
# Watch current directory
./bin/fswatch
# Watch app directory
./bin/fswatch ./app
inotifywait
통해 Linux를 지원합니다.
1. inotify 도구를 설치합니다.
2. 프로젝트 루트 디렉터리에서 명령을 실행합니다.
# Watch current directory
./bin/inotify
# Watch app directory
./bin/inotify ./app
위의 방법이 작동하지 않는 경우 궁극적인 해결 방법은 max_request=1,worker_num=1
설정하여 요청 처리 후 Worker
프로세스가 다시 시작되도록 하는 것입니다. 이 방법은 성능이 매우 좋지 않아 so only development environment use
.
SwooleServer
인스턴스 가져오기 /**
* $swoole is the instance of `SwooleWebSocketServer` if enable WebSocket server, otherwise `SwooleHttpServer`
* @var SwooleWebSocketServer|SwooleHttpServer $swoole
*/
$ swoole = app ( ' swoole ' );
var_dump ( $ swoole -> stats ());
$ swoole -> push ( $ fd , ' Push WebSocket message ' );
SwooleTable
사용1. 테이블을 정의하고 다중을 지원합니다.
정의된 모든 테이블은 Swoole이 시작되기 전에 생성됩니다.
// in file "config/laravels.php"
[
// ...
' swoole_tables ' => [
// Scene:bind UserId & FD in WebSocket
' ws ' => [ // The Key is table name, will add suffix "Table" to avoid naming conflicts. Here defined a table named "wsTable"
' size ' => 102400 , // The max size
' column ' => [ // Define the columns
[ ' name ' => ' value ' , ' type ' => Swoole Table:: TYPE_INT , ' size ' => 8 ],
],
],
//...Define the other tables
],
// ...
];
2.액세스 Table
: 모든 테이블 인스턴스는 SwooleServer
에 바인딩되며 app('swoole')->xxxTable
통해 액세스됩니다.
namespace App Services ;
use Hhxsv5 LaravelS Swoole WebSocketHandlerInterface ;
use Swoole Http Request ;
use Swoole WebSocket Frame ;
use Swoole WebSocket Server ;
class WebSocketService implements WebSocketHandlerInterface
{
/**@var SwooleTable $wsTable */
private $ wsTable ;
public function __construct ()
{
$ this -> wsTable = app ( ' swoole ' )-> wsTable ;
}
// Scene:bind UserId & FD in WebSocket
public function onOpen ( Server $ server , Request $ request )
{
// var_dump(app('swoole') === $server);// The same instance
/**
* Get the currently logged in user
* This feature requires that the path to establish a WebSocket connection go through middleware such as Authenticate.
* E.g:
* Browser side: var ws = new WebSocket("ws://127.0.0.1:5200/ws");
* Then the /ws route in Laravel needs to add the middleware like Authenticate.
* Route::get('/ws', function () {
* // Respond any content with status code 200
* return 'websocket';
* })->middleware(['auth']);
*/
// $user = Auth::user();
// $userId = $user ? $user->id : 0; // 0 means a guest user who is not logged in
$ userId = mt_rand ( 1000 , 10000 );
// if (!$userId) {
// // Disconnect the connections of unlogged users
// $server->disconnect($request->fd);
// return;
// }
$ this -> wsTable -> set ( ' uid: ' . $ userId , [ ' value ' => $ request -> fd ]); // Bind map uid to fd
$ this -> wsTable -> set ( ' fd: ' . $ request -> fd , [ ' value ' => $ userId ]); // Bind map fd to uid
$ server -> push ( $ request -> fd , " Welcome to LaravelS # { $ request -> fd }" );
}
public function onMessage ( Server $ server , Frame $ frame )
{
// Broadcast
foreach ( $ this -> wsTable as $ key => $ row ) {
if ( strpos ( $ key , ' uid: ' ) === 0 && $ server -> isEstablished ( $ row [ ' value ' ])) {
$ content = sprintf ( ' Broadcast: new message "%s" from #%d ' , $ frame -> data , $ frame -> fd );
$ server -> push ( $ row [ ' value ' ], $ content );
}
}
}
public function onClose ( Server $ server , $ fd , $ reactorId )
{
$ uid = $ this -> wsTable -> get ( ' fd: ' . $ fd );
if ( $ uid !== false ) {
$ this -> wsTable -> del ( ' uid: ' . $ uid [ ' value ' ]); // Unbind uid map
}
$ this -> wsTable -> del ( ' fd: ' . $ fd ); // Unbind fd map
$ server -> push ( $ fd , " Goodbye # { $ fd }" );
}
}
자세한 내용은 Swoole Server AddListener를 참조하세요.
우리의 메인 서버가 Http와 WebSocket뿐만 아니라 더 많은 프로토콜을 지원하도록 하기 위해 LaravelS에 Swoole의 multi-port mixed protocol
기능을 도입하고 이름을 Socket
으로 지정했습니다. 이제 Laravel을 기반으로 TCP/UDP
애플리케이션을 쉽게 구축할 수 있습니다.
Socket
핸들러 클래스를 생성하고 Hhxsv5LaravelSSwooleSocket{TcpSocket|UdpSocket|Http|WebSocket}
확장합니다.
namespace App Sockets ;
use Hhxsv5 LaravelS Swoole Socket TcpSocket ;
use Swoole Server ;
class TestTcpSocket extends TcpSocket
{
public function onConnect ( Server $ server , $ fd , $ reactorId )
{
Log:: info ( ' New TCP connection ' , [ $ fd ]);
$ server -> send ( $ fd , ' Welcome to LaravelS. ' );
}
public function onReceive ( Server $ server , $ fd , $ reactorId , $ data )
{
Log:: info ( ' Received data ' , [ $ fd , $ data ]);
$ server -> send ( $ fd , ' LaravelS: ' . $ data );
if ( $ data === " quit rn" ) {
$ server -> send ( $ fd , ' LaravelS: bye ' . PHP_EOL );
$ server -> close ( $ fd );
}
}
public function onClose ( Server $ server , $ fd , $ reactorId )
{
Log:: info ( ' Close TCP connection ' , [ $ fd ]);
$ server -> send ( $ fd , ' Goodbye ' );
}
}
이러한 Socket
연결은 HTTP
/ WebSocket
연결과 동일한 작업자 프로세스를 공유합니다. 따라서 작업을 전달하려는 경우 SwooleTable
사용하거나 DB, Eloquent 등과 같은 Laravel 구성 요소를 사용하는 경우 전혀 문제가 되지 않습니다. 동시에 멤버 속성 swoolePort
를 통해 SwooleServerPort
개체에 직접 액세스할 수 있습니다.
public function onReceive ( Server $ server , $ fd , $ reactorId , $ data )
{
$ port = $ this -> swoolePort ; // Get the `SwooleServerPort` object
}
namespace App Http Controllers ;
class TestController extends Controller
{
public function test ()
{
/**@var SwooleHttpServer|SwooleWebSocketServer $swoole */
$ swoole = app ( ' swoole ' );
// $swoole->ports: Traverse all Port objects, https://www.swoole.co.uk/docs/modules/swoole-server/multiple-ports
$ port = $ swoole -> ports [ 0 ]; // Get the `SwooleServerPort` object, $port[0] is the port of the main server
foreach ( $ port -> connections as $ fd ) { // Traverse all connections
// $swoole->send($fd, 'Send tcp message');
// if($swoole->isEstablished($fd)) {
// $swoole->push($fd, 'Send websocket message');
// }
}
}
}
소켓을 등록합니다.
// Edit `config/laravels.php`
//...
' sockets ' => [
[
' host ' => ' 127.0.0.1 ' ,
' port ' => 5291 ,
' type ' => SWOOLE_SOCK_TCP , // Socket type: SWOOLE_SOCK_TCP/SWOOLE_SOCK_TCP6/SWOOLE_SOCK_UDP/SWOOLE_SOCK_UDP6/SWOOLE_UNIX_DGRAM/SWOOLE_UNIX_STREAM
' settings ' => [ // Swoole settings:https://www.swoole.co.uk/docs/modules/swoole-server-methods#swoole_server-addlistener
' open_eof_check ' => true ,
' package_eof ' => "rn" ,
],
' handler ' => App Sockets TestTcpSocket::class,
' enable ' => true , // whether to enable, default true
],
],
하트비트 구성은 main server
에서만 설정할 수 있고 Socket
에서는 구성할 수 없지만 Socket
은 main server
의 하트비트 구성을 상속합니다.
TCP 소켓의 경우 Swoole의 dispatch_mode
1/3
일 때 onConnect
및 onClose
이벤트가 차단되므로 이 두 이벤트의 차단을 해제하려면 dispatch_mode
2/4/5
로 설정하세요.
' swoole ' => [
//...
' dispatch_mode ' => 2 ,
//...
];
시험.
TCP: telnet 127.0.0.1 5291
UDP: [Linux] echo "Hello LaravelS" > /dev/udp/127.0.0.1/5292
다른 프로토콜의 예를 등록합니다.
' sockets ' => [
[
' host ' => ' 0.0.0.0 ' ,
' port ' => 5292 ,
' type ' => SWOOLE_SOCK_UDP ,
' settings ' => [
' open_eof_check ' => true ,
' package_eof ' => "rn" ,
],
' handler ' => App Sockets TestUdpSocket::class,
],
],
' sockets ' => [
[
' host ' => ' 0.0.0.0 ' ,
' port ' => 5293 ,
' type ' => SWOOLE_SOCK_TCP ,
' settings ' => [
' open_http_protocol ' => true ,
],
' handler ' => App Sockets TestHttp::class,
],
],
turn on WebSocket
합니다. 즉, websocket.enable
true
로 설정해야 합니다. ' sockets ' => [
[
' host ' => ' 0.0.0.0 ' ,
' port ' => 5294 ,
' type ' => SWOOLE_SOCK_TCP ,
' settings ' => [
' open_http_protocol ' => true ,
' open_websocket_protocol ' => true ,
],
' handler ' => App Sockets TestWebSocket::class,
],
],
스울 코루틴
경고: 코루틴의 코드 실행 순서가 잘못되었습니다. 요청 수준의 데이터는 코루틴 ID로 격리되어야 합니다. 그러나 Laravel/Lumen에는 많은 싱글톤 및 정적 속성이 있으므로 서로 다른 요청 간의 데이터가 서로 영향을 미치므로 Unsafe
. 예를 들어, 데이터베이스 연결은 싱글톤이고, 동일한 데이터베이스 연결은 동일한 PDO 리소스를 공유합니다. 이는 동기 차단 모드에서는 문제가 없지만 비동기 코루틴 모드에서는 작동하지 않습니다. 각 쿼리는 서로 다른 연결을 생성하고 서로 다른 연결의 IO 상태를 유지해야 하며, 이를 위해서는 연결 풀이 필요합니다.
코루틴을 활성화 DO NOT
. 사용자 정의 프로세스에서만 코루틴을 사용할 수 있습니다.
개발자가 모니터링, 보고 또는 기타 특수 작업을 위한 특수 작업 프로세스를 만들 수 있도록 지원합니다. addProcess를 참조하세요.
Process 클래스를 생성하고 CustomProcessInterface를 구현합니다.
namespace App Processes ;
use App Tasks TestTask ;
use Hhxsv5 LaravelS Swoole Process CustomProcessInterface ;
use Hhxsv5 LaravelS Swoole Task Task ;
use Swoole Coroutine ;
use Swoole Http Server ;
use Swoole Process ;
class TestProcess implements CustomProcessInterface
{
/**
* @var bool Quit tag for Reload updates
*/
private static $ quit = false ;
public static function callback ( Server $ swoole , Process $ process )
{
// The callback method cannot exit. Once exited, Manager process will automatically create the process
while (! self :: $ quit ) {
Log:: info ( ' Test process: running ' );
// sleep(1); // Swoole < 2.1
Coroutine:: sleep ( 1 ); // Swoole>=2.1: Coroutine & Runtime will be automatically enabled for callback(). Pay attention to the compatibility between the components used and the coroutines. If they are not compatible, only some coroutines can be enabled, such as: SwooleRuntime::enableCoroutine(SWOOLE_HOOK_TCP | SWOOLE_HOOK_SLEEP | SWOOLE_HOOK_FILE);
// Deliver task in custom process, but NOT support callback finish() of task.
// Note: Modify task_ipc_mode to 1 or 2 in config/laravels.php, see https://www.swoole.co.uk/docs/modules/swoole-server/configuration
$ ret = Task:: deliver ( new TestTask ( ' task data ' ));
var_dump ( $ ret );
// The upper layer will catch the exception thrown in the callback and record it in the Swoole log, and then this process will exit. The Manager process will re-create the process after 3 seconds, so developers need to try/catch to catch the exception by themselves to avoid frequent process creation.
// throw new Exception('an exception');
}
}
// Requirements: LaravelS >= v3.4.0 & callback() must be async non-blocking program.
public static function onReload ( Server $ swoole , Process $ process )
{
// Stop the process...
// Then end process
Log:: info ( ' Test process: reloading ' );
self :: $ quit = true ;
// $process->exit(0); // Force exit process
}
// Requirements: LaravelS >= v3.7.4 & callback() must be async non-blocking program.
public static function onStop ( Server $ swoole , Process $ process )
{
// Stop the process...
// Then end process
Log:: info ( ' Test process: stopping ' );
self :: $ quit = true ;
// $process->exit(0); // Force exit process
}
}
TestProcess를 등록합니다.
// Edit `config/laravels.php`
// ...
' processes ' => [
' test ' => [ // Key name is process name
' class ' => App Processes TestProcess::class,
' redirect ' => false , // Whether redirect stdin/stdout, true or false
' pipe ' => 0 , // The type of pipeline, 0: no pipeline 1: SOCK_STREAM 2: SOCK_DGRAM
' enable ' => true , // Whether to enable, default true
//'num' => 3 // To create multiple processes of this class, default is 1
//'queue' => [ // Enable message queue as inter-process communication, configure empty array means use default parameters
// 'msg_key' => 0, // The key of the message queue. Default: ftok(__FILE__, 1).
// 'mode' => 2, // Communication mode, default is 2, which means contention mode
// 'capacity' => 8192, // The length of a single message, is limited by the operating system kernel parameters. The default is 8192, and the maximum is 65536
//],
//'restart_interval' => 5, // After the process exits abnormally, how many seconds to wait before restarting the process, default 5 seconds
],
],
참고: callback()은 종료할 수 없습니다. 종료하면 Manager 프로세스가 프로세스를 다시 생성합니다.
예: 사용자 정의 프로세스에 데이터를 씁니다.
// config/laravels.php
' processes ' => [
' test ' => [
' class ' => App Processes TestProcess::class,
' redirect ' => false ,
' pipe ' => 1 ,
],
],
// app/Processes/TestProcess.php
public static function callback ( Server $ swoole , Process $ process )
{
while ( $ data = $ process -> read ()) {
Log:: info ( ' TestProcess: read data ' , [ $ data ]);
$ process -> write ( ' TestProcess: ' . $ data );
}
}
// app/Http/Controllers/TestController.php
public function testProcessWrite ()
{
/**@var SwooleProcess[] $process */
$ customProcesses = Hhxsv5 LaravelS LaravelS:: getCustomProcesses ();
$ process = $ customProcesses [ ' test ' ];
$ process -> write ( ' TestController: write data ' . time ());
var_dump ( $ process -> read ());
}
LaravelS
시작할 때Apollo
구성을 가져와서.env
파일에 씁니다. 동시에LaravelS
구성을 모니터링하고 구성이 변경되면 자동으로reload
위해 사용자 정의 프로세스인apollo
시작합니다.
Apollo 활성화: 시작 매개변수에 --enable-apollo
및 Apollo 매개변수를 추가합니다.
php bin/laravels start --enable-apollo --apollo-server=http://127.0.0.1:8080 --apollo-app-id=LARAVEL-S-TEST
핫 업데이트를 지원합니다(선택 사항).
// Edit `config/laravels.php`
' processes ' => Hhxsv5 LaravelS Components Apollo Process:: getDefinition (),
// When there are other custom process configurations
' processes ' => [
' test ' => [
' class ' => App Processes TestProcess::class,
' redirect ' => false ,
' pipe ' => 1 ,
],
// ...
] + Hhxsv5 LaravelS Components Apollo Process:: getDefinition (),
사용 가능한 매개변수 목록입니다.
매개변수 | 설명 | 기본 | 데모 |
---|---|---|---|
아폴로 서버 | Apollo 서버 URL | - | --apollo-서버=http://127.0.0.1:8080 |
아폴로 앱 ID | 아폴로 앱 ID | - | --apollo-app-id=LARAVEL-S-TEST |
아폴로 네임스페이스 | APP가 속한 네임스페이스, 다중 지정 지원 | 애플리케이션 | --apollo-namespaces=응용 프로그램 --apollo-namespaces=env |
아폴로 클러스터 | APP가 속한 클러스터 | 기본 | --apollo-cluster=기본값 |
아폴로 클라이언트 IP | 현재 인스턴스의 IP는 그레이스케일 게시에도 사용할 수 있습니다. | 로컬 인트라넷 IP | --아폴로-클라이언트-ip=10.2.1.83 |
아폴로 풀타임아웃 | 구성을 가져올 때 시간 초과 시간(초) | 5 | --아폴로-풀-타임아웃=5 |
아폴로-백업-오래된 환경 | 구성 파일 .env 를 업데이트할 때 이전 구성 파일을 백업할지 여부 | 거짓 | --apollo-backup-old-env |
Prometheus 모니터링 및 경보를 지원하고 Grafana는 모니터링 측정항목을 시각적으로 확인합니다. Prometheus 및 Grafana의 환경 구축은 Docker Compose를 참고하세요.
확장 APCu >= 5.0.0이 필요합니다. pecl install apcu
로 설치하세요.
구성 파일 prometheus.php
프로젝트의 config
디렉터리에 복사합니다. 구성을 적절하게 수정합니다.
# Execute commands in the project root directory
cp vendor/hhxsv5/laravel-s/config/prometheus.php config/
프로젝트가 Lumen
인 경우 $app->configure('prometheus');
bootstrap/app.php
에 있습니다.
global
미들웨어 구성: Hhxsv5LaravelSComponentsPrometheusRequestMiddleware::class
. 요청 시간 소비를 최대한 정확하게 계산하려면 RequestMiddleware
다른 미들웨어 앞에 배치되어야 하는 first
전역 미들웨어여야 합니다.
ServiceProvider 등록: Hhxsv5LaravelSComponentsPrometheusServiceProvider::class
.
Swoole Worker/Task/Timer 프로세스의 지표를 정기적으로 수집하려면 config/laravels.php
에서 CollectorProcess를 구성하십시오.
' processes ' => Hhxsv5 LaravelS Components Prometheus CollectorProcess:: getDefinition (),
측정항목을 출력하는 경로를 만듭니다.
use Hhxsv5 LaravelS Components Prometheus Exporter ;
Route:: get ( ' /actuator/prometheus ' , function () {
$ result = app (Exporter::class)-> render ();
return response ( $ result , 200 , [ ' Content-Type ' => Exporter:: REDNER_MIME_TYPE ]);
});
Prometheus 구성을 완료하고 시작합니다.
global :
scrape_interval : 5s
scrape_timeout : 5s
evaluation_interval : 30s
scrape_configs :
- job_name : laravel-s-test
honor_timestamps : true
metrics_path : /actuator/prometheus
scheme : http
follow_redirects : true
static_configs :
- targets :
- 127.0.0.1:5200 # The ip and port of the monitored service
# Dynamically discovered using one of the supported service-discovery mechanisms
# https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config
# - job_name: laravels-eureka
# honor_timestamps: true
# scrape_interval: 5s
# metrics_path: /actuator/prometheus
# scheme: http
# follow_redirects: true
# eureka_sd_configs:
# - server: http://127.0.0.1:8080/eureka
# follow_redirects: true
# refresh_interval: 5s
Grafana를 시작한 다음 패널 json을 가져옵니다.
지원되는 이벤트:
이벤트 | 인터페이스 | 언제 일어났는가 |
---|---|---|
서버스타트 | Hhxsv5LaravelSSwooleEventsServerStartInterface | 마스터 프로세스가 시작될 때 발생합니다. this event should not handle complex business logic, and can only do some simple work of initialization . |
서버중지 | Hhxsv5LaravelSSwooleEventsServerStopInterface | 서버가 정상적으로 종료될 때 발생하며, CANNOT use async or coroutine related APIs in this event . |
작업자 시작 | Hhxsv5LaravelSSwooleEventsWorkerStartInterface | Worker/Task 프로세스가 시작되고 Laravel 초기화가 완료된 후에 발생합니다. |
작업자 중지 | Hhxsv5LaravelSSwooleEventsWorkerStopInterface | Worker/Task 프로세스가 정상적으로 종료된 후에 발생합니다. |
작업자 오류 | Hhxsv5LaravelSSwooleEventsWorkerErrorInterface | Worker/Task 프로세스에서 예외나 치명적인 오류가 발생한 경우 발생합니다. |
1. 해당 인터페이스를 구현하기 위한 이벤트 클래스를 생성합니다.
namespace App Events ;
use Hhxsv5 LaravelS Swoole Events ServerStartInterface ;
use Swoole Atomic ;
use Swoole Http Server ;
class ServerStartEvent implements ServerStartInterface
{
public function __construct ()
{
}
public function handle ( Server $ server )
{
// Initialize a global counter (available across processes)
$ server -> atomicCount = new Atomic ( 2233 );
// Invoked in controller: app('swoole')->atomicCount->get();
}
}
namespace App Events ;
use Hhxsv5 LaravelS Swoole Events WorkerStartInterface ;
use Swoole Http Server ;
class WorkerStartEvent implements WorkerStartInterface
{
public function __construct ()
{
}
public function handle ( Server $ server , $ workerId )
{
// Initialize a database connection pool
// DatabaseConnectionPool::init();
}
}
2. 구성.
// Edit `config/laravels.php`
' event_handlers ' => [
' ServerStart ' => [ App Events ServerStartEvent::class], // Trigger events in array order
' WorkerStart ' => [ App Events WorkerStartEvent::class],
],
함수 계산.
1. bootstrap/app.php
수정하고 저장 디렉터리를 설정합니다. 프로젝트 디렉토리는 읽기 전용이므로 /tmp
디렉토리는 읽기 및 쓰기만 가능합니다.
$ app -> useStoragePath ( env ( ' APP_STORAGE_PATH ' , ' /tmp/storage ' ));
2. 쉘 스크립트 laravels_bootstrap
을 생성하고 executable permission
부여합니다.
#! /usr/bin/env bash
set +e
# Create storage-related directories
mkdir -p /tmp/storage/app/public
mkdir -p /tmp/storage/framework/cache
mkdir -p /tmp/storage/framework/sessions
mkdir -p /tmp/storage/framework/testing
mkdir -p /tmp/storage/framework/views
mkdir -p /tmp/storage/logs
# Set the environment variable APP_STORAGE_PATH, please make sure it's the same as APP_STORAGE_PATH in .env
export APP_STORAGE_PATH=/tmp/storage
# Start LaravelS
php bin/laravels start
template.xml
구성합니다.
ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
laravel-s-demo:
Type: 'Aliyun::Serverless::Service'
Properties:
Description: 'LaravelS Demo for Serverless'
fc-laravel-s:
Type: 'Aliyun::Serverless::Function'
Properties:
Handler: laravels.handler
Runtime: custom
MemorySize: 512
Timeout: 30
CodeUri: ./
InstanceConcurrency: 10
EnvironmentVariables:
BOOTSTRAP_FILE: laravels_bootstrap
FPM 모드에서 싱글톤 인스턴스는 모든 요청에서 인스턴스화되고 재활용됩니다(요청 시작=>인스턴스 인스턴스화=>요청 종료=>재활용된 인스턴스).
Swoole Server에서 모든 싱글톤 인스턴스는 FPM과 수명이 다른 메모리에 보관됩니다. 요청 시작=>인스턴스 인스턴스화=>요청 종료=>싱글톤 인스턴스를 재활용하지 않습니다. 따라서 개발자는 모든 요청에서 싱글톤 인스턴스 상태를 유지해야 합니다.
일반적인 솔루션:
싱글톤 개체 상태를 정리하려면 XxxCleaner
클래스를 작성하세요. 이 클래스는 Hhxsv5LaravelSIlluminateCleanersCleanerInterface
인터페이스를 구현한 다음 이를 laravels.php
cleaners
에 등록합니다.
Middleware
에 의한 싱글톤 인스턴스의 상태를 Reset
.
ServiceProvider
다시 등록하고 laravels.php
파일의 register_providers
에 XxxServiceProvider
추가하세요. 따라서 모든 요청에서 싱글톤 인스턴스를 다시 초기화합니다.
구성 클리너.
알려진 문제: 알려진 문제 및 솔루션 패키지입니다.
벌채 반출; 콘솔에 출력하려면 stderr
, Log::channel('stderr')->debug('debug message') 를 사용할 수 있습니다.
Laravel Dump Server(Laravel 5.7이 기본적으로 통합되어 있습니다).
IlluminateHttpRequest
객체에 의한 읽기 요청, $_ENV는 읽기 가능, $_SERVER는 부분적으로 읽기 CANNOT USE
.
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 ();
//...
}
echo/vardump()/print_r()와 호환되는 IlluminateHttpResponse
객체로 응답합니다. dd()/exit()/die()/header()/setcookie()/http_response_code() 함수를 CANNOT USE
.
public function json ()
{
return response ()-> json ([ ' time ' => time ()])-> header ( ' header1 ' , ' value1 ' )-> withCookie ( ' c1 ' , ' v1 ' );
}
Singleton connection
메모리에 상주하므로 더 나은 성능을 위해 persistent connection
켜는 것이 좋습니다.
immediately
자동으로 다시 연결 will
. // 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 ,
],
],
],
immediately
자동으로 다시 연결되지 won't
연결 끊김에 대한 예외를 발생시키고 다음에 다시 연결합니다. Redis를 운영하기 전에는 매번 SELECT DB
확인해야 합니다. // config/database.php
' redis ' => [
' client ' => env ( ' REDIS_CLIENT ' , ' phpredis ' ), // It is recommended to use phpredis for better performance.
' default ' => [
' host ' => env ( ' REDIS_HOST ' , ' localhost ' ),
' password ' => env ( ' REDIS_PASSWORD ' , null ),
' port ' => env ( ' REDIS_PORT ' , 6379 ),
' database ' => 0 ,
' persistent ' => true , // Enable persistent connection
],
],
전역 변수를 사용하지 마십시오. 필요한 경우 수동으로 청소하거나 재설정하십시오.
static
/ global
변수에 요소를 무한히 추가하면 OOM(Out of Memory)이 발생합니다.
class Test
{
public static $ array = [];
public static $ string = '' ;
}
// Controller
public function test ( Request $ req )
{
// Out of Memory
Test:: $ array [] = $ req -> input ( ' param1 ' );
Test:: $ string .= $ req -> input ( ' param2 ' );
}
메모리 누수 감지 방법
config/laravels.php
수정하세요: worker_num=1, max_request=1000000
, 테스트 후에 다시 변경하는 것을 잊지 마세요.
Worker
프로세스의 메모리 변경을 관찰하기 위해 route middleware
없이 라우팅 /debug-memory-leak
추가합니다.
Route:: get ( ' /debug-memory-leak ' , function () {
global $ previous ;
$ current = memory_get_usage ();
$ stats = [
' prev_mem ' => $ previous ,
' curr_mem ' => $ current ,
' diff_mem ' => $ current - $ previous ,
];
$ previous = $ current ;
return $ stats ;
});
LaravelS
시작하고 diff_mem
0보다 작거나 같을 때까지 /debug-memory-leak
요청하세요. diff_mem
항상 0보다 크면 Global Middleware
또는 Laravel Framework
에 메모리 누수가 있을 수 있음을 의미합니다.
Step 3
완료 후 비즈니스 경로와 /debug-memory-leak
alternately
요청하면(비즈니스 경로에 대한 요청을 많이 하려면 ab
/ wrk
사용하는 것이 좋습니다) 초기 메모리 증가는 정상입니다. 비즈니스 경로에 대한 요청이 많은 후에 diff_mem
이 항상 0보다 크고 curr_mem
계속 증가하면 메모리 누수 가능성이 높습니다. curr_mem
항상 일정 범위 내에서 변하고 계속 증가하지 않는다면 메모리 누수 가능성이 낮다.
그래도 해결할 수 없다면 max_request가 마지막 보장입니다.
Linux 커널 매개변수 조정
압력 테스트
페이팔
BTC
기티
MIT