laravel-swoole 使用
最近常用的 PHP 框架是 Laravel,而 PHP 框架本身启动都要加载不少文件,往往 QPS 不会很高。
Swoole 扩展本身就常驻内存,异步执行,可以为项目提高不少性能,刚好也有轮子可以使用 swooletw/laravel-swoole,做个记录。
安装非常简单,按照文档来就行。
Swoole 扩展
# 安装
sudo apt-get install php7.2-dev php-pear
sudo pecl install swoole
# 扩展配置
php --ini
sudo echo "extension=swoole.so" >> /path/to/php.ini
# 查看是否启动
php -m | grep swoole
php --ri swoole
Laravel 配置
composer require swooletw/laravel-swoole
在 config/app.php
文件添加 providers:
[
...
'providers' => [
SwooleTW\Http\LaravelServiceProvider::class,
],
...
]
新增配置文件 config/swoole_http.php
,详细:
return [
'server' => [
'host' => env('SWOOLE_HTTP_HOST', '127.0.0.1'),
'port' => env('SWOOLE_HTTP_PORT', '1215'),
'options' => [
'pid_file' => env('SWOOLE_HTTP_PID_FILE', base_path('storage/logs/swoole_http.pid')),
'log_file' => env('SWOOLE_HTTP_LOG_FILE', base_path('storage/logs/swoole_http.log')),
'daemonize' => env('SWOOLE_HTTP_DAEMONIZE', true),
'reactor_num' => env('SWOOLE_HTTP_REACTOR_NUM', swoole_cpu_num() * 2),
'worker_num' => env('SWOOLE_HTTP_WORKER_NUM', swoole_cpu_num() * 2),
'task_worker_num' => env('SWOOLE_HTTP_TASK_WORKER_NUM', swoole_cpu_num() * 2),
'package_max_length' => 20 * 1024 * 1024,
'buffer_output_size' => 10 * 1024 * 1024,
'socket_buffer_size' => 128 * 1024 * 1024,
'max_request' => 3000,
'send_yield' => true,
'ssl_cert_file' => null,
'ssl_key_file' => null,
],
],
'pre_resolved' => [
'view', 'files', 'session', 'session.store', 'routes',
'db', 'db.factory', 'cache', 'cache.store', 'config', 'cookie',
'encrypter', 'hash', 'router', 'translator', 'url', 'log',
],
'providers' => [
Illuminate\Auth\AuthServiceProvider::class,
App\Providers\AuthServiceProvider::class,
Tymon\JWTAuth\Providers\LaravelServiceProvider::class,
],
];
启动命令:php artisan swoole:http {start|stop|restart|reload|infos}
更多的信息以及注意事项可以到文档或者 Wiki 看下,里面说得比较详细,地址。
遇到的坑
全局变量
已经换成 Swoole 的运行方式了,所以全局或者单例变量都是共用的,会出现混淆的情况,例如已经登录的用户,别人无需登录便可访问。
解决很简单,配置 providers
就行,每个请求下都会重新注册。
Nginx 配置
还是对 Nginx 的配置不太熟悉,在本地设置转发代理:
proxy_pass http://localhost:1215;
显示 502 错误,查看日志显示 no resolver defined to resolve localhost ...
。
查了下,是因为 proxy_pass
如果使用域名的话,需要使用 resolver
指令解析变量中的域名,如果换成 IP 就没问题 http://127.0.0.1:1215
。
不过我一般会用 Docker 来对项目打包,可以使用 upstream
来解决域名的问题。
upstream php.swoole {
server localhost:1215;
}
server {
...
location @swoole {
...
proxy_pass http://php.swoole;
}
...
}
分页参数
具体表现为:不管请求传递的 page
值是多少,最终拿到的还是 page=1
的数据。
这个之前有人提过 issue,在最后,作者说在 v2.3.7
版本已经解决,不过我目前使用的版本是 v2.5.0
,问题依然存在。
问题原因是 Illuminate/Pagination/PaginationServiceProvider.php
文件中这几行代码:
Paginator::currentPageResolver(function ($pageName = 'page') {
$page = $this->app['request']->input($pageName);
if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) {
return (int) $page;
}
return 1;
});
目前的解决方法是重写分页函数,例如 simplePaginate
。
新建文件 app/Models/Builder.php
。
namespace App\Models;
use Illuminate\Database\Eloquent\Builder as BaseBuilder;
class Builder extends BaseBuilder
{
public function simplePaginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
{
$request = request();
$page = $page ?: $request->get($pageName);
if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) {
$page = (int)$page;
} else {
$page = 1;
}
$this->skip(($page - 1) * $perPage)->take($perPage + 1);
return $this->simplePaginator($this->get($columns), $perPage, $page, [
'path' => $request->url(),
'pageName' => $pageName,
]);
}
}
更新文件 app/Models/Post.php
。
namespace App\Models;
use Illuminate\Database\Eloquent\Model as BaseModel;
class Post extends BaseModel
{
public function newEloquentBuilder($query)
{
return new Builder($query);
}
}
项目比较小,目前就遇到这些坑。