Swoole 是一款优秀的 PHP 扩展,利用其可以实现原生 PHP 很难做到的常驻服务和异步。正好我有个 Laravel 项目可以折腾,就研究了下。
点此加入我的企鹅群
Laravel 项目是基于 composer 的,所以我先帖下我的 composer.json 中的 require 声明:
{
"require": {
"php": "^7.1.3","cybercog/laravel-love": "^5.1","dingo/api": "~v2.0.0-alpha2","doctrine/dbal": "^2.8","fideloper/proxy": "^4.0","guzzlehttp/guzzle": "^6.3","infyomlabs/adminlte-templates": "5.6.x-dev","infyomlabs/laravel-generator": "5.6.x-dev","jeroennoten/laravel-adminlte": "^1.23","laravel/framework": "5.6.*","laravel/tinker": "^1.0","laravelcollective/html": "^5.6.0","lshorz/luocaptcha": "^1.0","overtrue/laravel-lang": "v3.0.08","overtrue/laravel-wechat": "^4.0","predis/predis": "^1.1","spatie/laravel-permission": "^2.17","tymon/jwt-auth": "~1.0.0-rc.2","yajra/laravel-datatables-buttons": "^4.0","yajra/laravel-datatables-oracle": "^8.7"
}
}
如果我们要开启 swoole,我们可选的包有这些:
swooletw/laravel-swoole
hhxsv5/laravel-s
但一般来说,项目中需要常驻容器的服务与每次均需重新构建的服务并不一样,所以我才剑走偏锋。
起步
我们需要将 public/index.php 替换成如下
<?php
use IlluminateHttpRequest;
use IlluminateHttpResponse;
define('LARAVEL_START',microtime(true));
require __DIR__ . '/../vendor/autoload.php';
$app = require_once __DIR__ . '/../bootstrap/app.php';
class Laravel
{
/**
* IlluminateFoundationApplication
*
* @var IlluminateFoundationApplication
*/
public $app;
/**
* AppHttpKernel
*
* @var AppHttpKernel
*/
public $kernel;
/**
* AppHttpRequestsRequest
*
* @var AppHttpRequestsRequest
*/
public $request;
/**
* IlluminateHttpJsonResponse
*
* @var IlluminateHttpJsonResponse
*/
public $response;
/**
* 构造
*
* @param IlluminateFoundationApplication $app
*/
public function __construct(IlluminateFoundationApplication $app)
{
$this->app = $app;
}
/**
* RUN
*
* @return void
*/
public function run()
{
SwooleRuntime::enableCoroutine(true);
$http = new swoole_http_server('127.0.0.1','80');
$http->set([
'document_root' => public_path('/'),'enable_static_handler' => true,]);
$http->on('request',function ($req,$res) {
try {
$kernel = $this->app->make(IlluminateContractsHttpKernel::class);
$get = $req->get ?? [];
$post = $req->post ?? [];
$input = array_merge($get,$post);
$cookie = $req->cookie ?? [];
$files = $req->files ?? [];
$server = $req->server ?? [];
$request = Request::create($req->server['request_uri'],$req->server['request_method'],$input,$cookie,$files,$server);
if (isset($req->header['x-requested-with']) && $req->header['x-requested-with'] == "XMLHttpRequest") {
$request->headers->set('X-Requested-With',"XMLHttpRequest",true);
}
if (isset($req->header['accept']) && $req->header['accept']) {
$request->headers->set('Accept',$req->header['accept'],true);
}
$response = $kernel->handle($request);
$res->status($response->getStatusCode());
foreach ($response->headers->allPreserveCaseWithoutCookies() as $name => $values) {
foreach ($values as $value) {
$res->header($name,$value,false);
}
}
foreach ($response->headers->getCookies() as $cookie) {
$res->header('Set-Cookie',$cookie->getName() . strstr($cookie,'='),false);
}
dump(time());
$res->end($response->getContent());
$this->app->forgetInstance('request');
} catch (throwable $e) {
echo $e->getMessage();
echo PHP_EOL;
echo $e->getFile();
echo PHP_EOL;
echo $e->getLine();
echo PHP_EOL;
}
});
$http->start();
}
}
(new Laravel($app))->run();
运行时发现大多数页面均没有问题,只有几个用了 infyomlabs/laravel-generator 产生的列表页,AJAX 拉取 JSON 时却返回了 HTML。
排查
在有问题页面的 controller 代码中,找到如下
/**
- Display a listing of the Star.
-
- @param StarDataTable $starDataTable
- @return Response
*/
public function index(StarDataTable $starDataTable)
{
return $starDataTable->render('stars.index');
}
定位 StarDataTable::render() 到了
/**
* Process dataTables needed render output.
*
* @param string $view
* @param array $data
* @param array $mergeData
* @return mixed
*/
public function render($view,$data = [],$mergeData = [])
{
if ($this->request()->ajax() && $this->request()->wantsJson()) {
return app()->call([$this,'ajax']);
}
...
}
这是判断 $this->request() 是不是 XHR 请求,且 Accept 请求头声明了 application/json
而 $this->request() 实现如下
/**
- Get DataTables Request instance.
-
- @return YajraDataTablesUtilitiesRequest
*/
public function request()
{
return $this->request ?: $this->request = resolve('datatables.request');
}
不难看出,如果第一次构建,会走到
$this->request = resolve('datatables.request');
而 resolve 的实现是啥?
if (! function_exists('resolve')) {
/**
* Resolve a service from the container.
*
* @param string $name
* @return mixed
*/
function resolve($name)
{
return app($name);
}
}
就是从容器中取出 datatables.request 的过程。
所以我们只需让每次请求结束,$app 容器忘掉 datatables.request 就好了
改进
增加遗忘 datatables.request
$res->end($response->getContent());
$this->app->forgetInstance('request');
$this->app->forgetInstance('datatables.request');
$this->app->forgetInstance(DingoApiHttpMiddlewareRequest::class);
完整最终版:
<?php
use IlluminateHttpRequest;
use IlluminateHttpResponse;
define('LARAVEL_START',false);
}
dump(time());
$res->end($response->getContent());
$this->app->forgetInstance('request');
//$this->app->forgetInstance('session');
//$this->app->forgetInstance('session.store');
//$this->app->forgetInstance('cookie');
$this->app->forgetInstance('datatables.request');
$this->app->forgetInstance(DingoApiHttpMiddlewareRequest::class);
//$kernel->terminate($request,$response);
} catch (throwable $e) {
echo $e->getMessage();
echo PHP_EOL;
echo $e->getFile();
echo PHP_EOL;
echo $e->getLine();
echo PHP_EOL;
}
});
$http->start();
}
}
(new Laravel($app))->run();
测试
比原生 laravel 确实快不少(这还有 4 句 SQL 查询)

- 注,此处给出的代码可以借鉴,但未经长期验证。且不同项目实际用到的包不同,需要在调试过程中 debug 容器中的服务提供者,和追踪代码来调优。
已知问题
点关注,不迷路
好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是人才。之前说过,PHP方面的技术点很多,也是因为太多了,实在是写不过来,写过来了大家也不会看的太多,所以我这里把它整理成了PDF和文档,如果有需要的可以
点击进入暗号: PHP+「平台」


更多学习内容可以访问【对标大厂】精品PHP架构师教程目录大全,只要你能看完保证薪资上升一个台阶(持续更新)
以上内容希望帮助到大家,很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要的可以加入我的 PHP技术交流群 (编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|