This commit is contained in:
as2252258@163.com
2021-07-11 03:57:25 +08:00
parent 27dc21e24e
commit 12d547c1a2
13 changed files with 1038 additions and 816 deletions
+28 -28
View File
@@ -9,6 +9,7 @@ use Exception;
use HttpServer\Http\Request;
use HttpServer\Route\Router;
use JetBrains\PhpStorm\Pure;
use Rpc\Actuator;
use Snowflake\Snowflake;
@@ -19,39 +20,38 @@ use Snowflake\Snowflake;
#[\Attribute(\Attribute::TARGET_METHOD)] class RpcProducer extends Attribute
{
private string $uri = '';
const PROTOCOL_JSON = 'json';
const PROTOCOL_SERIALIZE = 'serialize';
const PROTOCOL_JSON = 'json';
const PROTOCOL_SERIALIZE = 'serialize';
/**
* Route constructor.
* @param string $cmd
* @param string $protocol
* @param int $port
*/
#[Pure] public function __construct(public string $cmd, public string $protocol = self::PROTOCOL_SERIALIZE, public int $port = 443)
{
$this->uri = 'rpc/p' . $this->port . '/' . ltrim($this->cmd, '/');
}
/**
* Route constructor.
* @param string $cmd
* @param string $protocol
* @param int $port
*/
#[Pure] public function __construct(public string $cmd, public string $protocol = self::PROTOCOL_SERIALIZE, public int $port = 443)
{
}
/**
* @param array $handler
* @return Router
* @throws Exception
*/
/**
* @param array $handler
* @return Router
* @throws Exception
*/
public function execute(mixed $class, mixed $method = null): Router
{
// TODO: Implement setHandler() method.
$router = Snowflake::app()->getRouter();
$router->addRoute($this->uri, [$class, $method], Request::HTTP_CMD)
->setDataType($this->protocol);
return $router;
}
{
// TODO: Implement setHandler() method.
$router = Snowflake::app()->getRouter();
$cmd = $this->cmd;
$callback = function (Actuator $actuator) use ($cmd, $class, $method) {
$actuator->addListener($cmd, $class . '@' . $method);
};
$router->addRpcService($this->port, $callback);
return $router;
}
}
+3 -5
View File
@@ -25,12 +25,10 @@ class OnWorkerExit extends Callback
public function onHandler($server, $worker_id)
{
putenv('state=exit');
$channel = Snowflake::app()->getChannel();
$channel->cleanAll();
Event::trigger(Event::SERVER_WORKER_EXIT);
Event::trigger(Event::SERVER_WORKER_EXIT, [$server, $worker_id]);
logger()->insert();
}
Snowflake::getApp('logger')->insert();
}
}
-3
View File
@@ -26,9 +26,6 @@ class OnWorkerStop extends Callback
{
Event::trigger(Event::SERVER_WORKER_STOP);
$this->clearMysqlClient();
$this->clearRedisClient();
fire(Event::SYSTEM_RESOURCE_CLEAN);
Timer::clearAll();
File diff suppressed because it is too large Load Diff
+18 -35
View File
@@ -276,7 +276,7 @@ class Server extends HttpService
/**
* @param $config
* @return \Swoole\Server|Packet|Receive|Http|Websocket|null
* @return mixed
* @throws ConfigException
* @throws Exception
*/
@@ -288,7 +288,7 @@ class Server extends HttpService
}
$server = $this->dispatchCreate($config, $settings);
if (isset($config['events'])) {
$this->createEventListen($config);
$this->createEventListen($server, $config);
}
return $server;
}
@@ -298,33 +298,26 @@ class Server extends HttpService
* @param $config
* @throws Exception
*/
protected function createEventListen($config)
protected function createEventListen($server, $config)
{
if (!is_array($config['events'])) {
return;
}
foreach ($config['events'] as $name => $_event) {
if ($name !== Event::SERVER_CLIENT_CLOSE) {
Event::on('listen ' . $config['port'] . ' ' . $name, $_event);
} else {
Event::on($name, $_event);
}
$server->on($name, $_event);
}
}
/**
* @param $config
* @param $settings
* @return \Swoole\Server|Packet|Receive|Http|Websocket|null
* @return mixed
* @throws Exception
*/
private function dispatchCreate($config, $settings): \Swoole\Server|Packet|Receive|Http|Websocket|null
private function dispatchCreate($config, $settings): mixed
{
if (Snowflake::port_already($config['port'])) {
return $this->error_stop($config['host'], $config['port']);
}
if (!($this->swoole instanceof \Swoole\Server)) {
return $this->parseServer($config, $settings);
$this->parseServer($config, $settings);
}
return $this->addListener($config);
}
@@ -405,7 +398,17 @@ class Server extends HttpService
*/
private function onListenerBind($server, $config): Packet|Websocket|Receive|Http|null
{
$this->bindServerEvent($server, $config['type']);
if (self::PACKAGE == $config['type']) {
$this->onBindCallback($server, 'packet', $config['events'][Event::SERVER_ON_PACKET] ?? [make(OnPacket::class), 'onHandler']);
} else if ($config['type'] == self::TCP) {
$this->onBindCallback($server, 'connect', $config['events'][Event::SERVER_ON_CONNECT] ?? [make(OnConnect::class), 'onHandler']);
$this->onBindCallback($server, 'close', $config['events'][Event::SERVER_ON_CLOSE] ?? [make(OnClose::class), 'onHandler']);
$this->onBindCallback($server, 'receive', $config['events'][Event::SERVER_ON_RECEIVE] ?? [make(OnReceive::class), 'onHandler']);
} else if ($config['type'] === self::HTTP) {
$this->onBindCallback($server, 'request', $config['events'][Event::SERVER_ON_REQUEST] ?? [make(OnRequest::class), 'onHandler']);
} else {
throw new Exception('Unknown server type(' . $config['type'] . ').');
}
$this->debug(sprintf('Check listen %s::%d -> ok', $config['host'], $config['port']));
@@ -413,26 +416,6 @@ class Server extends HttpService
}
/**
* @param string $type
* @throws Exception
*/
private function bindServerEvent($server, $type = self::TCP)
{
if (self::PACKAGE == $type) {
$this->onBindCallback($server, 'packet', [make(OnPacket::class), 'onHandler']);
} else if ($type == self::TCP) {
$this->onBindCallback($server, 'connect', [make(OnConnect::class), 'onHandler']);
$this->onBindCallback($server, 'close', [make(OnClose::class), 'onHandler']);
$this->onBindCallback($server, 'receive', [make(OnReceive::class), 'onHandler']);
} else if ($type === self::HTTP) {
$this->onBindCallback($server, 'request', [make(OnRequest::class), 'onHandler']);
} else {
throw new Exception('Unknown server type(' . $type . ').');
}
}
/**
* @param $name
* @param $callback
+94 -66
View File
@@ -10,85 +10,113 @@ use Snowflake\Application;
use Snowflake\Exception\NotFindClassException;
use Snowflake\Snowflake;
/**
* Trait Server
* @package HttpServer\Service\Abstracts
*/
trait Server
{
public ?Application $application = null;
public ?Application $application = null;
/**
* Server constructor.
* @param $host
* @param null $port
* @param null $mode
* @param null $sock_type
*/
public function __construct($host, $port = null, $mode = null, $sock_type = null)
{
parent::__construct($host, $port, $mode, $sock_type);
}
/**
* Server constructor.
* @param $host
* @param null $port
* @param null $mode
* @param null $sock_type
*/
public function __construct($host, $port = null, $mode = null, $sock_type = null)
{
parent::__construct($host, $port, $mode, $sock_type);
}
/**
* @param array $settings
*/
public function set(array $settings)
{
parent::set($settings); // TODO: Change the autogenerated stub
$this->onInit();
}
/**
* @param array $settings
*/
public function set(array $settings)
{
parent::set($settings); // TODO: Change the autogenerated stub
$this->onInit();
}
/**
* @return void
* @throws NotFindClassException
* @throws ReflectionException
*/
public function onHandlerListener(): void
{
$this->on('WorkerStop', $this->createHandler('workerStop'));
$this->on('WorkerExit', $this->createHandler('workerExit'));
$this->on('WorkerStart', $this->createHandler('workerStart'));
$this->on('WorkerError', $this->createHandler('workerError'));
$this->on('ManagerStart', $this->createHandler('managerStart'));
$this->on('ManagerStop', $this->createHandler('managerStop'));
$this->on('PipeMessage', $this->createHandler('pipeMessage'));
$this->on('Shutdown', $this->createHandler('shutdown'));
$this->on('Start', $this->createHandler('start'));
$this->addTask();
}
/**
* @return void
* @throws NotFindClassException
* @throws ReflectionException
*/
public function onHandlerListener(): void
{
$this->on('Shutdown', $this->createHandler('shutdown'));
$this->on('Start', $this->createHandler('start'));
if ($this->setting['task_worker_num'] ?? 0 > 0) {
$this->on('Finish', $this->createHandler('finish'));
$this->on('Task', $this->createHandler('task'));
}
$this->onManager();
}
/**
* @throws NotFindClassException
* @throws ReflectionException
*/
protected function addTask()
{
$settings = $this->setting;
if (($taskNumber = $settings['task_worker_num'] ?? 0) > 0) {
$this->on('Finish', $this->createHandler('finish'));
$this->on('Task', $this->createHandler('task'));
}
}
/**
* @throws \ReflectionException
* @throws \Snowflake\Exception\NotFindClassException
*/
private function onManager()
{
$this->on('ManagerStart', $this->createHandler('managerStart'));
$this->on('ManagerStop', $this->createHandler('managerStop'));
$this->onWorker();
$this->onOther();
}
/**
* @param $eventName
* @return array
* @throws NotFindClassException
* @throws ReflectionException
* @throws Exception
*/
protected function createHandler($eventName): array
{
$classPrefix = 'HttpServer\Events\On' . ucfirst($eventName);
if (!class_exists($classPrefix)) {
throw new Exception('class not found.');
}
$class = Snowflake::createObject($classPrefix, [Snowflake::app()]);
return [$class, 'onHandler'];
}
/**
* @throws \ReflectionException
* @throws \Snowflake\Exception\NotFindClassException
*/
private function onWorker()
{
$this->on('WorkerStop', $this->createHandler('workerStop'));
$this->on('WorkerExit', $this->createHandler('workerExit'));
$this->on('WorkerStart', $this->createHandler('workerStart'));
$this->on('WorkerError', $this->createHandler('workerError'));
}
/**
* @throws \ReflectionException
* @throws \Snowflake\Exception\NotFindClassException
*/
private function onOther()
{
$this->on('PipeMessage', $this->createHandler('pipeMessage'));
$this->on('BeforeReload', $this->createHandler('BeforeReload'));
$this->on('AfterReload', $this->createHandler('AfterReload'));
}
/**
* @param $eventName
* @return array
* @throws NotFindClassException
* @throws ReflectionException
* @throws Exception
*/
protected function createHandler($eventName): array
{
$classPrefix = 'HttpServer\Events\On' . ucfirst($eventName);
if (!class_exists($classPrefix)) {
throw new Exception('class not found.');
}
$class = Snowflake::createObject($classPrefix, [Snowflake::app()]);
return [$class, 'onHandler'];
}
}
-2
View File
@@ -33,9 +33,7 @@ class Packet extends Tcp
*/
public function onBaseListener()
{
$this->on('connect', $this->createHandler('connect'));
$this->on('packet', $this->createHandler('packet'));
$this->on('close', $this->createHandler('close'));
}
+44
View File
@@ -0,0 +1,44 @@
<?php
namespace Rpc;
use HttpServer\Route\Router;
use Snowflake\Snowflake;
/**
* Class Actuator
* @package Rpc
*/
class Actuator
{
private Router $router;
/**
* Actuator constructor.
* @param int $port
* @throws \Exception
*/
public function __construct(public int $port)
{
$this->router = Snowflake::getApp('router');
}
/**
* @param string $path
* @param string|callable $callback
* @throws \Exception
*/
public function addListener(string $path, string|callable $callback): void
{
$this->router->addRoute('rpc/p' . $this->port . '/' . ltrim($path, '/'), $callback);
}
}
+145 -34
View File
@@ -4,25 +4,21 @@ namespace Rpc;
use Exception;
use HttpServer\Events\OnClose;
use HttpServer\Events\OnConnect;
use HttpServer\Events\OnPacket;
use HttpServer\Events\OnReceive;
use HttpServer\Http\Context;
use HttpServer\Http\Request;
use HttpServer\Server;
use HttpServer\Route\Router;
use HttpServer\Service\Http;
use HttpServer\Service\Packet;
use HttpServer\Service\Receive;
use HttpServer\Service\Websocket;
use JetBrains\PhpStorm\Pure;
use ReflectionException;
use Snowflake\Abstracts\Component;
use Snowflake\Abstracts\Config;
use Snowflake\Core\Json;
use Snowflake\Event;
use Snowflake\Exception\ConfigException;
use Snowflake\Exception\NotFindClassException;
use Snowflake\Snowflake;
use Swoole\Server;
use function Swoole\Coroutine\defer;
/**
@@ -41,6 +37,22 @@ class Service extends Component
'open_websocket_protocol' => false,
];
const RPC_CONNECT = 'RPC::CONNECT';
const RPC_CLOSE = 'RPC::CLOSE';
private Router $router;
/**
* @throws \Exception
*/
public function init()
{
$this->router = Snowflake::getApp('router');
}
/**
* @param Packet|Websocket|Receive|Http|null $server
* @throws ConfigException
@@ -49,33 +61,19 @@ class Service extends Component
public function instance(Packet|Websocket|Receive|null|Http $server): void
{
$service = Config::get('rpc');
if (empty($service) || !is_array($service)) {
if (!is_array($service) || empty($service)) {
return;
}
$mode = $service['mode'] ?? SWOOLE_SOCK_TCP6;
if (Snowflake::port_already($service['port'])) {
throw new Exception($this->already($service));
$listen_type = $service['mode'] ?? SWOOLE_SOCK_TCP6;
$rpcServer = $server->addlistener($service['host'], $service['port'], $listen_type);
if ($rpcServer === false) {
throw new Exception('Listen rpc service fail.');
}
$this->debug(Snowflake::listen($service));
if (!isset($service['setting'])) {
$service['setting'] = self::defaultConfig;
}
$rpcServer = $server->addlistener($service['host'], $service['port'], $mode);
$rpcServer->set($service['setting']);
$this->addCallback($rpcServer, $mode);
}
/**
* @param $service
* @return string
*/
#[Pure] private function already($service): string
{
return sprintf('Port %s::%d is already.', $service['host'], $service['port']);
$rpcServer->set($service['setting'] ?? self::defaultConfig);
$this->addCallback($rpcServer, $service, $listen_type);
}
@@ -83,18 +81,131 @@ class Service extends Component
* @param $mode
* @throws Exception
*/
private function addCallback($rpcServer, $mode)
private function addCallback($rpcServer, $config, $mode)
{
$tcp = [SWOOLE_SOCK_TCP, SWOOLE_TCP, SWOOLE_TCP6, SWOOLE_SOCK_TCP6];
$server = Snowflake::app()->getServer();
if (in_array($mode, $tcp)) {
$server->onBindCallback($rpcServer, 'connect', [make(OnConnect::class), 'onHandler']);
$server->onBindCallback($rpcServer, 'close', [make(OnClose::class), 'onHandler']);
$server->onBindCallback($rpcServer, 'receive', [make(OnReceive::class), 'onHandler']);
$connectCallback = $config['events'][Event::SERVER_ON_CONNECT] ?? [$this, 'onConnect'];
$server->onBindCallback($rpcServer, 'connect', $connectCallback);
$connectCallback = $config['events'][Event::SERVER_ON_CLOSE] ?? [$this, 'onClose'];
$server->onBindCallback($rpcServer, 'close', $connectCallback);
$connectCallback = $config['events'][Event::SERVER_ON_CONNECT] ?? [$this, 'onReceive'];
$server->onBindCallback($rpcServer, 'receive', $connectCallback);
} else {
$server->onBindCallback($rpcServer, 'packet', [make(OnReceive::class), 'onHandler']);
$connectCallback = $config['events'][Event::SERVER_ON_PACKET] ?? [$this, 'onPacket'];
$server->onBindCallback($rpcServer, 'packet', $connectCallback);
}
}
/**
* @param \Swoole\Server $server
* @param int $fd
* @param int $reactorId
*/
private function onConnect(Server $server, int $fd, int $reactorId)
{
defer(fn() => fire(Event::SYSTEM_RESOURCE_RELEASES));
$config = $server->setting['enable_delay_receive'] ?? null;
if ($config === true) {
$server->confirm($fd);
}
Event::trigger(Service::RPC_CONNECT, [$server, $fd, $reactorId]);
}
/**
* @param \Swoole\Server $server
* @param int $fd
* on tcp client close
*/
private function onClose(Server $server, int $fd)
{
defer(fn() => fire(Event::SYSTEM_RESOURCE_RELEASES));
Event::trigger(Service::RPC_CLOSE, [$server, $fd]);
}
/**
* @param \Swoole\Server $server
* @param int $fd
* @param int $reID
* @param string $data
* @throws \Exception
*/
private function onReceive(Server $server, int $fd, int $reID, string $data)
{
defer(fn() => fire(Event::SYSTEM_RESOURCE_RELEASES));
try {
$client = $server->getClientInfo($fd, $reID);
$request = $this->requestSpl((int)$client['server_port'], $data);
$result = $this->router->find_path($request)?->dispatch();
$server->send($fd, $result);
} catch (\Throwable $exception) {
$this->addError($exception, 'rpc-service');
$server->send($fd, $exception->getMessage());
}
}
/**
* @param \Swoole\Server $server
* @param int $fd
* @param int $reID
* @param string $data
* @throws \Exception
*/
private function onPacket(Server $server, string $data, array $client)
{
defer(fn() => fire(Event::SYSTEM_RESOURCE_RELEASES));
try {
$request = $this->requestSpl((int)$client['server_port'], $data);
$result = $this->router->find_path($request)?->dispatch();
$server->sendto($client['address'], $client['port'], $result);
} catch (\Throwable $exception) {
$this->addError($exception, 'rpc-service');
$server->sendto($client['address'], $client['port'], $exception->getMessage());
}
}
/**
* @param \Swoole\Server $server
* @param int $fd
* @param int $reID
* @param string $data
* @return mixed
* @throws \Exception
*/
private function requestSpl(int $server_port, string $data)
{
$sRequest = new Request();
[$cmd, $repeat, $body] = explode("\n", $data);
if (is_null($body) || is_null($cmd) || !empty($repeat)) {
throw new Exception('Protocol format error.');
}
if (is_string($body) && is_null($data = Json::decode($body))) {
throw new Exception('Protocol format error.');
}
$sRequest->params->setPosts($data);
$sRequest->headers->setRequestUri('rpc/p' . $server_port . '/' . ltrim($cmd, '/'));
$sRequest->headers->setRequestMethod(Request::HTTP_CMD);
return Context::setContext('request', $sRequest);
}
}
+42
View File
@@ -0,0 +1,42 @@
<?php
use HttpServer\Server;
use Snowflake\Snowflake;
/** @var \HttpServer\Route\Router $router */
$router = Snowflake::getApp('router');
$router->addRpcService(9527, function (\Rpc\Actuator $actuator) {
$actuator->addListener('', '');
$actuator->addListener('', '');
});
return [
'rpc' => [
'type' => Server::TCP,
'host' => '0.0.0.0',
'mode' => SWOOLE_SOCK_TCP,
'port' => 5377,
'setting' => [
'open_tcp_keepalive' => true,
'tcp_keepidle' => 30,
'tcp_keepinterval' => 10,
'tcp_keepcount' => 10,
'open_http_protocol' => false,
'open_websocket_protocol' => false,
],
'events' => [
Server::SERVER_ON_CONNECT => [],
Server::SERVER_ON_CLOSE => [],
],
'registry' => [
'protocol' => 'consul',
'address' => [
'host' => '47.14.25.45',
'port' => 5527,
'path' => ''
],
],
]
];
+4 -11
View File
@@ -18,15 +18,16 @@ class Channel extends Component
{
private static array $_channels = [];
private static ?array $_channels = [];
private static array $_waitRecover = [];
private static ?array $_waitRecover = [];
public function init()
{
Event::on(Event::SYSTEM_RESOURCE_RELEASES, [$this, 'recover']);
Event::on(Event::SERVER_WORKER_EXIT, [$this, 'cleanAll']);
}
@@ -84,15 +85,7 @@ class Channel extends Component
*/
public function cleanAll()
{
/** @var SplQueue $channel */
foreach (static::$_channels as $channel) {
if (!($channel instanceof SplQueue)) {
continue;
}
while ($channel->count() > 0) {
$channel->dequeue();
}
}
static::$_channels = null;
static::$_channels = [];
}
+5 -9
View File
@@ -81,26 +81,22 @@ class Service extends Component
* @return mixed
* @throws Exception
*/
public function set($id, $definition): mixed
public function set($id, $definition): void
{
if ($definition === NULL) {
return $this->remove($id);
$this->remove($id);
return;
}
$this->_ids[] = $id;
unset($this->_components[$id]);
if (is_object($definition) || is_callable($definition, TRUE)) {
return $this->_definition[$id] = $definition;
$this->_definition[$id] = $definition;
} else if (!is_array($definition)) {
throw new ComponentException("Unexpected configuration type for the \"$id\" component: " . gettype($definition));
}
if (!isset($definition['class'])) {
throw new ComponentException("The configuration for the \"$id\" component must contain a \"class\" element.");
} else {
$this->_definition[$id] = $definition;
}
return $this->get($id);
$this->_definition[$id] = $definition;
}
/**
+19
View File
@@ -60,6 +60,25 @@ class Event extends BaseObject
const SERVER_MESSAGE = 'on message';
const SERVER_CLIENT_CLOSE = 'SERVER:CLIENT:CLOSE';
const SERVER_ON_START = 'Start';
const SERVER_ON_SHUTDOWN = 'Shutdown';
const SERVER_ON_WORKER_START = 'WorkerStart';
const SERVER_ON_WORKER_STOP = 'WorkerStop';
const SERVER_ON_WORKER_EXIT = 'WorkerExit';
const SERVER_ON_CONNECT = 'Connect';
const SERVER_ON_RECEIVE = 'Receive';
const SERVER_ON_PACKET = 'Packet';
const SERVER_ON_REQUEST = 'request';
const SERVER_ON_CLOSE = 'Close';
const SERVER_ON_TASK = 'Task';
const SERVER_ON_FINISH = 'Finish';
const SERVER_ON_PIPE_MESSAGE = 'PipeMessage';
const SERVER_ON_WORKER_ERROR = 'WorkerError';
const SERVER_ON_MANAGER_START = 'ManagerStart';
const SERVER_ON_MANAGER_STOP = 'ManagerStop';
const SERVER_ON_BEFORE_RELOAD = 'BeforeReload';
const SERVER_ON_AFTER_RELOAD = 'AfterReload';
/**
* @param $name