改名
This commit is contained in:
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Annotation\Rpc;
|
||||
|
||||
|
||||
use Annotation\Attribute;
|
||||
use Exception;
|
||||
use Kiri\Kiri;
|
||||
|
||||
|
||||
/**
|
||||
* Class Consumer
|
||||
* @package Annotation\Rpc
|
||||
*/
|
||||
#[\Attribute(\Attribute::TARGET_METHOD)] class Consumer extends Attribute
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* Consumer constructor.
|
||||
* @param string $cmd
|
||||
*/
|
||||
public function __construct(public string $cmd)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param mixed $class
|
||||
* @param mixed $method
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public function execute(mixed $class, mixed $method = ''): bool
|
||||
{
|
||||
$rpc = Kiri::app()->getRpc();
|
||||
$rpc->addConsumer($this->cmd, [$class, $method]);
|
||||
return true; // TODO: Change the autogenerated stub
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+9
-8
@@ -3,6 +3,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Console;
|
||||
|
||||
use Exception;
|
||||
use ReflectionException;
|
||||
use Kiri\Abstracts\BaseObject;
|
||||
use Kiri\Abstracts\TraitApplication;
|
||||
@@ -44,13 +45,13 @@ abstract class Command extends BaseObject implements CommandInterface
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @return mixed
|
||||
* @throws ComponentException
|
||||
* @throws NotFindClassException
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
/**
|
||||
* @param $name
|
||||
* @return mixed
|
||||
* @throws NotFindClassException
|
||||
* @throws ReflectionException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __get($name): mixed
|
||||
{
|
||||
if ($this->has($name)) {
|
||||
@@ -63,6 +64,7 @@ abstract class Command extends BaseObject implements CommandInterface
|
||||
/**
|
||||
* @param $name
|
||||
* @return bool
|
||||
* @throws
|
||||
*/
|
||||
private function has($name): bool
|
||||
{
|
||||
@@ -74,7 +76,6 @@ abstract class Command extends BaseObject implements CommandInterface
|
||||
* @param $name
|
||||
* @return mixed
|
||||
* @throws ReflectionException
|
||||
* @throws ComponentException
|
||||
* @throws NotFindClassException
|
||||
*/
|
||||
private function get($name): mixed
|
||||
|
||||
@@ -16,8 +16,9 @@ use HttpServer\Http\Formatter\HtmlFormatter;
|
||||
use HttpServer\Http\Formatter\JsonFormatter;
|
||||
use HttpServer\Http\Formatter\XmlFormatter;
|
||||
use HttpServer\IInterface\IFormatter;
|
||||
use Server\ResponseInterface;
|
||||
use Kiri\Exception\NotFindClassException;
|
||||
use Server\ResponseInterface;
|
||||
use Server\ServerManager;
|
||||
use Swoole\Http\Response as SResponse;
|
||||
|
||||
/**
|
||||
@@ -53,6 +54,29 @@ class Response extends HttpService implements ResponseInterface
|
||||
];
|
||||
|
||||
public int $fd = 0;
|
||||
private int $clientId = 0;
|
||||
private int $reactorId = 0;
|
||||
|
||||
|
||||
/**
|
||||
* @param int $int
|
||||
* @param int $reID
|
||||
*/
|
||||
public function setClientId(int $int, int $reID)
|
||||
{
|
||||
$this->clientId = $int;
|
||||
$this->reactorId = $reID;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getClientInfo(): mixed
|
||||
{
|
||||
$server = ServerManager::getContext()->getServer();
|
||||
return $server->getClientInfo($this->clientId, $this->reactorId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@@ -64,6 +88,15 @@ class Response extends HttpService implements ResponseInterface
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getClientId(): int
|
||||
{
|
||||
return $this->clientId;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $format
|
||||
* @return $this
|
||||
@@ -117,6 +150,7 @@ class Response extends HttpService implements ResponseInterface
|
||||
/**
|
||||
* @param string $path
|
||||
* @param bool $isChunk
|
||||
* @param int $offset
|
||||
* @param int $limit
|
||||
* @return $this|Response
|
||||
* @throws Exception
|
||||
|
||||
@@ -14,7 +14,6 @@ use HttpServer\IInterface\Middleware;
|
||||
use HttpServer\IInterface\RouterInterface;
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
use ReflectionException;
|
||||
use Rpc\Actuator;
|
||||
use Server\RequestInterface;
|
||||
use Kiri\Abstracts\Config;
|
||||
use Kiri\Exception\ConfigException;
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Rpc;
|
||||
|
||||
|
||||
use Exception;
|
||||
use HttpServer\Route\Router;
|
||||
use Kiri\Kiri;
|
||||
|
||||
|
||||
/**
|
||||
* Class Actuator
|
||||
* @package Rpc
|
||||
*/
|
||||
class Actuator
|
||||
{
|
||||
|
||||
|
||||
private Router $router;
|
||||
|
||||
|
||||
/**
|
||||
* Actuator constructor.
|
||||
* @param int $port
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct(public int $port)
|
||||
{
|
||||
$this->router = Kiri::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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Rpc;
|
||||
|
||||
|
||||
use Exception;
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
use Kiri\Abstracts\Component;
|
||||
use Kiri\Kiri;
|
||||
|
||||
|
||||
/**
|
||||
* Class Producer
|
||||
* @package Rpc
|
||||
*/
|
||||
class Producer extends Component
|
||||
{
|
||||
|
||||
private static array $producers = [];
|
||||
|
||||
|
||||
private static array $classAlias = [];
|
||||
|
||||
|
||||
private static array $consumers = [];
|
||||
|
||||
|
||||
private static array $cods = [];
|
||||
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param array $handler
|
||||
* @param array $node
|
||||
*/
|
||||
public function addProducer(string $name, array $handler, array $node)
|
||||
{
|
||||
static::$classAlias[$handler[0]::class] = $name;
|
||||
|
||||
static::$consumers[$name] = $handler[0];
|
||||
|
||||
static::$producers[$name] = $node;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $cmd
|
||||
* @param array $handler
|
||||
*/
|
||||
public function addConsumer(string $cmd, array $handler)
|
||||
{
|
||||
$class = $handler[0]::class;
|
||||
|
||||
if (!isset(static::$classAlias[$class])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$name = static::$classAlias[$class];
|
||||
|
||||
static::$cods[$name . '.' . $cmd] = $handler;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $cmd
|
||||
* @param mixed ...$params
|
||||
* @return mixed
|
||||
*/
|
||||
public function dispatch($cmd, mixed ...$params): mixed
|
||||
{
|
||||
$handler = static::$cods[$cmd] ?? null;
|
||||
if (empty($handler)) {
|
||||
return false;
|
||||
}
|
||||
return call_user_func($handler, ...$params);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public function get($name): mixed
|
||||
{
|
||||
if (!isset(static::$consumers[$name])) {
|
||||
throw new Exception('Unknown rpc client.');
|
||||
}
|
||||
return static::$consumers[$name];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getService(): array
|
||||
{
|
||||
$array = [];
|
||||
foreach (array_keys(static::$cods) as $key) {
|
||||
$explode = explode('.', $key);
|
||||
$prefix = array_shift($explode);
|
||||
|
||||
$explode = implode('.', $explode);
|
||||
if (!isset($array[$prefix])) {
|
||||
$array[$prefix] = [];
|
||||
}
|
||||
$array[$prefix][] = $explode;
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string|null $host
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getClient(string $name, string $host = null): Client
|
||||
{
|
||||
$producer = static::$producers[$name] ?? null;
|
||||
if ($producer === null) {
|
||||
throw new Exception('Unknown rpc client config.');
|
||||
}
|
||||
if (!empty($host)) {
|
||||
$producer['host'] = $host;
|
||||
} else if (!isset($producer['host'])) {
|
||||
$producer['host'] = Kiri::localhost();
|
||||
}
|
||||
$producerName = $this->getName($name, $producer);
|
||||
|
||||
$snowflake = Kiri::app();
|
||||
if (!$snowflake->has($producerName)) {
|
||||
return $snowflake->set($producerName, $this->definer($name, $producer));
|
||||
} else {
|
||||
return $snowflake->get($producerName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string|null $host
|
||||
* @return Client
|
||||
* @throws Exception
|
||||
*/
|
||||
public function consumer(string $name, string $host = null): Client
|
||||
{
|
||||
return $this->getClient($name, $host);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param $producer
|
||||
* @return array
|
||||
*/
|
||||
#[ArrayShape(['class' => "string", 'service' => "", 'config' => ""])]
|
||||
private function definer($name, $producer): array
|
||||
{
|
||||
return ['class' => Client::class, 'service' => $name, 'config' => $producer];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @return Client|bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __get($name): Client|bool
|
||||
{
|
||||
return $this->get($name); // TODO: Change the autogenerated stub
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param $config
|
||||
* @return string
|
||||
*/
|
||||
private function getName($name, $config): string
|
||||
{
|
||||
return 'rpc.client.' . $name . '.' . $config['host'];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
<?php
|
||||
|
||||
|
||||
use HttpServer\Server;
|
||||
use Kiri\Kiri;
|
||||
|
||||
/** @var \HttpServer\Route\Router $router */
|
||||
$router = Kiri::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,9 +4,11 @@ namespace Server\Constrict;
|
||||
|
||||
use Exception;
|
||||
use HttpServer\Http\Formatter\FileFormatter;
|
||||
use HttpServer\IInterface\IFormatter;
|
||||
use Kiri\Exception\NotFindClassException;
|
||||
use ReflectionException;
|
||||
use Server\ResponseInterface;
|
||||
use Kiri\Exception\NotFindClassException;
|
||||
use Swoole\Server;
|
||||
|
||||
|
||||
/**
|
||||
@@ -17,15 +19,19 @@ class ResponseEmitter
|
||||
|
||||
|
||||
/**
|
||||
* @param \Swoole\Http\Response|\Swoole\Http2\Response $response
|
||||
* @param \Swoole\Http\Response|\Swoole\Http2\Response|Server $response
|
||||
* @param ResponseInterface $emitter
|
||||
* @throws ReflectionException
|
||||
* @throws NotFindClassException
|
||||
* @throws ReflectionException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function sender(\Swoole\Http\Response|\Swoole\Http2\Response $response, ResponseInterface $emitter)
|
||||
public function sender(\Swoole\Http\Response|\Swoole\Http2\Response|Server $response, ResponseInterface $emitter)
|
||||
{
|
||||
$content = $emitter->configure($response)->getContent();
|
||||
if ($response instanceof Server) {
|
||||
$this->sendTcpData($response, $emitter, $content);
|
||||
return;
|
||||
}
|
||||
if ($content instanceof FileFormatter) {
|
||||
$this->download($content->getData(), $response);
|
||||
} else {
|
||||
@@ -35,6 +41,21 @@ class ResponseEmitter
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Server $response
|
||||
* @param ResponseInterface $emitter
|
||||
* @param IFormatter $formatter
|
||||
*/
|
||||
private function sendTcpData(Server $response, ResponseInterface $emitter, IFormatter $formatter)
|
||||
{
|
||||
if ($formatter instanceof FileFormatter) {
|
||||
$response->sendfile($emitter->getClientId(), $formatter->getData());
|
||||
} else {
|
||||
$response->send($emitter->getClientId(), $formatter->getData());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const IMAGES = [
|
||||
'png' => 'image/png',
|
||||
'jpeg' => 'image/jpeg',
|
||||
|
||||
@@ -112,11 +112,15 @@ trait Attributes
|
||||
|
||||
|
||||
/**
|
||||
* @param ReflectionClass $class
|
||||
* @param ReflectionClass|string $class
|
||||
* @return array
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
#[Pure] public function getMethods(ReflectionClass $class): array
|
||||
public function getMethods(ReflectionClass|string $class): array
|
||||
{
|
||||
if (is_string($class)) {
|
||||
$class = $this->getReflect($class);
|
||||
}
|
||||
return $this->_classMethod[$class->getName()] ?? [];
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "game-worker/rpc-server",
|
||||
"description": "db",
|
||||
"authors": [
|
||||
{
|
||||
"name": "XiangLin",
|
||||
"email": "as2252258@163.com"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": ">=8.0",
|
||||
"ext-json": "*",
|
||||
"ext-pdo": "*",
|
||||
"game-worker/snowflake": "dev-master",
|
||||
"game-worker/validator": "dev-master"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Rpc\\": "src/"
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
"kwn/php-rdkafka-stubs": "^2.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Annotation\Rpc;
|
||||
|
||||
|
||||
use Annotation\Attribute;
|
||||
use Exception;
|
||||
use Kiri\Kiri;
|
||||
|
||||
|
||||
/**
|
||||
* Class RpcClient
|
||||
* @package Annotation\Rpc
|
||||
*/
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)] class RpcConsumer extends Attribute
|
||||
{
|
||||
|
||||
/**
|
||||
* RpcClient constructor.
|
||||
* @param string $package
|
||||
* @param string $method
|
||||
* @param int $timeout
|
||||
* @param int $mode
|
||||
*/
|
||||
public function __construct(
|
||||
public string $package,
|
||||
public string $method,
|
||||
public int $timeout,
|
||||
public int $mode
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param mixed $class
|
||||
* @param mixed $method
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public function execute(mixed $class, mixed $method = ''): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Rpc\Annotation;
|
||||
|
||||
|
||||
use Annotation\Attribute;
|
||||
use Exception;
|
||||
use HttpServer\Route\Router;
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
use Kiri\Kiri;
|
||||
|
||||
|
||||
/**
|
||||
* Class RpcProducer
|
||||
* @package Annotation\Route
|
||||
*/
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)] class RpcService extends Attribute
|
||||
{
|
||||
|
||||
const PROTOCOL_JSON = 'json';
|
||||
const PROTOCOL_SERIALIZE = 'serialize';
|
||||
|
||||
|
||||
/**
|
||||
* Route constructor.
|
||||
* @param string $package
|
||||
* @param string $protocol
|
||||
* @param string $server
|
||||
* @param string $version
|
||||
*/
|
||||
#[Pure] public function __construct(public string $package, public string $protocol = self::PROTOCOL_SERIALIZE, public string $server = 'json-rpc',
|
||||
public string $version = 'v1.0')
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param mixed $class
|
||||
* @param mixed|null $method
|
||||
* @return Router
|
||||
* @throws Exception
|
||||
*/
|
||||
public function execute(mixed $class, mixed $method = null): Router
|
||||
{
|
||||
// TODO: Implement setHandler() method.
|
||||
$router = Kiri::app()->getRouter();
|
||||
|
||||
$methods = Kiri::getDi()->getMethods($class::class);
|
||||
foreach ($methods as $method => $reflectionMethod) {
|
||||
$router->addRoute(':rpc/' . $this->package . '/' . $method . '/' . $this->version, [$class, $method], $this->server);
|
||||
}
|
||||
return $router;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -6,7 +6,6 @@ namespace Rpc;
|
||||
|
||||
use Exception;
|
||||
use Kiri\Abstracts\Component;
|
||||
use Kiri\Channel;
|
||||
use Kiri\Core\Json;
|
||||
use Swoole\Coroutine\Client as CClient;
|
||||
|
||||
@@ -28,12 +27,13 @@ class Client extends Component
|
||||
|
||||
|
||||
/**
|
||||
* @param string $cmd
|
||||
* @param string $package
|
||||
* @param string $method
|
||||
* @param array $param
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public function dispatch(string $cmd, array $param): mixed
|
||||
public function dispatch(string $package, string $method, array $param): mixed
|
||||
{
|
||||
if (empty($this->config)) {
|
||||
return $this->addError('Related service not found(404)');
|
||||
@@ -44,12 +44,11 @@ class Client extends Component
|
||||
if (!$this->client->isConnected() && !$this->connect()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$isSend = $this->client->send(implode("\n", [$cmd, '', serialize($param)]));
|
||||
$isSend = $this->client->send(Protocol::encode($package, $method, $param));
|
||||
if ($isSend === false) {
|
||||
return $this->addError($this->client->errMsg . '(' . $this->client->errCode . ')');
|
||||
}
|
||||
defer(fn() => $this->clientRecover());
|
||||
defer(fn() => $this->client->close());
|
||||
if (is_bool($unpack = Json::decode($this->client->recv()))) {
|
||||
$unpack = $this->addError('Service return data format error(500)');
|
||||
}
|
||||
@@ -57,22 +56,6 @@ class Client extends Component
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Client
|
||||
* @throws Exception
|
||||
*/
|
||||
public function clientRecover(): static
|
||||
{
|
||||
$host = $this->config['host'] ?? '127.0.0.1';
|
||||
$port = $this->config['port'] ?? 0;
|
||||
if ($port < 0) {
|
||||
return $this;
|
||||
}
|
||||
$this->client = null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
@@ -113,7 +96,7 @@ class Client extends Component
|
||||
if ($port < 0) {
|
||||
throw new Exception('Related service not have port(404)');
|
||||
}
|
||||
$client = new CClient($this->config['mode'] ?? SWOOLE_SOCK_TCP);
|
||||
$client = new CClient(SWOOLE_SOCK_TCP);
|
||||
$client->set([
|
||||
'timeout' => 0.5,
|
||||
'connect_timeout' => 1.0,
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Rpc;
|
||||
|
||||
use HttpServer\Controller;
|
||||
use HttpServer\Exception\RequestException;
|
||||
use Rpc\Annotation\RpcService;
|
||||
use Server\RequestInterface;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
#[RpcService(package: "default", protocol: RpcService::PROTOCOL_JSON, server: 'json-rpc')]
|
||||
class DefaultRpcController extends Controller
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @param RequestInterface $request
|
||||
* @return int
|
||||
* @throws RequestException
|
||||
*/
|
||||
public function getSystemConfig(RequestInterface $request): int
|
||||
{
|
||||
return $request->int('a') + $request->int('b');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace Rpc;
|
||||
|
||||
use Exception;
|
||||
use JetBrains\PhpStorm\Pure;
|
||||
use Rpc\Annotation\RpcService;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class Protocol
|
||||
{
|
||||
|
||||
private string $version = 'v1.0';
|
||||
|
||||
|
||||
private array $headers = [];
|
||||
|
||||
|
||||
private mixed $data = [];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion(): string
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getHeaders(): array
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getData(): mixed
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function parse($data): static
|
||||
{
|
||||
$protocol = new Protocol();
|
||||
$protocol->parseHeaders(...explode("\r\n\r\n", $data));
|
||||
return $protocol;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $service
|
||||
* @param string $cmd
|
||||
* @param array $param
|
||||
* @return string
|
||||
*/
|
||||
public static function encode(string $service, string $cmd, array $param = []): string
|
||||
{
|
||||
$proto = 'REQUEST tcp/other.protocol v1.0' . "\r\n";
|
||||
$proto .= ':Source: ' . implode(',', swoole_get_local_ip()) . "\r\n";
|
||||
$proto .= ':Package: ' . $service . "\r\n";
|
||||
$proto .= ':Path: ' . $cmd . "\r\n";
|
||||
$proto .= ':Content-Type: ' . $cmd . "\r\n";
|
||||
$proto .= ':Method: json-rpc' . "\r\n\r\n";
|
||||
|
||||
return $proto . json_encode($param) . "\r\n\r\n";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $body
|
||||
* @return void
|
||||
*/
|
||||
private function parseBody(string $body): void
|
||||
{
|
||||
if ($this->headers['Content-Type'] == RpcService::PROTOCOL_JSON) {
|
||||
$this->data = json_decode($body, true);
|
||||
} else {
|
||||
$this->data = unserialize($body);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $headers
|
||||
* @param string $body
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
private function parseHeaders(string $headers, string $body): void
|
||||
{
|
||||
$explode = explode("\r\n", $headers);
|
||||
$this->headers = [];
|
||||
foreach ($explode as $key => $value) {
|
||||
if ($key == 0) {
|
||||
if (!str_starts_with($value, 'REQUEST tcp/other.protocol')) {
|
||||
throw new Exception('Protocol format error.');
|
||||
}
|
||||
$this->version = str_replace('REQUEST tcp/other.protocol ', '', $value);
|
||||
continue;
|
||||
}
|
||||
[$name, $item] = explode(': ', $value);
|
||||
$this->headers[str_replace(':', '', $name)] = $item;
|
||||
}
|
||||
if (count(array_diff_key(Service::A_DEFAULT, $this->headers)) > 0) {
|
||||
throw new Exception('Protocol format error.');
|
||||
}
|
||||
$this->parseBody($body);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
#[Pure] public function parseUrl(): string
|
||||
{
|
||||
return ':rpc/' . $this->headers['Package'] . '/' . $this->headers['Path'] . '/' . $this->getVersion();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Rpc;
|
||||
|
||||
class Registry
|
||||
{
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -5,16 +5,26 @@ namespace Rpc;
|
||||
|
||||
use Annotation\Inject;
|
||||
use Exception;
|
||||
use HttpServer\Exception\RequestException;
|
||||
use HttpServer\Http\Context;
|
||||
use HttpServer\Http\Request;
|
||||
use HttpServer\Route\Node;
|
||||
use HttpServer\Route\Router;
|
||||
use Server\Constant;
|
||||
use Server\Events\OnAfterRequest;
|
||||
use Kiri\Core\Json;
|
||||
use Kiri\Abstracts\Config;
|
||||
use Kiri\Events\EventDispatch;
|
||||
use Kiri\Events\EventProvider;
|
||||
use Kiri\Exception\NotFindClassException;
|
||||
use Kiri\Kiri;
|
||||
use Swoole\Http\Request;
|
||||
use Server\Constant;
|
||||
use Server\Constrict\Response;
|
||||
use Server\Constrict\ResponseEmitter;
|
||||
use Server\Events\OnAfterRequest;
|
||||
use Server\ExceptionHandlerDispatcher;
|
||||
use Server\ExceptionHandlerInterface;
|
||||
use Server\RequestInterface;
|
||||
use Server\ResponseInterface;
|
||||
use Swoole\Server;
|
||||
use Server\Constrict\Request as cRequest;
|
||||
use function Swoole\Coroutine\defer;
|
||||
|
||||
|
||||
@@ -25,8 +35,13 @@ use function Swoole\Coroutine\defer;
|
||||
class Service extends \Server\Abstracts\Server
|
||||
{
|
||||
|
||||
const RPC_CONNECT = 'RPC::CONNECT';
|
||||
const RPC_CLOSE = 'RPC::CLOSE';
|
||||
const A_DEFAULT = [
|
||||
'Source' => '',
|
||||
'Package' => '',
|
||||
'Path' => '',
|
||||
'Content-Type' => '',
|
||||
'Method' => ''
|
||||
];
|
||||
|
||||
private Router $router;
|
||||
|
||||
@@ -36,17 +51,36 @@ class Service extends \Server\Abstracts\Server
|
||||
public EventProvider $eventProvider;
|
||||
|
||||
|
||||
public Response $response;
|
||||
|
||||
|
||||
/** @var EventDispatch */
|
||||
#[Inject(EventDispatch::class)]
|
||||
public EventDispatch $eventDispatch;
|
||||
|
||||
|
||||
#[Inject(ResponseEmitter::class)]
|
||||
public ResponseEmitter $responseEmitter;
|
||||
|
||||
|
||||
/**
|
||||
* @var ExceptionHandlerInterface
|
||||
*/
|
||||
public ExceptionHandlerInterface $exceptionHandler;
|
||||
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->router = Kiri::getApp('router');
|
||||
|
||||
$exceptionHandler = Config::get('exception.http', ExceptionHandlerDispatcher::class);
|
||||
if (!in_array(ExceptionHandlerInterface::class, class_implements($exceptionHandler))) {
|
||||
$exceptionHandler = ExceptionHandlerDispatcher::class;
|
||||
}
|
||||
$this->exceptionHandler = Kiri::getDi()->get($exceptionHandler);
|
||||
}
|
||||
|
||||
|
||||
@@ -103,15 +137,18 @@ class Service extends \Server\Abstracts\Server
|
||||
{
|
||||
defer(fn() => $this->eventDispatch->dispatch(new OnAfterRequest()));
|
||||
try {
|
||||
$client = $server->getClientInfo($fd, $reID);
|
||||
|
||||
$request = $this->requestSpl((int)$client['server_port'], $data, $fd);
|
||||
|
||||
$result = $this->router->find_path($request)?->dispatch();
|
||||
|
||||
$server->send($fd, $result);
|
||||
$node = $this->router->Branch_search($this->requestSpl($data, $fd));
|
||||
if (!($node instanceof Node)) {
|
||||
throw new RequestException('<h2>HTTP 404 Not Found</h2><hr><i>Powered by Swoole</i>', 404);
|
||||
}
|
||||
$this->response->setClientId($fd, $reID);
|
||||
if (!(($responseData = $node->dispatch()) instanceof ResponseInterface)) {
|
||||
$responseData = $this->response->setContent($responseData)->setStatusCode(200);
|
||||
}
|
||||
} catch (\Throwable $exception) {
|
||||
$server->send($fd, $exception->getMessage());
|
||||
$responseData = $this->exceptionHandler->emit($exception, $this->response);
|
||||
} finally {
|
||||
$this->responseEmitter->sender($server, $responseData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,34 +175,25 @@ class Service extends \Server\Abstracts\Server
|
||||
|
||||
|
||||
/**
|
||||
* @param int $server_port
|
||||
* @param string $data
|
||||
* @param int $fd
|
||||
* @return mixed
|
||||
* @throws NotFindClassException
|
||||
* @throws \ReflectionException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function requestSpl(int $server_port, string $data, int $fd = 0): \HttpServer\Http\Request
|
||||
public function requestSpl(string $data, int $fd = 0): RequestInterface
|
||||
{
|
||||
$data = Protocol::parse($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->fd = $fd;
|
||||
$sRequest->post = $data;
|
||||
$sRequest->header['request_uri'] = 'rpc/p' . $server_port . '/' . ltrim($cmd, '/');
|
||||
$sRequest->header['request_method'] = 'rpc';
|
||||
|
||||
$sRequest->setClientId($fd);
|
||||
$sRequest->setPosts($data->getData());
|
||||
$sRequest->setHeaders(array_merge($data->getHeaders(), [
|
||||
'request_uri' => $data->parseUrl(),
|
||||
'request_method' => $data->getHeaders()['Method']
|
||||
]));
|
||||
Context::setContext(Request::class, $sRequest);
|
||||
|
||||
return \request();
|
||||
return di(cRequest::class);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Rpc;
|
||||
|
||||
use Annotation\Rpc\RpcConsumer;
|
||||
use Kiri\Exception\NotFindClassException;
|
||||
use Rpc\Annotation\RpcService;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
#[RpcConsumer(package: 'default', method: '', timeout: 10, mode: 'json-rpc')]
|
||||
class TestRpcClient
|
||||
{
|
||||
|
||||
public string $package = 'default';
|
||||
|
||||
|
||||
public string $protocol = RpcService::PROTOCOL_JSON;
|
||||
|
||||
|
||||
public int $timeout = 10;
|
||||
|
||||
|
||||
public string $method = '';
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getRegistry(): array
|
||||
{
|
||||
return ['127.0.0.1', 5537];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws NotFindClassException
|
||||
* @throws \ReflectionException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function dispatch(array $param)
|
||||
{
|
||||
[$host, $port] = $this->getRegistry();
|
||||
|
||||
$client = di(Client::class);
|
||||
$client->config = ['host' => $host, 'port' => $port, 'timeout' => $this->timeout];
|
||||
$client->dispatch($this->package, $this->method, $param);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
|
||||
use Server\Constant;
|
||||
|
||||
return [
|
||||
'rpc' => [
|
||||
'name' => 'json-rpc',
|
||||
'type' => Constant::SERVER_TYPE_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' => [
|
||||
Constant::CONNECT => [],
|
||||
Constant::CLOSE => [],
|
||||
],
|
||||
'registry' => [
|
||||
'protocol' => 'consul',
|
||||
'address' => [
|
||||
'host' => '47.14.25.45',
|
||||
'port' => 5527,
|
||||
'path' => ''
|
||||
],
|
||||
],
|
||||
|
||||
'consumers' => [
|
||||
|
||||
]
|
||||
]
|
||||
|
||||
];
|
||||
Reference in New Issue
Block a user