Files
kiri-http-server/ServerManager.php
T

417 lines
11 KiB
PHP
Raw Normal View History

2021-11-03 15:17:52 +08:00
<?php
namespace Server;
use Exception;
2021-12-03 15:42:04 +08:00
use Kiri\Abstracts\Component;
2021-11-03 15:17:52 +08:00
use Kiri\Abstracts\Config;
2021-11-17 16:39:12 +08:00
use Kiri\Error\Logger;
2021-12-02 14:16:57 +08:00
use Kiri\Events\EventDispatch;
2021-11-03 15:17:52 +08:00
use Kiri\Exception\ConfigException;
use Kiri\Kiri;
2021-12-02 14:16:57 +08:00
use Note\Inject;
2021-11-27 17:43:28 +08:00
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
2021-11-03 15:17:52 +08:00
use ReflectionException;
2021-11-03 17:39:08 +08:00
use Server\Abstracts\BaseProcess;
2021-11-18 11:37:12 +08:00
use Server\Contract\OnCloseInterface;
use Server\Contract\OnConnectInterface;
use Server\Contract\OnDisconnectInterface;
use Server\Contract\OnHandshakeInterface;
use Server\Contract\OnMessageInterface;
use Server\Contract\OnPacketInterface;
use Server\Contract\OnProcessInterface;
use Server\Contract\OnReceiveInterface;
2021-12-02 14:16:57 +08:00
use Server\Events\OnServerBeforeStart;
2021-11-18 15:37:10 +08:00
use Server\Handler\OnPipeMessage;
use Server\Handler\OnServer;
use Server\Handler\OnServerManager;
use Server\Handler\OnServerReload;
use Server\Handler\OnServerWorker;
2021-12-02 14:16:57 +08:00
use Server\Tasker\OnServerTask;
2021-11-03 15:17:52 +08:00
use Swoole\Http\Server as HServer;
use Swoole\Process;
use Swoole\Server;
use Swoole\Server\Port;
use Swoole\WebSocket\Server as WServer;
/**
* Class OnServerManager
* @package Http\Service
*/
2021-12-03 15:42:04 +08:00
class ServerManager extends Component
2021-11-03 15:17:52 +08:00
{
2021-11-17 16:39:12 +08:00
use TraitServer;
2021-11-03 15:17:52 +08:00
/** @var string */
public string $host = '';
public int $port = 0;
2021-12-01 16:09:49 +08:00
/**
* @var Logger
*/
2021-11-17 16:39:12 +08:00
#[Inject(Logger::class)]
public Logger $logger;
2021-11-03 15:17:52 +08:00
/** @var array<string,Port> */
public array $ports = [];
public int $mode = SWOOLE_TCP;
private Server|null $server = null;
2021-12-06 17:58:11 +08:00
protected array $initProcesses = [];
2021-11-03 15:17:52 +08:00
const DEFAULT_EVENT = [
Constant::WORKER_START => [OnServerWorker::class, 'onWorkerStart'],
Constant::WORKER_EXIT => [OnServerWorker::class, 'onWorkerExit'],
Constant::WORKER_STOP => [OnServerWorker::class, 'onWorkerStop'],
Constant::WORKER_ERROR => [OnServerWorker::class, 'onWorkerError'],
Constant::MANAGER_START => [OnServerManager::class, 'onManagerStart'],
Constant::MANAGER_STOP => [OnServerManager::class, 'onManagerStop'],
Constant::BEFORE_RELOAD => [OnServerReload::class, 'onBeforeReload'],
Constant::AFTER_RELOAD => [OnServerReload::class, 'onAfterReload'],
Constant::START => [OnServer::class, 'onStart'],
Constant::BEFORE_SHUTDOWN => [OnServer::class, 'onBeforeShutdown'],
Constant::SHUTDOWN => [OnServer::class, 'onShutdown'],
];
private array $eventInterface = [
OnReceiveInterface::class => 'receive',
OnPacketInterface::class => 'packet',
OnHandshakeInterface::class => 'handshake',
OnMessageInterface::class => 'message',
OnConnectInterface::class => 'connect',
OnCloseInterface::class => 'close',
OnDisconnectInterface::class => 'disconnect'
];
/**
* @return Server|WServer|HServer|null
2021-12-02 18:24:23 +08:00
* @throws ReflectionException
2021-11-03 15:17:52 +08:00
*/
public function getServer(): Server|WServer|HServer|null
{
2021-12-02 18:24:23 +08:00
di(EventDispatch::class)->dispatch(new OnServerBeforeStart());
2021-11-03 15:17:52 +08:00
return $this->server;
}
/**
* @param string $type
* @param string $host
* @param int $port
* @param int $mode
* @param array $settings
* @throws ConfigException
2021-12-01 15:16:08 +08:00
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
2021-11-03 15:17:52 +08:00
*/
public function addListener(string $type, string $host, int $port, int $mode, array $settings = [])
{
if (!$this->server) {
$this->createBaseServer($type, $host, $port, $mode, $settings);
} else {
if (!isset($settings['settings'])) {
$settings['settings'] = [];
}
$this->addNewListener($type, $host, $port, $mode, $settings);
}
}
/**
2021-12-01 15:16:08 +08:00
* @param $configs
* @param int $daemon
2021-11-03 15:17:52 +08:00
* @throws ConfigException
2021-12-01 15:16:08 +08:00
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
2021-11-03 15:17:52 +08:00
*/
public function initBaseServer($configs, int $daemon = 0): void
{
$context = di(ServerManager::class);
foreach ($this->sortService($configs['ports']) as $config) {
$this->startListenerHandler($context, $config, $daemon);
}
2021-11-17 16:39:12 +08:00
$this->bindCallback([Constant::PIPE_MESSAGE => [OnPipeMessage::class, 'onPipeMessage']]);
2021-11-03 15:17:52 +08:00
}
/**
2021-11-03 17:39:08 +08:00
* @param string|OnProcessInterface|BaseProcess $customProcess
2021-11-03 15:17:52 +08:00
* @throws Exception
*/
2021-11-03 17:39:08 +08:00
public function addProcess(string|OnProcessInterface|BaseProcess $customProcess)
2021-11-03 15:17:52 +08:00
{
2021-11-03 17:39:08 +08:00
if (is_string($customProcess)) {
$customProcess = Kiri::getDi()->get($customProcess);
2021-11-03 15:17:52 +08:00
}
2021-11-18 15:37:10 +08:00
$system = sprintf('[%s].process', Config::get('id', 'system-service'));
2021-12-23 18:25:31 +08:00
$this->logger->debug($system . ' ' . $customProcess->getName() . ' start.');
$this->server->addProcess(new Process(function (Process $process) use ($customProcess, $system) {
2021-11-18 18:06:45 +08:00
if (Kiri::getPlatform()->isLinux()) {
$process->name($system . '(' . $customProcess->getName() . ')');
}
2021-12-06 17:58:11 +08:00
$customProcess->onSigterm()->process($process);
2021-12-23 18:25:31 +08:00
},
$customProcess->getRedirectStdinAndStdout(),
$customProcess->getPipeType(),
$customProcess->isEnableCoroutine()
));
2021-11-03 15:17:52 +08:00
}
2021-12-06 17:58:11 +08:00
/**
* @return array<string,Process>
*/
public function getProcesses(): array
{
return $this->initProcesses;
}
2021-11-03 15:17:52 +08:00
/**
* @return array
*/
public function getSetting(): array
{
return $this->server->setting;
}
/**
* @param ServerManager $context
* @param array $config
* @param int $daemon
* @throws ConfigException
2021-12-01 15:16:08 +08:00
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
2021-11-03 15:17:52 +08:00
* @throws Exception
*/
private function startListenerHandler(ServerManager $context, array $config, int $daemon = 0)
{
if (!$this->server) {
$config = $this->mergeConfig($config, $daemon);
}
$context->addListener(
$config['type'], $config['host'], $config['port'], $config['mode'],
$config);
}
/**
* @param $config
* @param $daemon
* @return array
* @throws Exception
*/
private function mergeConfig($config, $daemon): array
{
$config['settings'] = $config['settings'] ?? [];
2021-11-04 16:19:55 +08:00
$config['settings']['daemonize'] = $daemon;
2021-11-03 15:17:52 +08:00
if (!isset($config['settings']['log_file'])) {
$config['settings']['log_file'] = storage('system.log');
}
$config['settings']['pid_file'] = storage('.swoole.pid');
2021-11-18 16:54:15 +08:00
$config['settings'][Constant::OPTION_ENABLE_REUSE_PORT] = true;
2021-11-03 15:17:52 +08:00
$config['events'] = $config['events'] ?? [];
return $config;
}
/**
* @param string $type
* @param string $host
* @param int $port
* @param int $mode
* @param array $settings
2021-11-30 14:32:56 +08:00
* @throws ConfigException
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
2021-11-03 15:17:52 +08:00
* @throws Exception
*/
private function addNewListener(string $type, string $host, int $port, int $mode, array $settings = [])
{
$id = Config::get('id', 'system-service');
2021-12-01 15:16:08 +08:00
$this->logger->debug(sprintf('[%s].' . $type . ' service %s::%d start', $id, $host, $port));
2021-11-17 16:39:12 +08:00
2021-11-03 15:17:52 +08:00
/** @var Server\Port $service */
$this->ports[$port] = $this->server->addlistener($host, $port, $mode);
if ($this->ports[$port] === false) {
throw new Exception("The port is already in use[$host::$port]");
}
if ($type == Constant::SERVER_TYPE_HTTP && !isset($settings['settings']['open_http_protocol'])) {
$settings['settings']['open_http_protocol'] = true;
if (in_array($this->server->setting['dispatch_mode'], [2, 4])) {
$settings['settings']['open_http2_protocol'] = true;
}
}
if ($type == Constant::SERVER_TYPE_WEBSOCKET && !isset($settings['settings']['open_websocket_protocol'])) {
$settings['settings']['open_websocket_protocol'] = true;
}
$this->ports[$port]->set($settings['settings'] ?? []);
$this->addServiceEvents($settings['events'] ?? [], $this->ports[$port]);
}
/**
* @param string $type
* @param string $host
* @param int $port
* @param int $mode
* @param array $settings
* @throws ConfigException
2021-11-30 14:32:56 +08:00
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
2021-11-17 16:39:12 +08:00
* @throws Exception
2021-11-03 15:17:52 +08:00
*/
private function createBaseServer(string $type, string $host, int $port, int $mode, array $settings = [])
{
$match = match ($type) {
Constant::SERVER_TYPE_BASE, Constant::SERVER_TYPE_TCP,
Constant::SERVER_TYPE_UDP => Server::class,
Constant::SERVER_TYPE_HTTP => HServer::class,
Constant::SERVER_TYPE_WEBSOCKET => WServer::class
};
$this->server = new $match($host, $port, SWOOLE_PROCESS, $mode);
$this->server->set(array_merge(Config::get('server.settings', []), $settings['settings']));
2022-01-07 18:38:45 +08:00
$maxLength = $this->getMaxKeyLength($this->server->setting);
2022-01-07 18:41:28 +08:00
$string = sprintf("| %s \t| %s \t|\n", str_pad('名称', $maxLength - mb_strlen('名称')), '值');
2022-01-07 18:29:08 +08:00
foreach ($this->server->setting as $key => $value) {
2022-01-07 18:41:28 +08:00
$string .= sprintf("| %s \t| %s \t|\n", str_pad($key, $maxLength - mb_strlen($key),' '), $value);
2022-01-07 18:29:08 +08:00
}
2022-01-07 18:45:44 +08:00
print_r($string);
2022-01-07 18:27:01 +08:00
2021-11-03 15:17:52 +08:00
$id = Config::get('id', 'system-service');
2021-12-08 11:16:35 +08:00
$this->logger->debug(sprintf('[%s].' . $type . ' service %s::%d start', $id, $host, $port));
2021-11-03 15:17:52 +08:00
$this->addDefaultListener($settings);
}
2022-01-07 18:38:45 +08:00
private function getMaxKeyLength($array)
{
$length = 0;
foreach ($array as $key => $val) {
2022-01-07 18:41:28 +08:00
if (mb_strlen($key) > $length) {
$length = mb_strlen($key);
2022-01-07 18:38:45 +08:00
}
}
return $length;
}
2021-11-03 15:17:52 +08:00
/**
* @param array $settings
2021-11-30 14:32:56 +08:00
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
2021-11-03 15:17:52 +08:00
*/
private function addDefaultListener(array $settings): void
{
if (($this->server->setting['task_worker_num'] ?? 0) > 0) {
$this->addTaskListener($settings['events']);
}
$this->container->setBindings(SwooleServerInterface::class, $this->server);
$this->addServiceEvents(ServerManager::DEFAULT_EVENT, $this->server);
if (!empty($settings['events']) && is_array($settings['events'])) {
$this->addServiceEvents($settings['events'], $this->server);
}
}
2021-11-30 14:32:56 +08:00
/**
* @param array $events
* @param Server|Port $server
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
2021-11-03 15:17:52 +08:00
private function addServiceEvents(array $events, Server|Port $server)
{
foreach ($events as $name => $event) {
if (is_array($event) && is_string($event[0])) {
$event[0] = $this->container->get($event[0]);
}
$server->on($name, $event);
}
}
/**
*
*/
public function start()
{
$this->server->start();
}
/**
* @param mixed $message
* @param int $workerId
* @return mixed
*/
public function sendMessage(mixed $message, int $workerId): mixed
{
return $this->server?->sendMessage($message, $workerId);
}
/**
* @param array $events
2021-12-08 11:16:35 +08:00
* @return void
2021-12-01 15:16:08 +08:00
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
2021-11-03 15:17:52 +08:00
*/
private function addTaskListener(array $events = []): void
{
$task_use_object = $this->server->setting['task_object'] ?? $this->server->setting['task_use_object'] ?? false;
2021-12-01 15:16:08 +08:00
$reflect = $this->container->get(OnServerTask::class);
2021-11-17 16:39:12 +08:00
$this->server->on('finish', $events[Constant::FINISH] ?? [$reflect, 'onFinish']);
2021-11-03 15:17:52 +08:00
if ($task_use_object || $this->server->setting['task_enable_coroutine']) {
$this->server->on('task', $events[Constant::TASK] ?? [$reflect, 'onCoroutineTask']);
} else {
$this->server->on('task', $events[Constant::TASK] ?? [$reflect, 'onTask']);
}
}
/**
* @param array|null $settings
2021-12-08 11:16:35 +08:00
* @return void
2021-11-30 14:32:56 +08:00
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
2021-11-03 15:17:52 +08:00
*/
2021-11-17 16:39:12 +08:00
public function bindCallback(?array $settings = [])
2021-11-03 15:17:52 +08:00
{
if (count($settings) < 1) {
return;
}
foreach ($settings as $event_type => $callback) {
if ($this->server->getCallback($event_type) !== null) {
continue;
}
if (is_array($callback) && !is_object($callback[0])) {
$callback[0] = $this->container->get($callback[0]);
}
$this->server->on($event_type, $callback);
}
}
}