Laravel Octane 源码简单分析
php artisan octane:start 基于swoole模式来分析
运行这个命令之后,会执行下图中的 handle
方法,最终会执行startSwooleServer
方法
这个方法中调用了octane:swoole
command命令 运行 handle
方法
public function handle(
ServerProcessInspector $inspector,
ServerStateFile $serverStateFile,
SwooleExtension $extension
) {
//检测是否有swoole扩展
if (! $extension->isInstalled()) {
$this->error('The Swoole extension is missing.');
return 1;
}
//检测服务是否正在运行
if ($inspector->serverIsRunning()) {
$this->error('Server is already running.');
return 1;
}
if (config('octane.swoole.ssl', false) === true && ! defined('SWOOLE_SSL')) {
$this->error('You must configure Swoole with `--enable-openssl` to support ssl.');
return 1;
}
//服务状态写入文件
$this->writeServerStateFile($serverStateFile, $extension);
//清除环境变量
$this->forgetEnvironmentVariables();
//启动子进程
$server = tap(new Process([
(new PhpExecutableFinder)->find(),
...config('octane.swoole.php_options', []),
config('octane.swoole.command', 'swoole-server'),
$serverStateFile->path(),
], realpath(__DIR__.'/../../bin'), [
'APP_ENV' => app()->environment(),
'APP_BASE_PATH' => base_path(),
'LARAVEL_OCTANE' => 1,
]))->start();
//这里是监听子进程运行状态
return $this->runServer($server, $inspector, 'swoole');
}
子进程是执行swoole-server
这个文件
只分析 workerstart
和request
这两个监听
require_once __DIR__.'/WorkerState.php';
$workerState = new WorkerState;
$workerState->cacheTable = require __DIR__.'/createSwooleCacheTable.php';
$workerState->timerTable = $timerTable;
$workerState->tables = require __DIR__.'/createSwooleTables.php';
$server->on('workerstart', fn (Server $server, $workerId) =>
(fn ($basePath) => (new OnWorkerStart(
new SwooleExtension, $basePath, $serverState, $workerState
))($server, $workerId))($bootstrap($serverState))
);
在这个workerstart
这个监听中 有 实例化OnWorkerStart
类,在这个类中给$workerState
设置了worker
属性
public function __invoke($server, int $workerId)
{
$this->clearOpcodeCache();
$this->workerState->server = $server;
$this->workerState->workerId = $workerId;
$this->workerState->workerPid = posix_getpid();
//这里是重点
$this->workerState->worker = $this->bootWorker($server);
$this->dispatchServerTickTaskEverySecond($server);
$this->streamRequestsToConsole($server);
if ($this->shouldSetProcessName) {
$isTaskWorker = $workerId >= $server->setting['worker_num'];
$this->extension->setProcessName(
$this->serverState['appName'],
$isTaskWorker ? 'task worker process' : 'worker process',
);
}
}
在 bootWorker
这个方法中
protected function bootWorker($server)
{
try {
return tap(new Worker(
new ApplicationFactory($this->basePath),
$this->workerState->client = new SwooleClient
))->boot([
'octane.cacheTable' => $this->workerState->cacheTable,
Server::class => $server,
WorkerState::class => $this->workerState,
]);
} catch (Throwable $e) {
Stream::shutdown($e);
$server->shutdown();
}
}
在这里面会实例化 Worker
类 并调用boot
方法,这个方法做了以下操作
public function boot(array $initialInstances = []): void
{
//这里会初始化app容器
$this->app = $app = $this->appFactory->createApplication(
array_merge(
$initialInstances,
[Client::class => $this->client],
)
);
$this->dispatchEvent($app, new WorkerStarting($app));
}
会执行ApplicationFactory
类中的createApplication
这个方法,这个方法做了以下操作
public function createApplication(array $initialInstances = []): Application
{
$paths = [
$this->basePath.'/.laravel/app.php',
$this->basePath.'/bootstrap/app.php',
];
foreach ($paths as $path) {
if (file_exists($path)) {
//重点是这里
return $this->warm($this->bootstrap(require $path, $initialInstances));
}
}
throw new RuntimeException("Application bootstrap file not found in 'bootstrap' or '.laravel' directory.");
}
会给app容器预热(预加载)一些单例,会在拿octane.php
配置文件中的warm
数组
在上图bootstrap
方法中,在这个方法中会执行 getBootstrappers
方法
protected function getBootstrappers(Application $app): array
{
$method = (new ReflectionObject(
$kernel = $app->make(HttpKernelContract::class)
))->getMethod('bootstrappers');
$method->setAccessible(true);
return $this->injectBootstrapperBefore(
//拿到所有的Provider类,在app.php配置文件中的providers数组 和 自动注册的Provider类(5.5版本以后不用手动加到app.php中就是在这里做了处理)
RegisterProviders::class,
//创建Request对象到app容器
SetRequestForConsole::class,
//执行了Http\Kernel类中的 bootstrappers方法 拿到数组
$method->invoke($kernel)
);
}
Http\Kernel
类中的 bootstrappers
方法
protected $bootstrappers = [//加载env配置文件
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
//加载config配置文件
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
//处理异常
\Illuminate\Foundation\Bootstrap\HandleExceptions::class,
//注册静态代理
\Illuminate\Foundation\Bootstrap\RegisterFacades::class,
//执行每个Provider的register方法
\Illuminate\Foundation\Bootstrap\RegisterProviders::class,
//执行每个Provider的boot方法
\Illuminate\Foundation\Bootstrap\BootProviders::class,
];
然后就会在bootstrapWith
方法中make这些类去完成注册 然后标记了$this->hasBeenBootstrapped = true
workerstart
监听就结束了
接下来就是request
监听
$server->on('request', function ($request, $response) use ($server, $workerState, $serverState) {
$workerState->lastRequestTime = microtime(true);
if ($workerState->timerTable) {
$workerState->timerTable->set($workerState->workerId, [
'worker_pid' => $workerState->workerPid,
'time' => time(),
'fd' => $request->fd,
]);
}
//重点是这里
$workerState->worker->handle(...$workerState->client->marshalRequest(new RequestContext([
'swooleRequest' => $request,
'swooleResponse' => $response,
'publicPath' => $serverState['publicPath'],
'octaneConfig' => $serverState['octaneConfig'],
])));
if ($workerState->timerTable) {
$workerState->timerTable->del($workerState->workerId);
}
});
就是执行Worker
类中的handle
方法
public function handle(Request $request, RequestContext $context): void
{
//判断是不是静态文件
if ($this->client instanceof ServesStaticFiles &&
$this->client->canServeRequestAsStaticFile($request, $context)) {
$this->client->serveStaticFile($request, $context);
return;
}
//这个里面是给绑定容器实例给app
//然后Facade清除所有解析的实例 重新绑定app
//clone是为了防止污染app容器 可以做到复用的效果
CurrentApplication::set($sandbox = clone $this->app);
$gateway = new ApplicationGateway($this->app, $sandbox);
//下面的就是去找到控制器执行方法得到响应
try {
$responded = false;
ob_start();
$response = $gateway->handle($request);
$output = ob_get_contents();
ob_end_clean();
$this->client->respond(
$context,
$octaneResponse = new OctaneResponse($response, $output),
);
$responded = true;
$this->invokeRequestHandledCallbacks($request, $response, $sandbox);
$gateway->terminate($request, $response);
} catch (Throwable $e) {
$this->handleWorkerError($e, $sandbox, $request, $context, $responded);
} finally {
$sandbox->flush();
$this->app->make('view.engine.resolver')->forget('blade');
$this->app->make('view.engine.resolver')->forget('php');
// After the request handling process has completed we will unset some variables
// plus reset the current application state back to its original state before
// it was cloned. Then we will be ready for the next worker iteration loop.
unset($gateway, $sandbox, $request, $response, $octaneResponse, $output);
CurrentApplication::set($this->app);
}
}
简单分析一下这个 $response = $gateway->handle($request);
public function handle(Request $request): Response
{
//這個地方会触发事件 去flush已加载好的一些单例状态 防止被污染
$this->dispatchEvent($this->sandbox, new RequestReceived($this->app, $this->sandbox, $request));
if (Octane::hasRouteFor($request->getMethod(), '/'.$request->path())) {
return Octane::invokeRoute($request, $request->getMethod(), '/'.$request->path());
}
return tap($this->sandbox->make(Kernel::class)->handle($request), function ($response) use ($request) {
$this->dispatchEvent($this->sandbox, new RequestHandled($this->sandbox, $request, $response));
});
});
}
这里有又到了http\Kernel
laravel自己的执行流程
在这个里面会执行sendRequestThroughRouter
这个方法, 然后执行到这个bootstrap
方法 会通过 hasBeenBootstrapped
方法 判断是否已经加载 所以不会重复注册providers
大概就分析这些吧,第一次写博客,不怎么会写,有问题的地方请大家帮忙指正
本作品采用《CC 协议》,转载必须注明作者和本文链接
写得好,多写点儿!
这扩展说实在还是没解决阻塞的问题,能支持协程就好了。