Skip to content
This repository has been archived by the owner on Jan 17, 2022. It is now read-only.

Commit

Permalink
feat(api-server): Create API Server component
Browse files Browse the repository at this point in the history
API Server component will allow to manage Swoole HTTP Server trough HTTP interface
  • Loading branch information
k911 committed Mar 17, 2019
1 parent 741cd3e commit 8bca571
Show file tree
Hide file tree
Showing 22 changed files with 967 additions and 46 deletions.
44 changes: 32 additions & 12 deletions src/Bridge/Symfony/Bundle/Command/AbstractServerStartCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use function K911\Swoole\decode_string_as_set;
use function K911\Swoole\format_bytes;
use function K911\Swoole\get_max_memory;
use K911\Swoole\Server\Config\Socket;
use K911\Swoole\Server\Configurator\ConfiguratorInterface;
use K911\Swoole\Server\HttpServer;
use K911\Swoole\Server\HttpServerConfiguration;
Expand Down Expand Up @@ -71,13 +72,16 @@ private function getDefaultPublicDir(): string
*/
protected function configure(): void
{
$defaultSocket = $this->serverConfiguration->getDefaultSocket();
$this->addOption('host', null, InputOption::VALUE_REQUIRED, 'Host name to listen to.', $defaultSocket->host())
->addOption('port', null, InputOption::VALUE_REQUIRED, 'Range 0-65535. When 0 random available port is chosen.', $defaultSocket->port())
->addOption('serve-static', 's', InputOption::VALUE_NONE, 'Enables serving static content from public directory.')
$sockets = $this->serverConfiguration->getSockets();
$serverSocket = $sockets->getServerSocket();
$this->addOption('host', null, InputOption::VALUE_REQUIRED, 'Host name to bind to. To bind to any host, use: 0.0.0.0', $serverSocket->host())
->addOption('port', null, InputOption::VALUE_REQUIRED, 'Listen for Swoole HTTP Server on this port, when 0 random available port is chosen', $serverSocket->port())
->addOption('serve-static', 's', InputOption::VALUE_NONE, 'Enables serving static content from public directory')
->addOption('public-dir', null, InputOption::VALUE_REQUIRED, 'Public directory', $this->getDefaultPublicDir())
->addOption('trusted-hosts', null, InputOption::VALUE_REQUIRED, 'Trusted hosts', $this->parameterBag->get('swoole.http_server.trusted_hosts'))
->addOption('trusted-proxies', null, InputOption::VALUE_REQUIRED, 'Trusted proxies', $this->parameterBag->get('swoole.http_server.trusted_proxies'));
->addOption('trusted-proxies', null, InputOption::VALUE_REQUIRED, 'Trusted proxies', $this->parameterBag->get('swoole.http_server.trusted_proxies'))
->addOption('api', null, InputOption::VALUE_NONE, 'Enable API Server')
->addOption('api-port', null, InputOption::VALUE_REQUIRED, 'Listen for API Server on this port', $this->parameterBag->get('swoole.http_server.api.port'));
}

private function ensureXdebugDisabled(SymfonyStyle $io): void
Expand Down Expand Up @@ -134,9 +138,12 @@ final protected function execute(InputInterface $input, OutputInterface $output)
exit(1);
}

$sockets = $this->serverConfiguration->getSockets();
$serverSocket = $sockets->getServerSocket();
$server = HttpServerFactory::make(
$this->serverConfiguration->getDefaultSocket(),
$this->serverConfiguration->getRunningMode()
$serverSocket,
$this->serverConfiguration->getRunningMode(),
...($sockets->hasApiSocket() ? [$sockets->getApiSocket()] : [])
);
$this->serverConfigurator->configure($server);
$this->server->attach($server);
Expand All @@ -147,7 +154,10 @@ final protected function execute(InputInterface $input, OutputInterface $output)
$runtimeConfiguration = ['symfonyStyle' => $io] + $this->prepareRuntimeConfiguration($this->serverConfiguration, $input);
$this->bootManager->boot($runtimeConfiguration);

$io->success(\sprintf('Swoole HTTP Server started on http://%s', $this->serverConfiguration->getDefaultSocket()->addressPort()));
$io->success(\sprintf('Swoole HTTP Server started on http://%s', $serverSocket->addressPort()));
if ($sockets->hasApiSocket()) {
$io->success(\sprintf('API Server started on http://%s', $sockets->getApiSocket()->addressPort()));
}
$io->table(['Configuration', 'Values'], $this->prepareConfigurationRowsToPrint($this->serverConfiguration, $runtimeConfiguration));

