主要是分析默认的http
服务启动的过程。
php bin/hyperf.php start // 启动服务
根据系统流程分析一文可以知道这个命令是通过配置
(包括了ConfigProvider
返回的配置)或者注解
注册的,所以我们可以查找下相关配置文件和类。
经过文件搜索找到了如下代码:
namespace Hyperf\Server;
class ConfigProvider
{
public function __invoke(): array
{
return [
'commands' => [
StartServer::class,
],
];
}
}
我们继续跟踪StartServer::class
这个类
class StartServer extends Command
{
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
parent::__construct('start');
$this->setDescription('Start hyperf servers.');
}
// 执行入口
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->checkEnvironment($output);
$serverFactory = $this->container->get(ServerFactory::class)
->setEventDispatcher($this->container->get(EventDispatcherInterface::class))
->setLogger($this->container->get(StdoutLoggerInterface::class));
$serverConfig = $this->container->get(ConfigInterface::class)->get('server', []);
if (! $serverConfig) {
throw new InvalidArgumentException('At least one server should be defined.');
}
$serverFactory->configure($serverConfig);
Coroutine::set(['hook_flags' => swoole_hook_flags()]);
$serverFactory->start();
return 0;
}
}
其中ServerFactory
是具体的类,$serverConfig
是我们在config/autoload/server.php
定义的配置
// server.php
return [
'mode' => SWOOLE_PROCESS,
'servers' => [
[
'name' => 'http',
'type' => Server::SERVER_HTTP,
'host' => '0.0.0.0',
'port' => 9501,
'sock_type' => SWOOLE_SOCK_TCP,
'callbacks' => [
Event::ON_REQUEST => [Hyperf\HttpServer\Server::class, 'onRequest'],
],
],
],
'settings' => [
Constant::OPTION_ENABLE_COROUTINE => true,
Constant::OPTION_WORKER_NUM => swoole_cpu_num(),
Constant::OPTION_PID_FILE => BASE_PATH . '/runtime/hyperf.pid',
Constant::OPTION_OPEN_TCP_NODELAY => true,
Constant::OPTION_MAX_COROUTINE => 100000,
Constant::OPTION_OPEN_HTTP2_PROTOCOL => true,
Constant::OPTION_MAX_REQUEST => 100000,
Constant::OPTION_SOCKET_BUFFER_SIZE => 2 * 1024 * 1024,
Constant::OPTION_BUFFER_OUTPUT_SIZE => 2 * 1024 * 1024,
],
'callbacks' => [
Event::ON_WORKER_START => [Hyperf\Framework\Bootstrap\WorkerStartCallback::class, 'onWorkerStart'],
Event::ON_PIPE_MESSAGE => [Hyperf\Framework\Bootstrap\PipeMessageCallback::class, 'onPipeMessage'],
Event::ON_WORKER_EXIT => [Hyperf\Framework\Bootstrap\WorkerExitCallback::class, 'onWorkerExit'],
],
];
接下来需要重点分析下$serverFactory->configure($serverConfig)
// ServerFactory.php
public function configure(array $config)
{
$this->config = new ServerConfig($config);
$this->getServer()->init($this->config);
}
通过代码分析$this->getServer()
指的是Hyperf\Server\Server
并调用它的初始化init
// Server.php
public function init(ServerConfig $config): ServerInterface
{
$this->initServers($config);
return $this;
}
protected function initServers(ServerConfig $config)
{
$servers = $this->sortServers($config->getServers());
foreach ($servers as $server) {
...
if (! $this->server instanceof SwooleServer) {
$this->server = $this->makeServer($type, $host, $port, $config->getMode(), $sockType);
$callbacks = array_replace($this->defaultCallbacks(), $config->getCallbacks(), $callbacks);
$this->registerSwooleEvents($this->server, $callbacks, $name);
...
else {
$slaveServer = $this->server->addlistener($host, $port, $sockType);
$this->registerSwooleEvents($slaveServer, $callbacks, $name);
}
}
可以看出,这里会根据服务器配置文件生成对应的服务,并且注入相关的事件。也就是说可以同时支持多个协议和多个端口的启动和监听。根据上面的config/autoload/server.php
的配置文件可以知道servers
只配置了一个http
服务,并在callbacks
里面绑定了一个Event::ON_REQUEST
事件。
public function onRequest($request, $response): void
{
//
}
每当有新得请求进来的时候,底层的swoole
就会开一个协程去执行这个onRequest
方法, 类似如下伪代码:
// pseudo code
run(function() {
for (;;) {
$conn = $listener->accept(); // 接受请求,这里会阻塞
go(function() use($conn) { // 使用协程去执行
$request = new Swoole\Request(fread($conn, 64<<10)); // 读取消息并解析http协议为request对象
$response = new Swoole\Response(); // swoole的http响应对象
(new Hyperf\HttpServer\Server())->onRequest($request, $response); //执行绑定的方法
}
}
});
由于协程是单进程(线程)的,所以协程里面的逻辑不能包含阻塞逻辑,如果是阻塞的情况下是无法完成调度切换的,那么该进程就会退化为一次只能处理一个请求,类似php-fpm
的进程。如果协程的逻辑是异步的情况下,遇到了IO就会切换到下一个协程继续执行,大大的提高了并发处理能力。
ok,我们回过头来继续分析下OnRequest
方法里面的具体执行逻辑
public function onRequest($request, $response): void
{
try {
CoordinatorManager::until(Constants::WORKER_START)->yield();
[$psr7Request, $psr7Response] = $this->initRequestAndResponse($request, $response);
$psr7Request = $this->coreMiddleware->dispatch($psr7Request);
/** @var Dispatched $dispatched */
$dispatched = $psr7Request->getAttribute(Dispatched::class);
$middlewares = $this->middlewares;
if ($dispatched->isFound()) {
$registeredMiddlewares = MiddlewareManager::get($this->serverName, $dispatched->handler->route, $psr7Request->getMethod());
$middlewares = array_merge($middlewares, $registeredMiddlewares);
}
$psr7Response = $this->dispatcher->dispatch($psr7Request, $middlewares, $this->coreMiddleware);
} catch (Throwable $throwable) {
// Delegate the exception to exception handler.
$psr7Response = $this->exceptionHandlerDispatcher->dispatch($throwable, $this->exceptionHandlers);
} finally {
// Send the Response to client.
if (! isset($psr7Response)) {
return;
}
if (isset($psr7Request) && $psr7Request->getMethod() === 'HEAD') {
$this->responseEmitter->emit($psr7Response, $response, false);
} else {
$this->responseEmitter->emit($psr7Response, $response, true);
}
}
}
做的事情主要有以下几项
- 把
swoole
的response/request
转换成psr7的标准形式。 - 请求通过内核中间件
coreMiddleware
,在这个阶段会查找路由和相关的处理handler
- 请求通过全局中间件,由配置文件指定
config/autoload/middlewares
和当前的$serverName
决定。 - 请求通过注册中间件,最后到达指定的控制器方法。
- 返回标准的
psr7Response
响应。 - 最后通过
responseEmitter
响应提交器提交返回。
我们看下全局和注册中间件的处理逻辑
namespace Hyperf\Dispatcher;
class HttpDispatcher extends AbstractDispatcher
{
...
public function dispatch(...$params): ResponseInterface
{
[$request, $middlewares, $coreHandler] = $params;
$requestHandler = new HttpRequestHandler($middlewares, $coreHandler, $this->container);
return $requestHandler->handle($request);
}
}
$requestHandler->handle
继续跟踪下
namespace Hyperf\Dispatcher;
class HttpRequestHandler extends AbstractRequestHandler implements RequestHandlerInterface
{
public function handle(ServerRequestInterface $request): ResponseInterface
{
return $this->handleRequest($request);
}
// 处理请求
protected function handleRequest($request)
{
if (! isset($this->middlewares[$this->offset]) && ! empty($this->coreHandler)) {
$handler = $this->coreHandler;
} else {
$handler = $this->middlewares[$this->offset];
is_string($handler) && $handler = $this->container->get($handler);
}
if (! method_exists($handler, 'process')) {
throw new InvalidArgumentException(sprintf('Invalid middleware, it has to provide a process() method.'));
}
return $handler->process($request, $this->next());
}
protected function next(): self
{
++$this->offset;
return $this;
}
}
可以看到通过offset
不断递增,执行完所有的中间件,当没有中间件的时候,最后执行了$this->coreHandler
的process
方法。
我们知道$this->coreHandler
就是coreMiddleware
内核中间件,所以我们继续跟踪他的process
方法
// CoreMiddleware.php
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$request = Context::set(ServerRequestInterface::class, $request);
/** @var Dispatched $dispatched */
$dispatched = $request->getAttribute(Dispatched::class);
if (! $dispatched instanceof Dispatched) {
throw new ServerException(sprintf('The dispatched object is not a %s object.', Dispatched::class));
}
$response = null;
switch ($dispatched->status) {
case Dispatcher::NOT_FOUND:
$response = $this->handleNotFound($request);
break;
case Dispatcher::METHOD_NOT_ALLOWED:
$response = $this->handleMethodNotAllowed($dispatched->params, $request);
break;
case Dispatcher::FOUND:
$response = $this->handleFound($dispatched, $request);
break;
}
if (! $response instanceof ResponseInterface) {
$response = $this->transferToResponse($response, $request);
}
return $response->withAddedHeader('Server', 'Hyperf');
}
我们继续分析$this->handleFound()
protected function handleFound(Dispatched $dispatched, ServerRequestInterface $request)
{
if ($dispatched->handler->callback instanceof Closure) {
$parameters = $this->parseClosureParameters($dispatched->handler->callback, $dispatched->params);
$response = call($dispatched->handler->callback, $parameters);
} else {
[$controller, $action] = $this->prepareHandler($dispatched->handler->callback);
$controllerInstance = $this->container->get($controller);
if (! method_exists($controllerInstance, $action)) {
// Route found, but the handler does not exist.
throw new ServerErrorHttpException('Method of class does not exist.');
}
$parameters = $this->parseMethodParameters($controller, $action, $dispatched->params);
$response = $controllerInstance->{$action}(...$parameters);
}
return $response;
}
我们知道Dispatched
包括了3个字段, [Dispatcher::FOUND, $handler, ['varName' => 'value', ...]]
所以,一帮情况就是执行我们在路由表里面指定的控制器方法或者闭包方法。
至此,整个服务启动分析完毕。