Laravel编译级性能的服务模式可行性与前景
利益相关
PRipple协程 推介
不久前我写了一篇文章《无需修改任何代码和扩展将你的Laravel项目性能提高20倍》
受到了许多朋友的关注, 事实上性能提升只是它的附加项, 真正的大杀器是它的服务模式设计
今天就这个话题聊聊Laravel服务模式的可行性
毋庸置疑Laravel是最优雅的PHP框架, 它的设计思想和代码质量都非常优秀, 但在性能方面的表现却不尽如人意,若从Laravel内核开发者的角度来看
他们是否放弃了对性能的考虑?
我的观点是否定的,官方并未强调过Laravel应该在FPM/Swoole/FrankenPHP环境运行, 他们更注重的是
专注于设计本身, 我反而觉得就是这种专注于设计本身而非语言
的态度使得Laravel成为了最优雅的框架
现代化框架的设计包容性
这就是我所说的专注于设计本身
, 即包容多种构建模式
从我们从FPM开始, Laravel提供了一个入口文件public/index.php
<?php
use Illuminate\Http\Request;
\define('LARAVEL_START', \microtime(true));
// Determine if the application is in maintenance mode...
if (\file_exists($maintenance = __DIR__ . '/../storage/framework/maintenance.php')) {
require $maintenance;
}
// Register the Composer autoloader...
require __DIR__ . '/../vendor/autoload.php';
// Bootstrap Laravel and handle the request...
(require_once __DIR__ . '/../bootstrap/app.php')->handleRequest(Request::capture());
我们都知道,执行该文件可能会发生以下事情
容器初始化=>服务注册(配置=>路由=>中间件)=>控制器=>渲染=>返回
从该流程中包括模板引擎的甚至在整个项目中都找不到一句echo
语句
这些MVC框架在做一件什么事? 它们只是为FPM提供了一个驱动入口以实现
$application->handle($request);
这就是FPM的工作方式:
每次处理请求时,都会重新加载所有的文件和依赖
这导致了重复的文件解析和加载,影响了整体性能。
可行的取代方案
例如考虑这个流程
容器初始化=>服务注册(配置=>路由=>中间件)=>控制器=>渲染=>返回
能否在服务注册
这一阶段主动监听HTTP服务,并在服务注册
完成后,将服务注册的结果缓存到内存中?由HTTP服务接管后续的请求处理,这样就不需要每次请求都重新加载所有的文件和依赖?
答案是可以的, PHP许多年前就已经支持了Socket模块,实际上这就是上述Swoole/Webman正在做的事,
在广泛的实践中, 服务模式的性能提升是非常显著的,
这也为PHP开发者带来了更多的选择和挑战: 注重内存泄漏与生命周期的管理
服务模式的发展
上述服务模式也称为常驻内存模式
或PHP-CLI
模式, PHP官方在这一领域也提供了广泛支持, 也有如Swoole
Webman
AMPHP
等这类许多优秀的先行者, 感谢他们在这一领域的贡献与推动,使得PHP从PC-WEB时代走向了Serverless时代也有一席之地, 也让PHP开发者面对游戏/物联网等这类高并发场景时,有了实践的可能性
Fiber与Parallel的出现
Fiber与Parallel随着PHP8.1
的发布而出现, 为PHP在协程/多线程方面提供了更多的支持, 迄今为止,PHP8.1
已经发布有3年之久, 经过多次迭代也象征着Fiber走向成熟的标志
正如上篇文章所讲述, 可以使用PRipple协程引擎为Laravel提速数倍,也使用了服务模式, 不同其他引擎,PRipple是一个基于PHP8.1的Fiber实现的100%PHP代码协程引擎
且服务模式还支持更多的高耦合开发手段
PRipple的食用方法
例(1) 使用协程MySQL并(兼容Eloquent)
PRipple支持将数据库driver从
mysql
替换为mysql-amp
实现协程MySQL并兼容ORM
目前使用该方法需要掌握足够的协程编程知识,需要注重事务的上下文隔离,未来会提供更多的文档与示例
例(2) 使用递延实现异步处理任务
public function index() : JsonResponse
{
\P\defer(function(){
// 异步处理耗时的发送邮件/通知等轻任务
});
return Response::json(['code' => 0, 'msg' => 'success']);
}
例(3) 高并发http-client请求(内置连接池)
public function index() : JsonResponse
{
// 并发发起http请求
for($i = 0; $i < 100; $i ++){
\P\async(function(){
$response = \P\Plugin::Guzzle()->get('https://www.baidu.com/');
//TODO: 处理响应
});
}
return Response::json(['code' => 0, 'msg' => 'success']);
}
例(4) 自定义服务
Ws.php
<?php declare(strict_types=1);
namespace App\Server;
use P\Net;
use Psc\Core\WebSocket\Server\Connection;
use Psc\Core\WebSocket\Server\Server;
use Psc\Worker\Command;
use Psc\Worker\Manager;
use Psc\Worker\Worker;
class WsWorker extends Worker
{
private Server $wsServer;
private array $connections = [];
public function register(Manager $manager): void
{
// 主进程创建服务
$this->wsServer = Net::WebSocket()->server('ws://127.0.0.1:8001', null);
}
public function boot(): void
{
$this->wsServer->onMessage(function (string $content, Connection $connection) {
$connection->send("response > {$content}");
});
$this->wsServer->onConnect(function (Connection $connection) {
$this->connections[$connection->getId()] = $connection;
});
$this->wsServer->onClose(function (Connection $connection) {
unset($this->connections[$connection->getId()]);
});
// 服务进程监听
$this->wsServer->listen();
}
// 监听其他进程发送来的指令
public function onCommand(Command $workerCommand): void
{
if ($workerCommand->name === 'sendMessageToAll') {
foreach ($this->connections as $connection) {
$connection->send($workerCommand->arguments['message']);
}
}
}
public function getName(): string
{
return 'ws-server';
}
public function getCount(): int
{
return 1;
}
public function onReload(): void
{
exit(0);
}
}
注册服务
class AppServiceProvider extends ServiceProvider
{
public function boot(Manager $manager): void
{
$manager->addWorker(new \App\ServerWsWorker());
}
}
使用方法
Route::post('/ws-xhr', function (Request $request, \Psc\Drive\Laravel\Worker $httpWorker) {
// 构建指令
$command = Command::make('sendMessageToAll', ['message' => 'post message ' . $request->post('message')]);
// 跨服务服务发送指令
$httpWorker->commandToWorker($command, 'ws-server');
});
例(5)多线程支持
待文档更新
总结
通过上述的例子, 我们可以看到服务模式的强大之处, 也可以看到服务模式的可行性与前景,
这也是我写这篇文章的初衷, 希望能够给大家带来一些启发与思考, 也希望能够有更多的朋友加入到这个领域中来
推荐文章: