LaravelS:通过 Swoole 来加速 Laravel/Lumen - PHP开发社区 | CTOLib码库

LaravelS - 站在巨人的肩膀上

:rocket: 通过Swoole来加速 Laravel/Lumen,其中的S代表Swoole,速度,高性能。

English Documentation

特性

  • 高性能的Swoole
  • 内置Http/Websocket服务器
  • 常驻内存
  • 异步的事件监听
  • 异步的任务队列
  • 平滑Reload
  • 代码修改后自动Reload
  • 同时支持Laravel与Lumen,兼容主流版本
  • 简单,开箱即用

要求

依赖

说明

PHP

= 5.5.9

Swoole

= 1.7.19 推荐最新的稳定版 从2.0.12开始不再支持PHP5

Laravel / Lumen

= 5.1

Gzip[可选的]

zlib ,用于压缩HTTP响应,检查本机 libz 是否可用 ldconfig -p|grep libz

Inotify[可选的]

inotify ,用于修改代码后自动Reload Worker进程,检查本机 inotify 是否可用 php --ri inotify

安装

1.通过 Composer 安装( packagist )。

# 在你的Laravel/Lumen项目的根目录下执行
composer require "hhxsv5/laravel-s:~1.0" -vvv
# 确保你的composer.lock文件是在版本控制中

2.添加Service Provider。

  • Laravel : 修改文件 config/app.php

'providers' => [

//...
Hhxsv5\\LaravelS\\Illuminate\\LaravelSServiceProvider::class,

],

  • Lumen : 修改文件 bootstrap/app.php

$app->register(Hhxsv5\LaravelS\Illuminate\LaravelSServiceProvider::class);

3.发布配置文件。

php artisan laravels publish

使用Lumen时的特别说明 : 你不需要手动加载配置 laravels.php ,LaravelS底层已自动加载。

// 不必手动加载,但加载了也不会有问题
$app->configure('laravels');

4.修改配置 config/laravels.php :监听的IP、端口等,请参考 配置项

运行

php artisan laravels {start|stop|restart|reload|publish}

命令

说明

start

启动LaravelS,展示已启动的进程列表 ps -ef|grep laravels

stop

停止LaravelS

restart

重启LaravelS

reload

平滑重启所有worker进程,这些worker进程内包含你的业务代码和框架(Laravel/Lumen)代码,不会重启master/manger进程

publish

发布配置文件到你的项目中 config/laravels.php

与Nginx配合使用(推荐)

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 laravels {

server 192.168.0.1:5200 weight=5 max_fails=3 fail_timeout=30s;
#server 192.168.0.2:5200 weight=3 max\_fails=3 fail\_timeout=30s;
#server 192.168.0.3:5200 backup;

}
server {

listen 80;
server_name laravels.com;
root /xxxpath/laravel-s-test/public;
access_log /yyypath/log/nginx/$server_name.access.log  main;
autoindex off;
index index.html index.htm;

\# Nginx处理静态资源,LaravelS处理动态资源。
location / {
    try_files $uri @laravels;
}

location @laravels {
    proxy\_http\_version 1.1;
    \# proxy\_connect\_timeout 60s;
    \# proxy\_send\_timeout 60s;
    \# proxy\_read\_timeout 120s;
    proxy\_set\_header Connection "keep-alive";
    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 $host;
    proxy_pass http://laravels;
}

}

与Apache配合使用

LoadModule proxy_module /yyypath/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>

ServerName www.laravels.com
ServerAdmin hhxsv5@sina.com

DocumentRoot /xxxpath/laravel-s-test/public;
DirectoryIndex index.html index.htm
<Directory "/">
    AllowOverride None
    Require all granted
</Directory>

LoadModule proxy\_module /yyypath/modules/mod\_proxy.so
LoadModule proxy\_module /yyypath/modules/mod\_proxy_balancer.so
LoadModule proxy\_module /yyypath/modules/mod\_lbmethod_byrequests.so.so
LoadModule proxy\_module /yyypath/modules/mod\_proxy_http.so.so
LoadModule proxy\_module /yyypath/modules/mod\_slotmem_shm.so
LoadModule proxy\_module /yyypath/modules/mod\_rewrite.so

ProxyRequests Off
ProxyPreserveHost On
<Proxy balancer://laravels>  
    BalancerMember http://192.168.1.1:8011 loadfactor=7
    #BalancerMember http://192.168.1.2:8011 loadfactor=3
    #BalancerMember http://192.168.1.3:8011 loadfactor=1 status=+H
    ProxySet lbmethod=byrequests
</Proxy>
#ProxyPass / balancer://laravels/
#ProxyPassReverse / balancer://laravels/

\# Apache处理静态资源,LaravelS处理动态资源。
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服务器

Websocket服务器监听的IP和端口与Http服务器相同。

1.创建Websocket Handler类,并实现接口 WebsocketHandlerInterface 。

namespace AppServices;
use Hhxsv5LaravelSSwooleWebsocketHandlerInterface;
/**
* @see https://wiki.swoole.com/wiki/page/400.html
*/
class WebsocketService implements WebsocketHandlerInterface {

public function onOpen(\\swoole\_websocket\_server $server, \\swoole\_http\_request $request) {
    \\Log::info('New Websocket connection', \[$request->fd\]);
    $server->push($request->fd, 'Welcome to LaravelS');
    // throw new \\Exception('an exception'); //上层会自动忽略handle时抛出的异常
}
public function onMessage(\\swoole\_websocket\_server $server, \\swoole\_websocket\_frame $frame) {
    \\Log::info('Received message', \[$frame->fd, $frame->data, $frame->opcode, $frame->finish\]);
    $server->push($frame->fd, date('Y-m-d H:i:s'));
}
public function onClose(\\swoole\_websocket\_server $server, $fd, $reactorId) {
}

}

