This commit is contained in:
想打瞌睡 2024-07-11 17:31:38 +09:00
parent e84acb95b9
commit 3500b5eec0
5 changed files with 1117 additions and 170 deletions

View File

@ -12,7 +12,7 @@ export default [
{ text: '初始化依赖注入 (DI) 容器', link: hyperf + 'DI' }, { text: '初始化依赖注入 (DI) 容器', link: hyperf + 'DI' },
{ text: '初始化容器类', link: hyperf + 'container' }, { text: '初始化容器类', link: hyperf + 'container' },
{ text: '启动服务', link: hyperf + '启动服务' }, { text: '启动服务', link: hyperf + '启动服务' },
{ text: '请求', link: hyperf + '请求' }, { text: '接收请求', link: hyperf + '接收请求' },
{ text: '路由寻址', link: hyperf + '路由寻址' }, { text: '路由寻址', link: hyperf + '路由寻址' },
{ text: '中间件', link: hyperf + '中间件' }, { text: '中间件', link: hyperf + '中间件' },
{ text: '响应', link: hyperf + '响应' }, { text: '响应', link: hyperf + '响应' },
@ -20,6 +20,7 @@ export default [
{ text: '附录2 注解命令获取', link: hyperf + '附录2 注解命令获取' }, { text: '附录2 注解命令获取', link: hyperf + '附录2 注解命令获取' },
{ text: '附录3 命令注册Application', link: hyperf + '附录3 命令注册Application' }, { text: '附录3 命令注册Application', link: hyperf + '附录3 命令注册Application' },
{ text: '附录4 Application Run方法', link: hyperf + '附录4 Application_Run' }, { text: '附录4 Application Run方法', link: hyperf + '附录4 Application_Run' },
{ text: '附录5 初始化中间件', link: hyperf + '附录5 初始化中间件' },
] ]
}, },
{ {

View File

@ -527,6 +527,7 @@ class ServerFactory
::: details 点我查看`ServerConfig` ::: details 点我查看`ServerConfig`
::: code-group ::: code-group
```php [ServerConfig] ```php [ServerConfig]
namespace Hyperf\Server; namespace Hyperf\Server;
@ -630,6 +631,7 @@ class ServerConfig implements Arrayable
} }
} }
``` ```
```php [Server build] ```php [Server build]
namespace Hyperf\Server; namespace Hyperf\Server;
@ -771,6 +773,7 @@ class Port
} }
} }
``` ```
::: :::
2. 根据`Server`配置的服务类型,实例化对应服务,调用服务的`init`方法 2. 根据`Server`配置的服务类型,实例化对应服务,调用服务的`init`方法
::: tip ::: tip
@ -786,9 +789,11 @@ class Port
// $this->getServer()返回的是一个Hyperf/Server/Server类实例 // $this->getServer()返回的是一个Hyperf/Server/Server类实例
$this->getServer()->init($this->config); $this->getServer()->init($this->config);
``` ```
> 文件位置:/vendor/hyperf/server/src/Server.php > 文件位置:/vendor/hyperf/server/src/Server.php
::: code-group ::: code-group
```php [init方法] ```php [init方法]
public function init(ServerConfig $config): ServerInterface public function init(ServerConfig $config): ServerInterface
{ {
@ -797,6 +802,7 @@ public function init(ServerConfig $config): ServerInterface
return $this; return $this;
} }
``` ```
```php [initServers方法] ```php [initServers方法]
protected function initServers(ServerConfig $config) protected function initServers(ServerConfig $config)
{ {
@ -848,6 +854,7 @@ protected function initServers(ServerConfig $config)
} }
} }
``` ```
```php [sortServer方法] ```php [sortServer方法]
protected function sortServers(array $servers): array protected function sortServers(array $servers): array
{ {
@ -875,6 +882,7 @@ protected function sortServers(array $servers): array
return $sortServers; return $sortServers;
} }
``` ```
```php [Server类源码] ```php [Server类源码]
namespace Hyperf\Server; namespace Hyperf\Server;
@ -1072,28 +1080,35 @@ class Server implements ServerInterface
} }
} }
``` ```
::: :::
分析`sortServicers`方法, 分析`sortServicers`方法, 对`Server`进行排序,确保 `HTTP` 服务器和 `WebSocket` 服务器按特定顺序排列
```php ```php
// $config->getServers()返回的是一个Hyperf\Server\Port类对象数组
$servers = $this->sortServers($config->getServers()); $servers = $this->sortServers($config->getServers());
``` ```
这里返回排好序的`Port`类对象数组。 这里返回排好序的`Port`类对象数组。
之后循环遍历`$servers`数组,每个`value`是一个`Port`对象,走`if` 之后循环遍历`$servers`数组,每个`value`是一个`Port`对象,走`if`
::: code-group
```php ```php [启动Server]
foreach ($servers as $server) { foreach ($servers as $server) { // [!code focus]
// 获取服务名
$name = $server->getName(); $name = $server->getName();
// 获取服务类型http or websocket
$type = $server->getType(); $type = $server->getType();
// 获取绑定的主机地址
$host = $server->getHost(); $host = $server->getHost();
// 获取端口
$port = $server->getPort(); $port = $server->getPort();
// 获取套接字类型
$sockType = $server->getSockType(); $sockType = $server->getSockType();
// 获取回调信息
$callbacks = $server->getCallbacks(); $callbacks = $server->getCallbacks();
if (! $this->server instanceof SwooleServer) { if (! $this->server instanceof SwooleServer) { // [!code focus:39]
// 分析makeServer方法 // 分析makeServer方法
// 这里返回的是一个Swoole\Server对象 // 这里返回的是一个Swoole\Server对象
$this->server = $this->makeServer($type, $host, $port, $config->getMode(), $sockType); $this->server = $this->makeServer($type, $host, $port, $config->getMode(), $sockType);
@ -1112,45 +1127,239 @@ foreach ($servers as $server) {
} }
} else { } else {
/** @var bool|\Swoole\Server\Port $slaveServer */ /** @var bool|\Swoole\Server\Port $slaveServer */
// $slaveServer = $this->server->addlistener($host, $port, $sockType); $slaveServer = $this->server->addlistener($host, $port, $sockType);
// if (! $slaveServer) { if (! $slaveServer) {
// throw new \RuntimeException("Failed to listen server port [{$host}:{$port}]"); throw new \RuntimeException("Failed to listen server port [{$host}:{$port}]");
// } }
// $server->getSettings() && $slaveServer->set(array_replace($config->getSettings(), $server->getSettings())); $server->getSettings() && $slaveServer->set(array_replace($config->getSettings(), $server->getSettings()));
// $this->registerSwooleEvents($slaveServer, $callbacks, $name); $this->registerSwooleEvents($slaveServer, $callbacks, $name);
// ServerManager::add($name, [$type, $slaveServer]); ServerManager::add($name, [$type, $slaveServer]);
} }
// Trigger beforeStart event. // 如果设置了ON_BEFORE_START回调则运行该回调
if (isset($callbacks[Event::ON_BEFORE_START])) { if (isset($callbacks[Event::ON_BEFORE_START])) {
[$class, $method] = $callbacks[Event::ON_BEFORE_START]; [$class, $method] = $callbacks[Event::ON_BEFORE_START];
if ($this->container->has($class)) { if ($this->container->has($class)) {
$this->container->get($class)->{$method}(); $this->container->get($class)->{$method}();
} }
} }
if (class_exists(BeforeServerStart::class)) { if (class_exists(BeforeServerStart::class)) {
// Trigger BeforeServerStart event. // 触发BeforeServerStart事件
$this->eventDispatcher->dispatch(new BeforeServerStart($name)); $this->eventDispatcher->dispatch(new BeforeServerStart($name));
} }
} // [!code focus]
```
```php [Port类]
namespace Hyperf\Server;
class Port
{
protected string $name = 'http';
protected int $type = ServerInterface::SERVER_HTTP;
protected string $host = '0.0.0.0';
protected int $port = 9501;
protected int $sockType = 0;
protected array $callbacks = [];
protected array $settings = [];
protected ?Option $options = null;
public static function build(array $config): static
{
$config = self::filter($config);
$port = new static();
isset($config['name']) && $port->setName($config['name']);
isset($config['type']) && $port->setType($config['type']);
isset($config['host']) && $port->setHost($config['host']);
isset($config['port']) && $port->setPort($config['port']);
isset($config['sock_type']) && $port->setSockType($config['sock_type']);
isset($config['callbacks']) && $port->setCallbacks($config['callbacks']);
isset($config['settings']) && $port->setSettings($config['settings']);
isset($config['options']) && $port->setOptions(Option::make($config['options']));
return $port;
}
public function getName(): string
{
return $this->name;
}
public function setName(string $name): static
{
$this->name = $name;
return $this;
}
public function getType(): int
{
return $this->type;
}
public function setType(int $type): static
{
$this->type = $type;
return $this;
}
public function getHost(): string
{
return $this->host;
}
public function setHost(string $host): static
{
$this->host = $host;
return $this;
}
public function getPort(): int
{
return $this->port;
}
public function setPort(int $port): static
{
$this->port = $port;
return $this;
}
public function getSockType(): int
{
return $this->sockType;
}
public function setSockType(int $sockType): static
{
$this->sockType = $sockType;
return $this;
}
public function getCallbacks(): array
{
return $this->callbacks;
}
public function setCallbacks(array $callbacks): static
{
$this->callbacks = $callbacks;
return $this;
}
public function getSettings(): array
{
return $this->settings;
}
public function setSettings(array $settings): static
{
$this->settings = $settings;
return $this;
}
public function getOptions(): ?Option
{
return $this->options;
}
public function setOptions(Option $options): static
{
$this->options = $options;
return $this;
}
private static function filter(array $config): array
{
if ((int) $config['type'] === ServerInterface::SERVER_BASE) {
$default = [
'open_http2_protocol' => false,
'open_http_protocol' => false,
];
$config['settings'] = array_merge($default, $config['settings'] ?? []);
}
return $config;
}
} }
``` ```
###### makeServer方法 ```php [config/server配置文件]
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'],
],
'options' => [
// Whether to enable request lifecycle event
'enable_request_lifecycle' => false,
],
],
],
'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,
// 将 public 替换为上传目录
'document_root' => BASE_PATH . '/public',
'enable_static_handler' => true,
],
'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'],
],
];
```
分析可知,`makeServer`方法的作用就是根据`$type`参数,返回对应的`Server`类型。 :::
根据配置文件中的`type`参数可知,返回的是一个`Swoole\Server`对象实例。
```php ##### initServers
分析可知,`makeServer`方法的作用就是根据`$type`参数,默认返回对应的`Server`类型。
根据配置文件中的`type`参数可知,返回的是一个`Swoole\Http\Server`对象实例。
::: code-group
```php [makeServer]
$this->server = $this->makeServer($type, $host, $port, $config->getMode(), $sockType);
```
```php [具体实现]
use Swoole\Http\Server as SwooleHttpServer;
use Swoole\Server as SwooleServer; use Swoole\Server as SwooleServer;
use Swoole\WebSocket\Server as SwooleWebSocketServer;
protected function makeServer(int $type, string $host, int $port, int $mode, int $sockType): SwooleServer protected function makeServer(int $type, string $host, int $port, int $mode, int $sockType): SwooleServer
{ {
switch ($type) { switch ($type) {
// HTTP类型 (默认)
case ServerInterface::SERVER_HTTP: case ServerInterface::SERVER_HTTP:
return new SwooleHttpServer($host, $port, $mode, $sockType); return new SwooleHttpServer($host, $port, $mode, $sockType);
// WebSocket类型 (默认)
case ServerInterface::SERVER_WEBSOCKET: case ServerInterface::SERVER_WEBSOCKET:
return new SwooleWebSocketServer($host, $port, $mode, $sockType); return new SwooleWebSocketServer($host, $port, $mode, $sockType);
// 基础服务TCP类型
case ServerInterface::SERVER_BASE: case ServerInterface::SERVER_BASE:
return new SwooleServer($host, $port, $mode, $sockType); return new SwooleServer($host, $port, $mode, $sockType);
} }
@ -1159,146 +1368,267 @@ protected function makeServer(int $type, string $host, int $port, int $mode, int
} }
``` ```
###### registerSwooleEvents方法 :::
看这一行, 接下来将回调合并, 合并顺序: 默认回调 -> 配置回调 -> 传递的回调, `server`数组中的配置最大,可以覆盖`Server`、默认回调的配置。
::: code-group
```php
$callbacks = array_replace($this->defaultCallbacks(), $config->getCallbacks(), $callbacks);
```
```php [具体实现]
protected function defaultCallbacks()
{
$hasCallback = class_exists(Bootstrap\StartCallback::class)
&& class_exists(Bootstrap\ManagerStartCallback::class)
&& class_exists(Bootstrap\WorkerStartCallback::class);
if ($hasCallback) {
$callbacks = [
Event::ON_MANAGER_START => [Bootstrap\ManagerStartCallback::class, 'onManagerStart'],
Event::ON_WORKER_START => [Bootstrap\WorkerStartCallback::class, 'onWorkerStart'],
Event::ON_WORKER_STOP => [Bootstrap\WorkerStopCallback::class, 'onWorkerStop'],
Event::ON_WORKER_EXIT => [Bootstrap\WorkerExitCallback::class, 'onWorkerExit'],
];
if ($this->server->mode === SWOOLE_BASE) {
return $callbacks;
}
return array_merge([
Event::ON_START => [Bootstrap\StartCallback::class, 'onStart'],
], $callbacks);
}
return [
Event::ON_WORKER_START => function (SwooleServer $server, int $workerId) {
printf('Worker %d started.' . PHP_EOL, $workerId);
},
];
}
```
:::
根据服务名注册回调,
::: details 默认回调列表
```php
Array
(
[start] => Array
(
[0] => Hyperf\Framework\Bootstrap\StartCallback
[1] => onStart
)
[managerStart] => Array
(
[0] => Hyperf\Framework\Bootstrap\ManagerStartCallback
[1] => onManagerStart
)
[workerStart] => Array
(
[0] => Hyperf\Framework\Bootstrap\WorkerStartCallback
[1] => onWorkerStart
)
[workerStop] => Array
(
[0] => Hyperf\Framework\Bootstrap\WorkerStopCallback
[1] => onWorkerStop
)
[workerExit] => Array
(
[0] => Hyperf\Framework\Bootstrap\WorkerExitCallback
[1] => onWorkerExit
)
[pipeMessage] => Array
(
[0] => Hyperf\Framework\Bootstrap\PipeMessageCallback
[1] => onPipeMessage
)
[request] => Array
(
[0] => Hyperf\HttpServer\Server
[1] => onRequest
)
)
```
:::
::: code-group
```php ```php
$this->registerSwooleEvents($this->server, $callbacks, $name); $this->registerSwooleEvents($this->server, $callbacks, $name);
```
```php [具体实现]
protected function registerSwooleEvents(SwoolePort|SwooleServer $server, array $events, string $serverName): void protected function registerSwooleEvents(SwoolePort|SwooleServer $server, array $events, string $serverName): void
{ {
foreach ($events as $event => $callback) { foreach ($events as $event => $callback) {
// beforeStart不是swoole回调事件会跳过
if (! Event::isSwooleEvent($event)) { if (! Event::isSwooleEvent($event)) {
continue; continue;
} }
if (is_array($callback)) { if (is_array($callback)) {
// 解构出类名和方法名 // 获取事件对应class和回调方法
[$className, $method] = $callback; [$className, $method] = $callback;
// 如果类名+方法名已经存在,追加到控制台输出警告信息
if (array_key_exists($className . $method, $this->onRequestCallbacks)) { if (array_key_exists($className . $method, $this->onRequestCallbacks)) {
$this->logger->warning(sprintf('%s will be replaced by %s. Each server should have its own onRequest callback. Please check your configs.', $this->onRequestCallbacks[$className . $method], $serverName)); $this->logger->warning(sprintf('%s will be replaced by %s. Each server should have its own onRequest callback. Please check your configs.', $this->onRequestCallbacks[$className . $method], $serverName));
} }
// 追加到onRequestCallbacks数组中 // 记录回调
$this->onRequestCallbacks[$className . $method] = $serverName; $this->onRequestCallbacks[$className . $method] = $serverName;
// 从容器中解析出回调事件对象实例 // 解析回调类
$class = $this->container->get($className); $class = $this->container->get($className);
// 设置服务名
if (method_exists($class, 'setServerName')) { if (method_exists($class, 'setServerName')) {
// Override the server name. // Override the server name.
$class->setServerName($serverName); $class->setServerName($serverName);
} }
// 最后一个回调事件中Hyperf\HttpServer\Server是MiddlewareInitializerInterface的实现类 // 如果类实现MiddlewareInitializerInterface接口则调用初始化中间件方法
// 所以会进入该if
// 这里会初始化中间件
if ($class instanceof MiddlewareInitializerInterface) { if ($class instanceof MiddlewareInitializerInterface) {
$class->initCoreMiddleware($serverName); $class->initCoreMiddleware($serverName);
} }
$callback = [$class, $method]; $callback = [$class, $method];
} }
// 在server上注册回调事件 // 注册事件回调函数
$server->on($event, $callback); $server->on($event, $callback);
} }
}
```
这是所有的回调事件,如下图
![](https://oss.xiaokeaii.top/2024/event.png)
查看以下代码,这里进行配置和初始化中间件相关的内容
```php
$class->initCoreMiddleware($serverName);
```
> 文件位置:/vendor/hyperf/http-server/src/Server.php
```php
public function initCoreMiddleware(string $serverName): void
{
// http
$this->serverName = $serverName;
// 创建核心中间件对象实例
$this->coreMiddleware = $this->createCoreMiddleware();
$config = $this->container->get(ConfigInterface::class);
// 获取配置的中间件
$this->middlewares = $config->get('middlewares.' . $serverName, []);
// 设置异常处理器
$this->exceptionHandlers = $config->get('exceptions.handler.' . $serverName, $this->getDefaultExceptionHandler());
// 设置服务的option选项对中间件进行排序
$this->initOption();
} }
``` ```
逐行分析, :::
::: tip
当`http`请求过来时,会触发`request`事件,从注册的事件列表中可知,会调用`Hyperf\HttpServer\Server`的`onRequest`方法。
:::
:::warning 参考
初始化中间件操作,请参考[附录5](./附录5%20初始化中间件){target="_blank"}
:::
接下来设置运行时的一些参数,列表如下,
::: details 运行参数
```php ```php
protected function createCoreMiddleware(): CoreMiddlewareInterface Array
{ (
return make(CoreMiddleware::class, [$this->container, $this->serverName]); [enable_coroutine] => 1
} [worker_num] => 4
// make方法从容器中获取对象实例 [pid_file] => /opt/www/runtime/hyperf.pid
function make(string $name, array $parameters = []) [open_tcp_nodelay] => 1
{ [max_coroutine] => 100000
if (ApplicationContext::hasContainer()) { [open_http2_protocol] => 1
/** @var \Hyperf\Di\Container $container */ [max_request] => 100000
$container = ApplicationContext::getContainer(); [socket_buffer_size] => 2097152
if (method_exists($container, 'make')) { [buffer_output_size] => 2097152
return $container->make($name, $parameters); [document_root] => /opt/www/public
} [enable_static_handler] => 1
} )
$parameters = array_values($parameters);
return new $name(...$parameters);
}
protected function initOption(): void
{
$ports = $this->container->get(ServerFactory::class)->getConfig()?->getServers();
if (! $ports) {
return;
}
foreach ($ports as $port) {
if ($port->getName() === $this->serverName) {
$this->option = $port->getOptions();
}
}
$this->option ??= Option::make([]);
$this->option->setMustSortMiddlewaresByMiddlewares($this->middlewares);
}
``` ```
###### ServerManager::add方法 :::
同样的,合并`setting``server`配置会替换 `settings`下的某些配置。
上面分析完了,继续看下面的代码。 `setting`的具体配置可参考[swoole官方文档](https://wiki.swoole.com/#/server/setting)
```php ```php
// 设置sever运行时的参数
$this->server->set(array_replace($config->getSettings(), $server->getSettings())); $this->server->set(array_replace($config->getSettings(), $server->getSettings()));
// 添加到ServerManager中
ServerManager::add($name, [$type, current($this->server->ports)]);
``` ```
最后,添加服务信息到`ServerManager`,触发`BeforeMainServerStart`事件
```php
ServerManager::add($name, [$type, current($this->server->ports)]);
if (class_exists(BeforeMainServerStart::class)) {
// Trigger BeforeMainServerStart event, this event only trigger once before main server start.
$this->eventDispatcher->dispatch(new BeforeMainServerStart($this->server, $config->toArray()));
}
```
当存在多个`server`时,后续`server`启动,会通过`else`启动,同样的,设置运行参数,注册事件回调,添加信息到`ServerManager`
```php
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);
$this->server->set(array_replace($config->getSettings(), $server->getSettings()));
ServerManager::add($name, [$type, current($this->server->ports)]);
if (class_exists(BeforeMainServerStart::class)) {
// Trigger BeforeMainServerStart event, this event only trigger once before main server start.
$this->eventDispatcher->dispatch(new BeforeMainServerStart($this->server, $config->toArray()));
}
} else { // [!code focus:13]
/** @var bool|SwoolePort $slaveServer */
// 添加监听端口
$slaveServer = $this->server->addlistener($host, $port, $sockType);
if (! $slaveServer) {
throw new \RuntimeException("Failed to listen server port [{$host}:{$port}]");
}
// 获取服务配置
$server->getSettings() && $slaveServer->set(array_replace($config->getSettings(), $server->getSettings()));
// 注册回调事件
$this->registerSwooleEvents($slaveServer, $callbacks, $name);
ServerManager::add($name, [$type, $slaveServer]);
}
```
`initServers`方法执行完成,返回自身
```php
public function init(ServerConfig $config): ServerInterface
{
$this->initServers($config);
return $this; // [!code focus]
}
```
###### startServer
至此,`Hyperf\Server\Server`的`init`方法走完,返回自身实例。 至此,`Hyperf\Server\Server`的`init`方法走完,返回自身实例。
`Hyperf\Server\ServerFactory`工厂类`configure`方法完成。 `Hyperf\Server\ServerFactory`工厂类`configure`方法完成。
回到`StartServer`类的`execute`方法。 回到`StartServer`类的`execute`方法。
该方法的`$serverFactory->configure($serverConfig);`这一行执行完成。
```php ```php
//
Coroutine::set(['hook_flags' => swoole_hook_flags()]);
```
##### start方法 protected function execute(InputInterface $input, OutputInterface $output): int
{
$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(); // [!code focus]
return 0;
}
```
::: tip
该方法的`$serverFactory->configure($serverConfig);`这一行执行完成,返回`Hyperf\Server\Server`对象,然后调用`start`方法,启动服务。
:::
#### start方法
终于可以启动服务了, 终于可以启动服务了,
> 文件位置:/vendor/hyperf/server/src/ServerFactory.php > 文件位置:/vendor/hyperf/server/src/ServerFactory.php
```php ```php
$serverFactory->start();
public function start(): void public function start(): void
{ {
$this->getServer()->start(); $this->getServer()->start();
@ -1315,5 +1645,7 @@ public function start(): void
} }
``` ```
::: tip
其中`$this->server`对应的是`Swoole\Server`实例。调用`start`方法,实际就是启动`Swoole`的服务。 其中`$this->server`对应的是`Swoole\Server`实例。调用`start`方法,实际就是启动`Swoole`的服务。
此时`server`启动,等待请求。 此时`server`启动,等待请求。
:::

View File

@ -1,9 +1,12 @@
--- ---
title: 请求 title: 接收请求
--- ---
# 接收请求
由前一节可知,事件的注册在`Hyerf\Server\Server`类的`registerSwooleEvents`方法中完成。 由前一节可知,事件的注册在`Hyerf\Server\Server`类的`registerSwooleEvents`方法中完成。
![](https://oss.xiaokeaii.top/2024/event.png) ![](https://oss.xiaokeaii.top/2024/event.png)
注册的回调事件的执行顺序如下: 注册的回调事件的执行顺序如下:
| 所属类 | 事件 | 描述 | 触发时机 | 触发顺序 | | 所属类 | 事件 | 描述 | 触发时机 | 触发顺序 |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
@ -14,14 +17,18 @@ title: 请求
| WorkerExitCallback | onWorkerExit | 当 worker 进程退出后触发 | 仅当 worker 进程退出后触发 | 5 | | WorkerExitCallback | onWorkerExit | 当 worker 进程退出后触发 | 仅当 worker 进程退出后触发 | 5 |
| Server | onRequest | 当接收到请求时触发 | 仅当 worker 进程接收到请求后触发 | 6 | | Server | onRequest | 当接收到请求时触发 | 仅当 worker 进程接收到请求后触发 | 6 |
| PipeMessageCallback | onPipeMessage | 当接收到消息时触发 | 仅当 worker 进程接收到消息后触发 | 7 | | PipeMessageCallback | onPipeMessage | 当接收到消息时触发 | 仅当 worker 进程接收到消息后触发 | 7 |
> 要理解上面事件,请参考`Swoole`的运行流程。 ::: tip
要理解上面事件,请参考`Swoole`的运行流程。
:::
### Swoole流程 ### Swoole流程
首先了解`Swoole`的运行流程,(图源网络) 首先了解`Swoole`的运行流程,(图源网络)
![](https://oss.xiaokeaii.top/2024/swoole流程.jpg) ![](https://oss.xiaokeaii.top/2024/swoole流程.jpg)
具体内容参考[swoole官网](https://wiki.swoole.com/) 具体内容参考[swoole官网](https://wiki.swoole.com/)
当请求过来时,会触发`onRequest`事件,然后执行`onRequest`回调函数。 ::: tip
当请求过来时,会触发`Request`事件,基于之前准备阶段注册的`swoole`事件可知,会调用`Hyperf\HttpServer\Server`类的`onRequest`函数。
:::
### OnRequest方法 ### OnRequest方法
```php ```php

View File

@ -0,0 +1,607 @@
---
title: 附录5 初始化中间件
---
# 初始化中间件
在注册服务,注册服务的事件时,有一个初始化中间件的操作。
```php
protected function registerSwooleEvents(SwoolePort|SwooleServer $server, array $events, string $serverName): void
{
foreach ($events as $event => $callback) {
if (! Event::isSwooleEvent($event)) {
continue;
}
if (is_array($callback)) {
// 获取事件对应class和回调方法
[$className, $method] = $callback;
if (array_key_exists($className . $method, $this->onRequestCallbacks)) {
$this->logger->warning(sprintf('%s will be replaced by %s. Each server should have its own onRequest callback. Please check your configs.', $this->onRequestCallbacks[$className . $method], $serverName));
}
// 记录回调
$this->onRequestCallbacks[$className . $method] = $serverName;
// 解析回调类
$class = $this->container->get($className);
// 设置服务名
if (method_exists($class, 'setServerName')) {
// Override the server name.
$class->setServerName($serverName);
}
// 如果类实现MiddlewareInitializerInterface接口则调用初始化中间件方法 // [!code focus:4]
if ($class instanceof MiddlewareInitializerInterface) {
$class->initCoreMiddleware($serverName);
}
$callback = [$class, $method];
}
// 注册事件回调函数
$server->on($event, $callback);
}
}
```
其中,默认注册的回调事件有以下几种,我们看`request`回调,其中回调的类是`Hyperf\HttpServer\Server`类,
::: details 回调事件
```php
Array
(
[start] => Array
(
[0] => Hyperf\Framework\Bootstrap\StartCallback
[1] => onStart
)
[managerStart] => Array
(
[0] => Hyperf\Framework\Bootstrap\ManagerStartCallback
[1] => onManagerStart
)
[workerStart] => Array
(
[0] => Hyperf\Framework\Bootstrap\WorkerStartCallback
[1] => onWorkerStart
)
[workerStop] => Array
(
[0] => Hyperf\Framework\Bootstrap\WorkerStopCallback
[1] => onWorkerStop
)
[workerExit] => Array
(
[0] => Hyperf\Framework\Bootstrap\WorkerExitCallback
[1] => onWorkerExit
)
[pipeMessage] => Array
(
[0] => Hyperf\Framework\Bootstrap\PipeMessageCallback
[1] => onPipeMessage
)
[request] => Array
(
[0] => Hyperf\HttpServer\Server
[1] => onRequest
)
)
```
:::
查看`Hyperf\HttpServer\Server`类,
::: details `Hyperf\HttpServer\Server`
```php
namespace Hyperf\HttpServer;
use FastRoute\Dispatcher;
use Hyperf\Context\RequestContext;
use Hyperf\Context\ResponseContext;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Contract\MiddlewareInitializerInterface;
use Hyperf\Contract\OnRequestInterface;
use Hyperf\Coordinator\Constants;
use Hyperf\Coordinator\CoordinatorManager;
use Hyperf\Dispatcher\HttpDispatcher;
use Hyperf\Engine\Http\WritableConnection;
use Hyperf\ExceptionHandler\ExceptionHandlerDispatcher;
use Hyperf\HttpMessage\Server\Request as Psr7Request;
use Hyperf\HttpMessage\Server\Response as Psr7Response;
use Hyperf\HttpServer\Contract\CoreMiddlewareInterface;
use Hyperf\HttpServer\Event\RequestHandled;
use Hyperf\HttpServer\Event\RequestReceived;
use Hyperf\HttpServer\Event\RequestTerminated;
use Hyperf\HttpServer\Exception\Handler\HttpExceptionHandler;
use Hyperf\HttpServer\Router\Dispatched;
use Hyperf\HttpServer\Router\DispatcherFactory;
use Hyperf\Server\Option;
use Hyperf\Server\ServerFactory;
use Hyperf\Support\SafeCaller;
use Psr\Container\ContainerInterface;
use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Throwable;
use function Hyperf\Coroutine\defer;
use function Hyperf\Support\make;
//该类实现了MiddlewareInitializerInterface接口 // [!code focus:2]
class Server implements OnRequestInterface, MiddlewareInitializerInterface
{
protected array $middlewares = [];
protected ?CoreMiddlewareInterface $coreMiddleware = null;
protected array $exceptionHandlers = [];
protected ?string $serverName = null;
protected ?EventDispatcherInterface $event = null;
protected ?Option $option = null;
public function __construct(
protected ContainerInterface $container,
protected HttpDispatcher $dispatcher,
protected ExceptionHandlerDispatcher $exceptionHandlerDispatcher,
protected ResponseEmitter $responseEmitter
) {
if ($this->container->has(EventDispatcherInterface::class)) {
$this->event = $this->container->get(EventDispatcherInterface::class);
}
}
//....
}
```
:::
所以该类会调用`initCoreMiddleware`方法,进行中间件的初始化。
```php
$class->initCoreMiddleware($serverName);
```
> 文件位置:/vendor/hyperf/http-server/src/Server.php
```php
public function initCoreMiddleware(string $serverName): void
{
// http
$this->serverName = $serverName;
// 创建核心中间件对象实例
$this->coreMiddleware = $this->createCoreMiddleware();
$config = $this->container->get(ConfigInterface::class);
// 获取配置的中间件
$this->middlewares = $config->get('middlewares.' . $serverName, []);
// 设置异常处理器
$this->exceptionHandlers = $config->get('exceptions.handler.' . $serverName, $this->getDefaultExceptionHandler());
// 设置服务的option选项对中间件进行排序
$this->initOption();
}
```
## 创建中间件对象实例
首先创建核心中间件对象实例,通过`make`方法创建。
```php
protected function createCoreMiddleware(): CoreMiddlewareInterface // [!code focus:4]
{
return make(CoreMiddleware::class, [$this->container, $this->serverName]);
}
function make(string $name, array $parameters = [])
{
if (ApplicationContext::hasContainer()) {
/** @var \Hyperf\Di\Container $container */
$container = ApplicationContext::getContainer();
if (method_exists($container, 'make')) {
return $container->make($name, $parameters);
}
}
$parameters = array_values($parameters);
return new $name(...$parameters);
}
protected function initOption(): void
{
$ports = $this->container->get(ServerFactory::class)->getConfig()?->getServers();
if (! $ports) {
return;
}
foreach ($ports as $port) {
if ($port->getName() === $this->serverName) {
$this->option = $port->getOptions();
}
}
$this->option ??= Option::make([]);
$this->option->setMustSortMiddlewaresByMiddlewares($this->middlewares);
}
```
实际上`make`方法还是从依赖注入容器中使用`make`方法创建对象。
### CoreMiddleware实例化过程
::: code-group
```php [构造函数]
public function __construct(protected ContainerInterface $container, private string $serverName)
{
// 创建调度器
$this->dispatcher = $this->createDispatcher($serverName);
// 获取标准化接口实例
$this->normalizer = $this->container->get(NormalizerInterface::class);
// 获取方法定义收集器实例
$this->methodDefinitionCollector = $this->container->get(MethodDefinitionCollectorInterface::class);
// 如果存在,获取闭包定义收集器实例
if ($this->container->has(ClosureDefinitionCollectorInterface::class)) {
$this->closureDefinitionCollector = $this->container->get(ClosureDefinitionCollectorInterface::class);
}
}
protected function createDispatcher(string $serverName): Dispatcher
{
// 从容器中获取DispatcherFactory实例
$factory = $this->container->get(DispatcherFactory::class);
// 获取调度器实例
return $factory->getDispatcher($serverName);
}
```
```php [CoreMiddleware类源码]
namespace Hyperf\HttpServer;
use Closure;
use FastRoute\Dispatcher;
use Hyperf\Codec\Json;
use Hyperf\Context\Context;
use Hyperf\Context\RequestContext;
use Hyperf\Context\ResponseContext;
use Hyperf\Contract\Arrayable;
use Hyperf\Contract\Jsonable;
use Hyperf\Contract\NormalizerInterface;
use Hyperf\Di\ClosureDefinitionCollectorInterface;
use Hyperf\Di\MethodDefinitionCollectorInterface;
use Hyperf\Di\ReflectionType;
use Hyperf\HttpMessage\Exception\MethodNotAllowedHttpException;
use Hyperf\HttpMessage\Exception\NotFoundHttpException;
use Hyperf\HttpMessage\Exception\ServerErrorHttpException;
use Hyperf\HttpMessage\Server\ResponsePlusProxy;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Hyperf\HttpServer\Contract\CoreMiddlewareInterface;
use Hyperf\HttpServer\Router\Dispatched;
use Hyperf\HttpServer\Router\DispatcherFactory;
use Hyperf\Server\Exception\ServerException;
use InvalidArgumentException;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use RuntimeException;
use Swow\Psr7\Message\ResponsePlusInterface;
class CoreMiddleware implements CoreMiddlewareInterface
{
protected Dispatcher $dispatcher;
private MethodDefinitionCollectorInterface $methodDefinitionCollector;
private ?ClosureDefinitionCollectorInterface $closureDefinitionCollector = null;
private NormalizerInterface $normalizer;
public function __construct(protected ContainerInterface $container, private string $serverName)
{
$this->dispatcher = $this->createDispatcher($serverName);
$this->normalizer = $this->container->get(NormalizerInterface::class);
$this->methodDefinitionCollector = $this->container->get(MethodDefinitionCollectorInterface::class);
if ($this->container->has(ClosureDefinitionCollectorInterface::class)) {
$this->closureDefinitionCollector = $this->container->get(ClosureDefinitionCollectorInterface::class);
}
}
public function dispatch(ServerRequestInterface $request): ServerRequestInterface
{
$routes = $this->dispatcher->dispatch($request->getMethod(), $request->getUri()->getPath());
$dispatched = new Dispatched($routes, $this->serverName);
return RequestContext::set($request)->setAttribute(Dispatched::class, $dispatched);
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$request = RequestContext::set($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 = match ($dispatched->status) {
Dispatcher::NOT_FOUND => $this->handleNotFound($request),
Dispatcher::METHOD_NOT_ALLOWED => $this->handleMethodNotAllowed($dispatched->params, $request),
Dispatcher::FOUND => $this->handleFound($dispatched, $request),
default => null,
};
if (! $response instanceof ResponsePlusInterface) {
$response = $this->transferToResponse($response, $request);
}
return $response->addHeader('Server', 'Hyperf');
}
public function getMethodDefinitionCollector(): MethodDefinitionCollectorInterface
{
return $this->methodDefinitionCollector;
}
public function getClosureDefinitionCollector(): ClosureDefinitionCollectorInterface
{
return $this->closureDefinitionCollector;
}
public function getNormalizer(): NormalizerInterface
{
return $this->normalizer;
}
protected function createDispatcher(string $serverName): Dispatcher
{
$factory = $this->container->get(DispatcherFactory::class);
return $factory->getDispatcher($serverName);
}
/**
* Handle the response when found.
*
* @return array|Arrayable|mixed|ResponseInterface|string
*/
protected function handleFound(Dispatched $dispatched, ServerRequestInterface $request): mixed
{
if ($dispatched->handler->callback instanceof Closure) {
$parameters = $this->parseClosureParameters($dispatched->handler->callback, $dispatched->params);
$callback = $dispatched->handler->callback;
$response = $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;
}
/**
* Handle the response when cannot found any routes.
*/
protected function handleNotFound(ServerRequestInterface $request): mixed
{
throw new NotFoundHttpException();
}
/**
* Handle the response when the routes found but doesn't match any available methods.
*/
protected function handleMethodNotAllowed(array $methods, ServerRequestInterface $request): mixed
{
throw new MethodNotAllowedHttpException('Allow: ' . implode(', ', $methods));
}
protected function prepareHandler(array|string $handler): array
{
if (is_string($handler)) {
if (str_contains($handler, '@')) {
return explode('@', $handler);
}
if (str_contains($handler, '::')) {
return explode('::', $handler);
}
return [$handler, '__invoke'];
}
if (is_array($handler) && isset($handler[0], $handler[1])) {
return $handler;
}
throw new RuntimeException('Handler not exist.');
}
/**
* Transfer the non-standard response content to a standard response object.
*
* @param null|array|Arrayable|Jsonable|ResponseInterface|string $response
*/
protected function transferToResponse($response, ServerRequestInterface $request): ResponsePlusInterface
{
if (is_string($response)) {
return $this->response()->addHeader('content-type', 'text/plain')->setBody(new SwooleStream($response));
}
if ($response instanceof ResponseInterface) {
return new ResponsePlusProxy($response);
}
if (is_array($response) || $response instanceof Arrayable) {
return $this->response()
->addHeader('content-type', 'application/json')
->setBody(new SwooleStream(Json::encode($response)));
}
if ($response instanceof Jsonable) {
return $this->response()
->addHeader('content-type', 'application/json')
->setBody(new SwooleStream((string) $response));
}
if ($this->response()->hasHeader('content-type')) {
return $this->response()->setBody(new SwooleStream((string) $response));
}
return $this->response()->addHeader('content-type', 'text/plain')->setBody(new SwooleStream((string) $response));
}
/**
* Get response instance from context.
*/
protected function response(): ResponsePlusInterface
{
return ResponseContext::get();
}
/**
* Parse the parameters of method definitions, and then bind the specified arguments or
* get the value from DI container, combine to an argument array that should be injected
* and return the array.
*/
protected function parseMethodParameters(string $controller, string $action, array $arguments): array
{
$definitions = $this->getMethodDefinitionCollector()->getParameters($controller, $action);
return $this->getInjections($definitions, "{$controller}::{$action}", $arguments);
}
/**
* Parse the parameters of closure definitions, and then bind the specified arguments or
* get the value from DI container, combine to an argument array that should be injected
* and return the array.
*/
protected function parseClosureParameters(Closure $closure, array $arguments): array
{
if (! $this->container->has(ClosureDefinitionCollectorInterface::class)) {
return [];
}
$definitions = $this->getClosureDefinitionCollector()->getParameters($closure);
return $this->getInjections($definitions, 'Closure', $arguments);
}
/**
* @param ReflectionType[] $definitions
*/
private function getInjections(array $definitions, string $callableName, array $arguments): array
{
$injections = [];
foreach ($definitions as $pos => $definition) {
$value = $arguments[$pos] ?? $arguments[$definition->getMeta('name')] ?? null;
if ($value === null) {
if ($definition->getMeta('defaultValueAvailable')) {
$injections[] = $definition->getMeta('defaultValue');
} elseif ($this->container->has($definition->getName())) {
$injections[] = $this->container->get($definition->getName());
} elseif ($definition->allowsNull()) {
$injections[] = null;
} else {
throw new InvalidArgumentException("Parameter '{$definition->getMeta('name')}' "
. "of {$callableName} should not be null");
}
} else {
$injections[] = $this->getNormalizer()->denormalize($value, $definition->getName());
}
}
return $injections;
}
}
```
:::
`DispatcherFactory`类在实例化时,调用构造方法,进行初始化路由和调度器
```php
public function __construct()
{
// 根据注解初始化路由
$this->initAnnotationRoute(AnnotationCollector::list());
// 根据配置文件初始化路由
$this->initConfigRoute();
}
public function getDispatcher(string $serverName): Dispatcher
{
if (isset($this->dispatchers[$serverName])) {
return $this->dispatchers[$serverName];
}
$router = $this->getRouter($serverName);
return $this->dispatchers[$serverName] = new GroupCountBased($router->getData());
}
```
CoreMiddleware实例化完成。
```php
public function initCoreMiddleware(string $serverName): void
{
$this->serverName = $serverName;
// 这里保存了所有的路由信息
$this->coreMiddleware = $this->createCoreMiddleware();
// 获取配置文件
$config = $this->container->get(ConfigInterface::class); // [!code focus:5]
// 设置配置的中间件数组
$this->middlewares = $config->get('middlewares.' . $serverName, []);
// 设置异常处理器
$this->exceptionHandlers = $config->get('exceptions.handler.' . $serverName, $this->getDefaultExceptionHandler());
$this->initOption();
}
```
### 设置异常处理器
配置服务的异常处理程序,首先从配置文件`exceptions`中,获取配置的异常处理器,如不存在,则使用默认的异常处理器。
```php
$this->exceptionHandlers = $config->get('exceptions.handler.' . $serverName, $this->getDefaultExceptionHandler());
protected function getDefaultExceptionHandler(): array
{
return [
HttpExceptionHandler::class,
];
}
```
### 初始化配置
设置`Server`的`option`参数
```php
protected function initOption(): void
{
// 获取服务器配置对象数组类型为port[]
$ports = $this->container->get(ServerFactory::class)->getConfig()?->getServers();
if (! $ports) {
return;
}
// 设置服务的option配置
foreach ($ports as $port) {
if ($port->getName() === $this->serverName) {
$this->option = $port->getOptions();
}
}
$this->option ??= Option::make([]);
// 对中间件进行排序
$this->option->setMustSortMiddlewaresByMiddlewares($this->middlewares);
}
// 如果设置了顺序比如[foo::class => 1]或者继承了PriorityMiddleware中间件都将启动中间件的排序操作
public function setMustSortMiddlewaresByMiddlewares(array $middlewares): static
{
foreach ($middlewares as $middleware) {
if (is_int($middleware) || $middleware instanceof PriorityMiddleware) {
return $this->setMustSortMiddlewares(true);
}
}
return $this;
}
```
最后,初始化中间件完成,等待`http`请求。