$this->startServer($this->serverConfiguration, $this->server, $io);
Expand All @@ -163,16 +173,26 @@ final protected function execute(InputInterface $input, OutputInterface $output)
*/
protected function prepareServerConfiguration(HttpServerConfiguration $serverConfiguration, InputInterface $input): void
{
$sockets = $serverConfiguration->getSockets();

$port = $input->getOption('port');
$host = $input->getOption('host');
Assertion::numeric($port, 'Port must be numeric');
Assertion::string($host, 'Host must be string');

$socket = $serverConfiguration->getDefaultSocket()
Assertion::numeric($port, 'Port must be a number.');
Assertion::string($host, 'Host must be a string.');

$newServerSocket = $sockets->getServerSocket()
->withPort((int) $port)
->withHost($host);

$serverConfiguration->changeDefaultSocket($socket);
$sockets->changeServerSocket($newServerSocket);

if ((bool) $input->getOption('api') || $sockets->hasApiSocket()) {
$apiPort = $input->getOption('api-port');
Assertion::numeric($apiPort, 'Port must be a number.');

$sockets->changeApiSocket(new Socket('0.0.0.0', (int) $apiPort));
}

if (\filter_var($input->getOption('serve-static'), FILTER_VALIDATE_BOOLEAN)) {
$publicDir = $input->getOption('public-dir');
Expand Down
103 changes: 103 additions & 0 deletions src/Bridge/Symfony/Bundle/Command/ServerStatusCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

declare(strict_types=1);

namespace K911\Swoole\Bridge\Symfony\Bundle\Command;

use Assert\Assertion;
use K911\Swoole\Server\Api\ApiServerInterface;
use K911\Swoole\Server\Config\Socket;
use K911\Swoole\Server\Config\Sockets;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;

final class ServerStatusCommand extends Command
{
private $apiServer;
private $sockets;
private $parameterBag;

public function __construct(
Sockets $sockets,
ApiServerInterface $apiServer,
ParameterBagInterface $parameterBag
) {
$this->apiServer = $apiServer;
$this->sockets = $sockets;
$this->parameterBag = $parameterBag;

parent::__construct();
}

/**
* {@inheritdoc}
*/
protected function configure(): void
{
$this->setDescription('Get current status of the Swoole HTTP Server by querying running API Server.')
->addOption('api.host', null, InputOption::VALUE_REQUIRED, 'API Server listens on this host.', $this->parameterBag->get('swoole.http_server.api.host'))
->addOption('api.port', null, InputOption::VALUE_REQUIRED, 'API Server listens on this port.', $this->parameterBag->get('swoole.http_server.api.port'));
}

/**
* {@inheritdoc}
*
* @throws \Assert\AssertionFailedException
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);

$this->prepareClientConfiguration($input);

\go(function () use ($io): void {
// $status = $this->apiServer->status();
// dump($status);
$metrics = $this->apiServer->metrics();
// dump($metrics);

$date = \DateTimeImmutable::createFromFormat(DATE_ATOM, $metrics['date']);
$runningSeconds = $date->getTimestamp() - $metrics['server']['start_time'];

$idleWorkers = $metrics['server']['idle_worker_num'];
$workers = $metrics['server']['worker_num'];

$io->success('Fetched metrics');

$io->table([
'Metric', 'Quantity', 'Unit',
], [
['Requests', $metrics['server']['request_count'], '1'],
['Up time', $runningSeconds, 'Seconds'],
['Active connections', $metrics['server']['connection_num'], '1'],
['Accepted connections', $metrics['server']['accept_count'], '1'],
['Closed connections', $metrics['server']['close_count'], '1'],
['Active workers', $workers - $idleWorkers, '1'],
['Idle workers', $idleWorkers, '1'],
]);
});
\swoole_event_wait();

return 0;
}

/**
* @param InputInterface $input
*
* @throws \Assert\AssertionFailedException
*/
protected function prepareClientConfiguration(InputInterface $input): void
{
$port = $input->getOption('api.port');
$host = $input->getOption('api.host');

Assertion::numeric($port, 'Port must be a number.');
Assertion::string($host, 'Host must be a string.');

$this->sockets->changeApiSocket(new Socket($host, (int) $port));
}
}
28 changes: 28 additions & 0 deletions src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,34 @@ public function getConfigTreeBuilder(): TreeBuilder
->treatFalseLike('off')
->values(['off', 'auto', 'inotify'])
->end()
->arrayNode('api')
->addDefaultsIfNotSet()
->beforeNormalization()
->ifTrue(function ($v): bool {
return \is_string($v) || \is_bool($v) || \is_numeric($v) || null === $v;
})
->then(function ($v): array {
return [
'enabled' => (bool) $v,
'host' => '0.0.0.0',
'port' => 9200,
];
})
->end()
->children()
->booleanNode('enabled')
->defaultFalse()
->end()
->scalarNode('host')
->cannotBeEmpty()
->defaultValue('0.0.0.0')
->end()
->scalarNode('port')
->cannotBeEmpty()
->defaultValue(9200)
->end()
->end()
->end()
->arrayNode('static')
->addDefaultsIfNotSet()
->beforeNormalization()
Expand Down
14 changes: 12 additions & 2 deletions src/Bridge/Symfony/Bundle/DependencyInjection/SwooleExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use K911\Swoole\Bridge\Symfony\HttpFoundation\TrustAllProxiesRequestHandler;
use K911\Swoole\Bridge\Symfony\HttpKernel\DebugHttpKernelRequestHandler;
use K911\Swoole\Server\Config\Socket;
use K911\Swoole\Server\Config\Sockets;
use K911\Swoole\Server\Configurator\ConfiguratorInterface;
use K911\Swoole\Server\Configurator\WithWorkerStartHandler;
use K911\Swoole\Server\HttpServerConfiguration;
Expand Down Expand Up @@ -72,6 +73,8 @@ private function registerHttpServer(array $config, ContainerBuilder $container):

$container->setParameter('swoole.http_server.trusted_proxies', $config['trusted_proxies']);
$container->setParameter('swoole.http_server.trusted_hosts', $config['trusted_hosts']);
$container->setParameter('swoole.http_server.api.host', $config['api']['host']);
$container->setParameter('swoole.http_server.api.port', $config['api']['port']);

$this->registerHttpServerConfiguration($config, $container);
}
Expand All @@ -80,6 +83,7 @@ private function registerHttpServerConfiguration(array $config, ContainerBuilder
{
[
'static' => $static,
'api' => $api,
'hmr' => $hmr,
'host' => $host,
'port' => $port,
Expand Down Expand Up @@ -113,9 +117,15 @@ private function registerHttpServerConfiguration(array $config, ContainerBuilder
$hmr = $this->resolveAutoHMR();
}

$defaultSocket = new Definition(Socket::class, [$host, $port, $socketType, $sslEnabled]);
$sockets = $container->getDefinition(Sockets::class)
->addArgument(new Definition(Socket::class, [$host, $port, $socketType, $sslEnabled]));

if ($api['enabled']) {
$sockets->addArgument(new Definition(Socket::class, [$api['host'], $api['port']]));
}

$container->getDefinition(HttpServerConfiguration::class)
->addArgument($defaultSocket)
->addArgument(new Reference(Sockets::class))
->addArgument($runningMode)
->addArgument($settings);

Expand Down
5 changes: 5 additions & 0 deletions src/Bridge/Symfony/Bundle/Resources/config/commands.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ services:
autoconfigure: true
public: false

'K911\Swoole\Bridge\Symfony\Bundle\Command\ServerStatusCommand':
tags: [ { name: console.command, command: 'swoole:server:status' } ]
arguments:
$apiServer: '@K911\Swoole\Server\Api\ApiServerClient'

'K911\Swoole\Bridge\Symfony\Bundle\Command\ServerStopCommand':
tags: [ { name: console.command, command: 'swoole:server:stop' } ]

Expand Down
17 changes: 16 additions & 1 deletion src/Bridge/Symfony/Bundle/Resources/config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,28 @@ services:

'K911\Swoole\Server\HttpServer':

'K911\Swoole\Server\Api\ApiServerClient':

'K911\Swoole\Server\Api\ApiServerInterface':
class: K911\Swoole\Server\Api\ApiServer

# Could be helpful for projects that uses/have included proxy-manager
# lazy: true
# tags:
# - { name: proxy, interface: K911\Swoole\Server\Api\ApiServerInterface }

'K911\Swoole\Server\Config\Sockets':

'K911\Swoole\Server\HttpServerConfiguration':

'K911\Swoole\Server\Configurator\WithHttpServerConfiguration':

'K911\Swoole\Server\Api\WithApiServerConfiguration':

'K911\Swoole\Server\Configurator\CallableChainConfiguratorFactory':

'K911\Swoole\Server\Api\ApiServerRequestHandler':

'swoole_bundle.server.http_server.configurator_collection':
class: K911\Swoole\Component\GeneratedCollection
arguments: [!tagged 'swoole_bundle.server_configurator']
Expand All @@ -61,4 +77,3 @@ services:
autoconfigure: false
arguments:
$handler: '@K911\Swoole\Server\LifecycleHandler\SigIntHandler'

Loading

0 comments on commit 8bca571

Please sign in to comment.