2.更改配置 config/laravels.php 。

// ...
'websocket' => [

'enable'  =\> true,
'handler' =\> \\App\\Services\\WebsocketService::class,

],
// ...

监听事件

系统事件

通常,你可以在这些事件中重置或销毁一些全局或静态的变量,也可以修改当前的请求和响应。

  • laravels.received_request 将 swoole_http_request 转成 Illuminate\Http\Request 后,在Laravel内核处理请求前。

// 修改`app/Providers/EventServiceProvider.php`, 添加下面监听代码到boot方法中
// 如果变量$exents不存在,你也可以调用\Event::listen()。
$events->listen('laravels.received_request', function (\\Illuminate\\Http\\Request $req) {

$req->query->set('get_key', 'hhxsv5');// 修改querystring
$req->request->set('post_key', 'hhxsv5'); // 修改post body

});

  • laravels.generated_response 在Laravel内核处理完请求后,将 Illuminate\Http\Response 转成 swoole_http_response 之前(下一步将响应给客户端)。

$events->listen('laravels.generated_response', function (\\Illuminate\\Http\\Request $req, \Symfony\Component\HttpFoundation\Response $rsp) {

$rsp->headers->set('header-key', 'hhxsv5');// 修改header

});

自定义的异步事件

事件监听的处理能力受Task进程数影响,需合理设置 task_worker_num

1.创建事件类。

use Hhxsv5LaravelSSwooleTaskEvent;
class TestEvent extends Event {

private $data;
public function __construct($data) {
    $this->data = $data;
}
public function getData() {
    return $this->data;
}

}

2.创建监听器类。

use Hhxsv5LaravelSSwooleTaskEvent;
use Hhxsv5LaravelSSwooleTaskListener;
class TestListener1 extends Listener {

public function handle(Event $event) {
    \\Log::info(\_\_CLASS\_\_ . ':handle start', \[$event->getData()\]);
    sleep(2);// 模拟一些慢速的事件处理
    // throw new \\Exception('an exception'); //上层会自动忽略handle时抛出的异常
}

}

3.绑定事件与监听器。

// 在"config/laravels.php"中绑定事件与监听器,一个事件可以有多个监听器,多个监听器按顺序执行
[

// ...
'events' =\> \[
    \\App\\Tasks\\TestEvent::class => \[
        \\App\\Tasks\\TestListener1::class,
        //\\App\\Tasks\\TestListener2::class,
    \],
\],
// ...

];

4.触发事件。

// 实例化TestEvent并通过fire触发,此操作是异步的,触发后立即返回,由Task进程继续处理监听器中的handle逻辑
use Hhxsv5LaravelSSwooleTaskEvent;
$success = Event::fire(new TestEvent('event data'));
var_dump($success);//判断是否触发成功

异步的任务队列

异步任务的处理能力受Task进程数影响,需合理设置 task_worker_num

1.创建任务类。

use Hhxsv5LaravelSSwooleTaskTask;
class TestTask extends Task {

private $data;
public function __construct($data) {
    $this->data = $data;
}
public function handle() {
    \\Log::info(\_\_CLASS\_\_ . ':handle start', \[$this->data\]);
    sleep(2);// 模拟一些慢速的事件处理
    // throw new \\Exception('an exception'); //上层会自动忽略handle时抛出的异常
}

}

2.投递任务。

// 实例化TestTask并通过deliver投递,此操作是异步的,投递后立即返回,由Task进程继续处理TestTask中的handle逻辑
use Hhxsv5LaravelSSwooleTaskTask;
$ret = Task::deliver(new TestTask('task data'));
var_dump($ret);//判断是否投递成功

在你的项目中使用 swoole_http_server 实例

/**
* @var \swoole_http_server
*/
$swoole = app('swoole');// Singleton
var_dump($swoole->stats());

注意事项

  • 推荐通过 Illuminate\Http\Request 对象来获取请求信息,兼容$\_SERVER、$_GET、$\_POST、$_FILES、$\_COOKIE、$_REQUEST, 不能使用 $\_SESSION、$_ENV。

public function form(\Illuminate\Http\Request $request) {

$name = $request->input('name');
$all = $request->all();
$sessionId = $request->cookie('sessionId');
$photo = $request->file('photo');
$rawContent = $request->getContent();
//...

}

  • 推荐通过返回 Illuminate\Http\Response 对象来响应请求,兼容echo、vardump()、print_r(), 不能使用 函数像exit()、die()、header()、setcookie()、http_response_code()。

public function json() {

return response()->json(\['time' =\> time()\])->header('header1', 'value1')->withCookie('c1', 'v1');

}

  • 你声明的全局、静态变量必须手动清理或重置。
  • 无限追加元素到静态或全局变量中,将导致内存爆满。

// 某类
class Test {

public static $array = \[\];
public static $string = '';

}

// 某控制器
public function test(Request $req) {

// 内存爆满
Test::$array\[\] = $req->input('param1');
Test::$string .= $req->input('param2');

}

待办事项

  1. 针对MySQL/Redis的连接池。
  2. 包装MySQL/Redis/Http的协程客户端。

打赏

LaravelS:通过 Swoole 来加速 Laravel/Lumen

License

MIT

查看原文: LaravelS:通过 Swoole 来加速 Laravel/Lumen


原网址: 访问
创建于: 2019-04-07 21:44:56
目录: default
标签: 无

请先后发表评论
  • 最新评论
  • 总共0条评论