作者 pengjch

dev: swrpc代码提交

@@ -2,15 +2,21 @@ @@ -2,15 +2,21 @@
2 2
3 namespace AukeySwrpc; 3 namespace AukeySwrpc;
4 4
  5 +use AukeySwrpc\Commands\SwrpcGenCom;
  6 +use AukeySwrpc\Commands\SwrcpTestCom;
  7 +use AukeySwrpc\Commands\SwrpcServerCom;
5 use Illuminate\Support\ServiceProvider; 8 use Illuminate\Support\ServiceProvider;
6 9
7 -class SwrpcProvider extends ServiceProvider 10 +class AukeySwrpcProvider extends ServiceProvider
8 { 11 {
9 public function boot() 12 public function boot()
10 { 13 {
11 $this->loadMigrationsFrom(__DIR__ . '/database/migrations'); 14 $this->loadMigrationsFrom(__DIR__ . '/database/migrations');
12 if ($this->app->runningInConsole()) { 15 if ($this->app->runningInConsole()) {
13 $this->commands([ 16 $this->commands([
  17 + SwrpcServerCom::class,
  18 + SwrpcGenCom::class,
  19 + SwrcpTestCom::class,
14 ]); 20 ]);
15 } 21 }
16 } 22 }
  1 +<?php
  2 +
  3 +namespace AukeySwrpc;
  4 +
  5 +
  6 +use AukeySwrpc\Request\SyncRequest;
  7 +use AukeySwrpc\Tracer\TracerContext;
  8 +
  9 +/**
  10 + * Class BaseService
  11 + *
  12 + * @package App\Clients
  13 + * @author pengjch 2024314 11:26:45
  14 + */
  15 +class BaseService
  16 +{
  17 + protected $serviceKey;
  18 + protected $traceContext = null;
  19 +
  20 + /**
  21 + * @return static
  22 + * @author 2021-03-14 11:25:18
  23 + */
  24 + public static function factory()
  25 + {
  26 + return new static();
  27 + }
  28 +
  29 + /**
  30 + * BaseService constructor.
  31 + */
  32 + public function __construct()
  33 + {
  34 + $this->init();
  35 + }
  36 +
  37 + /**
  38 + * @author pengjch 2024314 11:33:22
  39 + */
  40 + protected function init()
  41 + {
  42 + }
  43 +
  44 + /**
  45 + * @param $serviceKey
  46 + * @author pengjch 2024314 11:31:57
  47 + */
  48 + protected function setService($serviceKey)
  49 + {
  50 + $this->serviceKey = $serviceKey;
  51 + }
  52 +
  53 + /**
  54 + * 链路追踪上下文
  55 + *
  56 + * @param TracerContext|null $context
  57 + * @return $this
  58 + * @author pengjch 2024314 11:27:45
  59 + */
  60 + public function trace(?TracerContext $context = null)
  61 + {
  62 + $this->traceContext = $context;
  63 + return $this;
  64 + }
  65 +
  66 + /**
  67 + * 调用远程服务
  68 + *
  69 + * @param $method
  70 + * @param $args
  71 + * @return mixed
  72 + * @throws \Exception
  73 + * @author pengjch 2024-03-14 11:25:18
  74 + */
  75 + protected function callRemoteService($method, $args)
  76 + {
  77 + $serviceNs = $this->getTargetServiceNamespace();
  78 + $method = $serviceNs . '_' . $method;
  79 + $client = ClientManger::getInstance($this->serviceKey);
  80 + $request = SyncRequest::create($method, $args, $this->traceContext);
  81 + return $client->send($request);
  82 + }
  83 +
  84 + /**
  85 + * 调用本地服务
  86 + *
  87 + * @param $method
  88 + * @param $args
  89 + * @return string
  90 + * @author pengjch 2024-03-14 11:25:18
  91 + */
  92 + protected function callLocalService($method, $args)
  93 + {
  94 + $serviceNs = $this->getTargetServiceNamespace();
  95 + return call_user_func_array([$serviceNs::factory(), $method], $args);
  96 + }
  97 +
  98 + /**
  99 + * 获取目标调用类命名空间
  100 + *
  101 + * @return string
  102 + * @author pengjch 2024314 12:6:8
  103 + */
  104 + protected function getTargetServiceNamespace(): string
  105 + {
  106 + $moduleName = $this->extractModuleName();
  107 + /** @var \Nwidart\Modules\Laravel\Module $module */
  108 + $module = \Nwidart\Modules\Facades\Module::find($moduleName);
  109 + $moduleNs = ltrim(str_replace([base_path(), '/'], ['', '\\'], $module->getPath()), '\\');
  110 + return $moduleNs . '\\Services\\' . $this->extractServiceName();
  111 + }
  112 +
  113 + /**
  114 + * 根据调用类提前模块名称
  115 + *
  116 + * @return false|string
  117 + * @author pengjch 2024314 12:0:17
  118 + */
  119 + protected function extractModuleName()
  120 + {
  121 + $calledClass = get_called_class();
  122 + $module = str_replace(['App\Clients\\', '\\'], ['', '_'], $calledClass);
  123 + return substr($module, 0, strrpos($module, '_'));
  124 + }
  125 +
  126 + /**
  127 + * 根据调用类提前service名称
  128 + *
  129 + * @return false|string
  130 + * @author pengjch 2024314 12:0:34
  131 + */
  132 + protected function extractServiceName()
  133 + {
  134 + $calledClass = get_called_class();
  135 + return substr($calledClass, strrpos($calledClass, '\\') + 1);
  136 + }
  137 +
  138 + /**
  139 + * __call
  140 + *
  141 + * @param $method
  142 + * @param $args
  143 + * @return false|mixed
  144 + * @throws \Exception
  145 + * @author 2021-03-14 11:25:18
  146 + */
  147 + public function __call($method, $args)
  148 + {
  149 + switch (env('SERVICE_CALL_STRATEGY', 'remote')) {
  150 + case 'remote':
  151 + return $this->callRemoteService($method, $args);
  152 + case 'local':
  153 + return $this->callLocalService($method, $args);
  154 + default:
  155 + throw new \Exception('error strategy');
  156 + }
  157 + }
  158 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc;
  4 +
  5 +
  6 +use Swoole\Client as SwClient;
  7 +use AukeySwrpc\Exceptions\RpcException;
  8 +use AukeySwrpc\Packer\PackerInterface;
  9 +use AukeySwrpc\Packer\SerializeLengthPacker;
  10 +use AukeySwrpc\Register\RegisterInterface;
  11 +use AukeySwrpc\Register\Service;
  12 +use AukeySwrpc\Request\Request;
  13 +
  14 +/**
  15 + * Class Client
  16 + *
  17 + * @package Swrpc
  18 + * @author pengjch 202439 11:36:25
  19 + */
  20 +class Client
  21 +{
  22 + protected $services = [];
  23 + protected $connects = [];
  24 +
  25 +
  26 + const STRATEGY_RANDOM = 1;
  27 + const STRATEGY_WEIGHT = 2;
  28 +
  29 + protected $mode;
  30 + protected $timeout = 3;
  31 + protected array $options;
  32 + protected string $module;
  33 + protected int $strategy;
  34 + protected ?RegisterInterface $register = null;
  35 + protected ?PackerInterface $packer = null;
  36 +
  37 + protected array $defaultOptions
  38 + = [
  39 + 'open_length_check' => true,
  40 + 'package_length_type' => 'N',
  41 + 'package_length_offset' => 0, //第N个字节是包长度的值
  42 + 'package_body_offset' => 4, //第几个字节开始计算长度
  43 + 'package_max_length' => 81920, //协议最大长度
  44 + ];
  45 +
  46 + /**
  47 + * Client constructor.
  48 + *
  49 + * @param string $module
  50 + * @param array $services
  51 + * @param int $mode
  52 + * @param int $timeout
  53 + * @param array $options
  54 + */
  55 + public function __construct(string $module, array $services, $mode = SWOOLE_SOCK_TCP, $timeout = 3, $options = [])
  56 + {
  57 + $this->module = $module;
  58 + $this->services = $services;
  59 + $this->mode = $mode;
  60 + $this->timeout = $timeout;
  61 + if (empty($options)) {
  62 + $options = $this->defaultOptions;
  63 + }
  64 + $this->options = $options;
  65 +
  66 + }
  67 +
  68 + /**
  69 + * @param string $module
  70 + * @param string $host
  71 + * @param int $port
  72 + * @param int $mode
  73 + * @param array $options
  74 + * @return Client
  75 + * @author pengjch 2024313 18:31:17
  76 + */
  77 + public static function create(
  78 + string $module,
  79 + string $host,
  80 + int $port,
  81 + $mode = SWOOLE_SOCK_TCP,
  82 + $timeout = 3,
  83 + $options = []
  84 + ): Client {
  85 + $service = Service::build($host, $port, 1);
  86 + return new static($module, [$service], $mode, $timeout, $options);
  87 + }
  88 +
  89 + /**
  90 + * @param string $module
  91 + * @param RegisterInterface $register
  92 + * @param int $strategy
  93 + * @param int $mode
  94 + * @param int $timeout
  95 + * @param array $options
  96 + * @return Client
  97 + * @author pengjch 2024313 18:31:22
  98 + */
  99 + public static function createBalancer(
  100 + string $module,
  101 + RegisterInterface $register,
  102 + $strategy = self::STRATEGY_RANDOM,
  103 + $mode = SWOOLE_SOCK_TCP,
  104 + $timeout = 3,
  105 + $options = []
  106 + ): Client {
  107 + $client = new static($module, [], $mode, $timeout, $options);
  108 + $client->strategy = $strategy;
  109 + $client->addRegister($register);
  110 + return $client;
  111 + }
  112 +
  113 + /**
  114 + * @param RegisterInterface $register
  115 + * @return $this
  116 + * @author pengjch 2024313 18:27:20
  117 + */
  118 + public function addRegister(RegisterInterface $register): Client
  119 + {
  120 + $this->register = $register;
  121 + $this->services = $this->register->getServices($this->module);
  122 + return $this;
  123 + }
  124 +
  125 + /**
  126 + * @param PackerInterface $packer
  127 + * @return $this
  128 + * @author pengjch 2024313 18:27:24
  129 + */
  130 + public function addPacker(PackerInterface $packer): Client
  131 + {
  132 + $this->packer = $packer;
  133 + return $this;
  134 + }
  135 +
  136 + /**
  137 + * @return SwClient
  138 + * @throws RpcException
  139 + * @author pengjch 2024313 18:23:37
  140 + */
  141 + public function connect(): SwClient
  142 + {
  143 + $n = count($this->services);
  144 + if ($n == 0) {
  145 + throw new RpcException('No services available');
  146 + }
  147 +
  148 + /** @var Service $service */
  149 + if ($n == 1) { //单个服务节点
  150 + $service = $this->services[0];
  151 + $key = $service->getHost() . '_' . $service->getPort();
  152 + } else { //多个服务节点
  153 + $key = $this->getConnectKey();
  154 + }
  155 +
  156 + if (isset($this->connects[$key]) && $this->connects[$key]->isConnected()) {
  157 + return $this->connects[$key];
  158 + }
  159 + $client = new SwClient($this->mode ?: SWOOLE_SOCK_TCP);
  160 + if (!$client->connect($service->getHost(), $service->getPort(), $this->timeout ?? 3)) {
  161 + throw new RpcException("connect failed. Error: {$client->errCode}");
  162 + }
  163 + $client->set($this->options);
  164 + $this->connects[$key] = $client;
  165 + return $this->connects[$key];
  166 + }
  167 +
  168 + /**
  169 + * 发送请求
  170 + *
  171 + * @param Request $request
  172 + * @return mixed
  173 + * @throws RpcException
  174 + * @author pengjch 202439 13:35:25
  175 + */
  176 + public function send(Request $request)
  177 + {
  178 + /** @var \Swoole\Client $conn */
  179 + $conn = $this->connect();
  180 +
  181 + if (!$this->packer) {
  182 + $this->packer = new SerializeLengthPacker([
  183 + 'package_length_type' => $options['package_length_type'] ?? 'N',
  184 + 'package_body_offset' => $options['package_body_offset'] ?? 4,
  185 + ]);
  186 + }
  187 +
  188 + $request->setModule($this->module);
  189 + $conn->send($this->packer->pack($request));
  190 +
  191 + /** @var Response $response */
  192 + $response = @unserialize($conn->recv());
  193 + if (!($response instanceof Response)) {
  194 + throw new RpcException('The server return type is not a Swrpc\Response');
  195 + }
  196 + if ($response->code == Response::RES_ERROR) {
  197 + throw new RpcException($response->msg);
  198 + }
  199 +
  200 + return $response->data['result'] ?? null;
  201 + }
  202 +
  203 + /**
  204 + * @return string
  205 + * @author pengjch 2024313 18:20:38
  206 + */
  207 + public function getConnectKey(): string
  208 + {
  209 + /** @var Service $service */
  210 + if ($this->strategy == self::STRATEGY_RANDOM) {
  211 + $service = array_rand($this->services);
  212 + return $service->getHost() . '_' . $service->getPort();
  213 + } else {
  214 + /** @var Service $service */
  215 + foreach ($this->services as $service) {
  216 + $totalWeight += $service->getWeight();
  217 + $sort[] = $service->getWeight();
  218 + $serviceArr[] = $service->toArray();
  219 + }
  220 +
  221 + array_multisort($serviceArr, SORT_DESC, $sort);
  222 +
  223 + $start = 0;
  224 + $rand = rand(1, $totalWeight);
  225 + foreach ($serviceArr as $service) {
  226 + if ($start + $service['weight'] >= $rand) {
  227 + return $service['host'] . '_' . $service['port'];
  228 + }
  229 + $start = $start + $service['weight'];
  230 + }
  231 + }
  232 + }
  233 +
  234 + /**
  235 + * 关闭客户端连接
  236 + *
  237 + * @return mixed
  238 + * @author pengjch 2024310 9:16:46
  239 + */
  240 + public function close()
  241 + {
  242 + foreach ($this->connects as $connect) {
  243 + $connect->close(true);
  244 + }
  245 + }
  246 +
  247 + /**
  248 + * 刷新节点服务信息
  249 + * 客户端使用长连接的情况下,需要起一个定时器来定时更新节点服务信息
  250 + *
  251 + * @author pengjch 2024313 18:24:23
  252 + */
  253 + public function refreshServices()
  254 + {
  255 + if ($this->register) {
  256 + $this->services = $this->register->getServices($this->module);
  257 + $this->connects = [];
  258 + }
  259 + }
  260 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc;
  4 +
  5 +use AukeySwrpc\Client;
  6 +use AukeySwrpc\Register\Consul;
  7 +
  8 +/**
  9 + * 客户端管理器
  10 + * Class ClientManger
  11 + *
  12 + * @package App\Clients
  13 + * @author pengjch 202435 23:20:30
  14 + */
  15 +class ClientManger
  16 +{
  17 + /** @var \Hprose\Client */
  18 + private static $clients;
  19 +
  20 + private function __sleep()
  21 + {
  22 + }
  23 +
  24 + private function __wakeup()
  25 + {
  26 + }
  27 +
  28 + private function __construct()
  29 + {
  30 + }
  31 +
  32 + /**
  33 + * @param $key
  34 + * @return Client
  35 + * @throws \Exception
  36 + * @author pengjch 2024314 12:34:8
  37 + */
  38 + public static function getInstance($key): Client
  39 + {
  40 + if (!isset(self::$clients[$key])) {
  41 + $registerUri = env('SWRPC_REGISTER_URI');
  42 + if ($registerUri) { //从注册中心获取连接地址
  43 + self::$clients[$key] = Client::createBalancer($key, new Consul($registerUri));
  44 + } else { //直连模式
  45 + $conf = explode(':', env($key));
  46 + if (!$conf || count($conf) != 2) {
  47 + throw new \Exception('.env未配置' . $key);
  48 + }
  49 + self::$clients[$key] = Client::create($key, $conf[0], $conf[1]);
  50 + }
  51 + }
  52 +
  53 + return self::$clients[$key];
  54 + }
  55 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc\Commands;
  4 +
  5 +
  6 +use Illuminate\Console\Command;
  7 +use Nwidart\Modules\Facades\Module;
  8 +use AukeySwrpc\LogicService;
  9 +
  10 +/**
  11 + * Class SwrpcGenCom
  12 + *
  13 + * @package App\Console\Commands
  14 + * @author pengjch 2024314 12:13:7
  15 + */
  16 +class SwrpcGenCom extends Command
  17 +{
  18 + /**
  19 + * The name and signature of the console command.
  20 + *
  21 + * @var string
  22 + */
  23 + protected $signature = 'swrpc:gen {-m|--module=}';
  24 +
  25 + /**
  26 + * The console command description.
  27 + *
  28 + * @var string
  29 + */
  30 + protected $description = 'swrpc客户端代码生成器';
  31 +
  32 + /**
  33 + * Create a new command instance.
  34 + *
  35 + * @return void
  36 + */
  37 + public function __construct()
  38 + {
  39 + parent::__construct();
  40 + }
  41 +
  42 + /**
  43 + * Execute the console command.
  44 + *
  45 + * @return int
  46 + * @throws \ReflectionException
  47 + */
  48 + public function handle()
  49 + {
  50 +// $serviceKey = env('SWRPC_SERVER_NAME');
  51 +// if (!$serviceKey) {
  52 +// $this->error('SWRPC_SERVER_NAME未设置');
  53 +// return -1;
  54 +// }
  55 +
  56 + $filters = [];
  57 + $specifyModules = $this->option('module') ? explode(',', $this->option('module')) : [];
  58 + $modules = Module::all();
  59 + print_r($modules);exit();
  60 + /** @var \Nwidart\Modules\Laravel\Module $module */
  61 + foreach ($modules as $module) {
  62 + if ($module->getName() == 'Common') {
  63 + continue;
  64 + }
  65 + //如果有指定moudle,则只有指定的moudle会生成,否则会生成全部
  66 + if (count($specifyModules) > 0 && !in_array($module->getName(), $specifyModules)) {
  67 + continue;
  68 + }
  69 +
  70 + $moduleName = $module->getName();
  71 + $modulePath = str_replace('_', '/', $moduleName);
  72 +
  73 + //create module dir
  74 + $basePath = base_path('app/Clients/' . $modulePath);
  75 + if (!is_dir($basePath)) {
  76 + $this->mkdir($basePath);
  77 + }
  78 +
  79 + //target service namespace
  80 + $namespace = ltrim(str_replace([base_path(), '/'], ['', '\\'], $module->getPath()), '\\') . '\\Services';
  81 +
  82 + //fetch file
  83 + $servicePath = $module->getPath() . '/Services';
  84 + if (!is_dir($servicePath)) {
  85 + continue;
  86 + }
  87 + $queues = scandir($servicePath);
  88 + while (count($queues) > 0) {
  89 + $file = array_shift($queues);
  90 + if ($file == '.' || $file == '..' || in_array($file, $filters)) {
  91 + continue;
  92 + }
  93 + //支持嵌套多层service目录
  94 + if (is_dir($servicePath . '/' . $file)) {
  95 + $childrenFiles = scandir($servicePath . '/' . $file);
  96 + foreach ($childrenFiles as $f) {
  97 + if ($f == '.' || $f == '..' || in_array($f, $filters)) {
  98 + continue;
  99 + }
  100 + array_push($queues, $file . '/' . $f);
  101 + }
  102 + continue;
  103 + }
  104 + $class = substr($file, 0, strrpos($file, '.'));
  105 + $funcs = $this->parseFunc($namespace, $class);
  106 + if (empty($funcs)) {
  107 + continue;
  108 + }
  109 +
  110 + //generate proxy file
  111 + $proxyPath = $basePath . '/' . $file;
  112 + ob_start();
  113 + ob_implicit_flush(false);
  114 + include(base_path('app/Clients/client.template.php'));
  115 + $content = ob_get_clean();
  116 + if (!file_put_contents($proxyPath, $content)) {
  117 + $this->error($proxyPath . ' import failure.');
  118 + } else {
  119 + $this->info($proxyPath . ' import success.');
  120 + }
  121 + }
  122 + }
  123 +
  124 + $this->info('完成.');
  125 + return 0;
  126 + }
  127 +
  128 + /**
  129 + * @param $path
  130 + * @return bool
  131 + * @author pengjch 2024314 12:54:6
  132 + */
  133 + protected function mkdir($path)
  134 + {
  135 + if (!is_dir($path)) {
  136 + $this->mkdir(dirname($path));
  137 + if (!mkdir($path, 0777)) {
  138 + return false;
  139 + }
  140 + }
  141 + return true;
  142 + }
  143 +
  144 + /**
  145 + * parseFunc
  146 + *
  147 + * @param $namespace
  148 + * @param $class
  149 + * @return array
  150 + * @throws \ReflectionException
  151 + * @author pengjch 202435 16:3:16
  152 + */
  153 + protected function parseFunc($namespace, $class): array
  154 + {
  155 + $serviceClass = str_replace('/', '\\', $namespace . '\\' . $class);
  156 + if (!class_exists($serviceClass)) {
  157 + $this->error($serviceClass . '类不存在');
  158 + return [];
  159 + }
  160 +
  161 + try {
  162 + $rc = new \ReflectionClass($serviceClass);
  163 + } catch (\ReflectionException $e) {
  164 + $this->error($serviceClass . $e->getMessage());
  165 + return [];
  166 + }
  167 +
  168 + $serviceObj = $rc->newInstance();
  169 + if (!($serviceObj instanceof LogicService)) {
  170 + $this->warn('没有继承\Swrpc\LogicService类,跳过'.get_class($serviceObj));
  171 + return [];
  172 + }
  173 +
  174 + $funcs = [];
  175 + foreach ($rc->getMethods() as $method) {
  176 + if (in_array($method->getName(), [
  177 + 'factory',
  178 + 'initTracer',
  179 + 'setModule',
  180 + 'setTracerUrl',
  181 + 'setParams',
  182 + 'setTracerContext',
  183 + 'getTracerContext'
  184 + ])
  185 + ) {
  186 + continue;
  187 + }
  188 + if (false === $method->isPublic()) {
  189 + continue;
  190 + }
  191 + $line = '';
  192 + if ($returnType = $method->getReturnType()) {
  193 + $line .= $returnType->getName();
  194 + }
  195 + $line .= ' ' . $method->getName() . '(';
  196 + $params = '';
  197 + foreach ($method->getParameters() as $parameter) {
  198 + $params .= '$' . $parameter->getName();
  199 + if ($parameter->isOptional() && $value = $parameter->getDefaultValue()) {
  200 + if (is_string($value)) {
  201 + $params .= '="' . $value . '"';
  202 + } else {
  203 + $params .= '=' . $value;
  204 + }
  205 + }
  206 + $params .= ',';
  207 + }
  208 + $line .= rtrim($params, ',');
  209 + $line .= ")\r\n";
  210 + $funcs[] = $line;
  211 + }
  212 +
  213 + if (count($funcs) == 0) {
  214 + $this->warn('没有可用的方法', ['service' => $serviceObj->getName()]);
  215 + }
  216 +
  217 + return $funcs;
  218 + }
  219 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc\Commands;
  4 +
  5 +
  6 +use AukeySwrpc\ClientManger;
  7 +use Illuminate\Console\Command;
  8 +use Monolog\Handler\StreamHandler;
  9 +use Monolog\Logger;
  10 +use Nwidart\Modules\Facades\Module;
  11 +use AukeySwrpc\Client;
  12 +use AukeySwrpc\LogicService;
  13 +use AukeySwrpc\Register\Consul;
  14 +use AukeySwrpc\Request\SystemRequest;
  15 +use AukeySwrpc\Server;
  16 +
  17 +/**
  18 + * Class SwrpcServerCom
  19 + *
  20 + * @package App\Console\Commands
  21 + * @author pengjch 2024314 12:13:15
  22 + */
  23 +class SwrpcServerCom extends Command
  24 +{
  25 + /**
  26 + * The name and signature of the console command.
  27 + *
  28 + * @var string
  29 + */
  30 + protected $signature = 'swrpc:server {action=start} {--module=}';
  31 +
  32 + /**
  33 + * The console command description.
  34 + *
  35 + * @var string
  36 + */
  37 + protected $description = 'swrpc服务端';
  38 +
  39 + /**
  40 + * Create a new command instance.
  41 + *
  42 + * @return void
  43 + */
  44 + public function __construct()
  45 + {
  46 + parent::__construct();
  47 + }
  48 +
  49 + protected $pidFile = __DIR__ . '/swrpc.pid';
  50 +
  51 + /**
  52 + * Execute the console command.
  53 + *
  54 + * @return int
  55 + */
  56 + public function handle()
  57 + {
  58 + $action = $this->argument('action');
  59 + if ($action == 'start') {
  60 + $this->start();
  61 + } elseif ($action == 'stop') {
  62 + $this->stop();
  63 + } elseif ($action == 'status') {
  64 + $this->status();
  65 + } elseif ($action == 'restart') {
  66 + $this->stop();
  67 + sleep(2);
  68 + $this->start();
  69 + }
  70 + return 0;
  71 + }
  72 +
  73 + protected function status()
  74 + {
  75 + $pid = @file_get_contents($this->pidFile);
  76 + if (!$pid) {
  77 + $this->error('swrpc未启动');
  78 + return;
  79 + }
  80 + $res = posix_kill($pid, 0);
  81 + if (!$res) {
  82 + $this->error('swrpc未启动或已退出');
  83 + return;
  84 + }
  85 +
  86 + $client = ClientManger::getInstance(env('SWRPC_SERVER_NAME'));
  87 + $records = $client->send(SystemRequest::create('stats', []));
  88 + $this->info('----------swrpc服务端状态---------');
  89 + foreach ((array)$records as $key => $value) {
  90 + $this->info($key . ': ' . $value);
  91 + }
  92 + }
  93 +
  94 + protected function stop()
  95 + {
  96 + $pid = @file_get_contents($this->pidFile);
  97 + if (!$pid) {
  98 + $this->error('swrpc未启动');
  99 + return;
  100 + }
  101 +
  102 + $res = posix_kill($pid, SIGTERM);
  103 + $res ? $this->info('swrpc停止成功') : $this->info('swrpc停止失败');
  104 + }
  105 +
  106 + protected function start()
  107 + {
  108 + $pid = @file_get_contents($this->pidFile);
  109 + if ($pid && posix_kill($pid, 0)) {
  110 + $this->error('swrpc已启动,请不要重复启动');
  111 + return -1;
  112 + }
  113 +
  114 + $name = env('SWRPC_SERVER_NAME');
  115 + if (!$name) {
  116 + $this->error('SWRPC_SERVER_NAME未设置');
  117 + return -1;
  118 + }
  119 + $host = env('SWRPC_SERVER_HOST');
  120 + if (!$host) {
  121 + $this->error('SWRPC_SERVER_HOST未设置');
  122 + return -1;
  123 + }
  124 + $port = env('SWRPC_SERVER_PORT');
  125 + if (!$port) {
  126 + $this->error('SWRPC_SERVER_PORT未设置');
  127 + return -1;
  128 + }
  129 +
  130 + $logger = new Logger('swrpc');
  131 + $logger->pushHandler(new StreamHandler(STDOUT, Logger::INFO));
  132 + $server = new Server($name, $host, $port, [
  133 + 'worker_num' => swoole_cpu_num(),
  134 + 'pid_file' => $this->pidFile,
  135 + ], SWOOLE_PROCESS, SWOOLE_SOCK_TCP, $logger);
  136 +
  137 + if ($regiserUri = env('SWRPC_REGISTER_URI')) {
  138 + $server->addRegister(new Consul($regiserUri));
  139 + }
  140 +
  141 + $filters = [];
  142 + $specifyModules = $this->option('module') ? explode(',', $this->option('module')) : [];
  143 + $modules = Module::all();
  144 + /** @var \Nwidart\Modules\Laravel\Module $module */
  145 + foreach ($modules as $module) {
  146 + if ($module->getName() == 'Common') {
  147 + continue;
  148 + }
  149 + //如果有指定moudle,则只有指定的moudle会生成,否则会生成全部
  150 + if (count($specifyModules) > 0 && !in_array($module->getName(), $specifyModules)) {
  151 + continue;
  152 + }
  153 + $namespace = ltrim(str_replace([base_path(), '/'], ['', '\\'], $module->getPath()), '\\') . '\\Services\\';
  154 + $servicePath = $module->getPath() . '/Services';
  155 + if (!is_dir($servicePath)) {
  156 + continue;
  157 + }
  158 + $queues = scandir($servicePath);
  159 + while (count($queues) > 0) {
  160 + $file = array_shift($queues);
  161 + if ($file == '.' || $file == '..' || in_array($file, $filters)) {
  162 + continue;
  163 + }
  164 + //支持嵌套多层service目录
  165 + if (is_dir($servicePath . '/' . $file)) {
  166 + $childrenFiles = scandir($servicePath . '/' . $file);
  167 + foreach ($childrenFiles as $f) {
  168 + if ($f == '.' || $f == '..' || in_array($f, $filters)) {
  169 + continue;
  170 + }
  171 + array_push($queues, $file . '/' . $f);
  172 + }
  173 + continue;
  174 + }
  175 + $class = substr($file, 0, strrpos($file, '.'));
  176 + $serviceObj = $this->build($namespace, $class);
  177 + if (!$serviceObj) {
  178 + continue;
  179 + }
  180 + if (!($serviceObj instanceof LogicService)) {
  181 + $this->warn('没有继承\Swrpc\LogicService类,跳过' . get_class($serviceObj));
  182 + continue;
  183 + }
  184 +
  185 + $server->addService($serviceObj);
  186 + }
  187 + }
  188 +
  189 + $server->start();
  190 + }
  191 +
  192 + /**
  193 + * build
  194 + *
  195 + * @param $namespace
  196 + * @param $class
  197 + * @return object|null
  198 + * @author pengjch 202435 16:51:18
  199 + */
  200 + protected function build($namespace, $class): ?object
  201 + {
  202 + $serviceClass = str_replace('/', '\\', $namespace . $class);
  203 + if (!class_exists($serviceClass)) {
  204 + $this->error($serviceClass . '类不存在');
  205 + return null;
  206 + }
  207 +
  208 + try {
  209 + $rf = new \ReflectionClass($serviceClass);
  210 + } catch (\ReflectionException $e) {
  211 + $this->error($serviceClass . $e->getMessage());
  212 + return null;
  213 + }
  214 +
  215 + try {
  216 + return $rf->newInstance();
  217 + } catch (\ReflectionException $e) {
  218 + $this->error($serviceClass . $e->getMessage());
  219 + return null;
  220 + }
  221 + }
  222 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc;
  4 +
  5 +trait Event
  6 +{
  7 + public function OnWorkerStart(\Swoole\Server $server, int $workerId)
  8 + {
  9 + }
  10 +
  11 + public function onStart(\Swoole\Server $server)
  12 + {
  13 + }
  14 +
  15 + public function onShutdown(\Swoole\Server $server)
  16 + {
  17 +
  18 + }
  19 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc\Exceptions;
  4 +
  5 +
  6 +use Exception;
  7 +
  8 +/**
  9 + * Class RequestException
  10 + *
  11 + * @package AukeySwrpc\Exceptions
  12 + * @author pengjch 202439 11:38:5
  13 + */
  14 +class RequestException extends Exception
  15 +{
  16 +
  17 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc\Exceptions;
  4 +
  5 +
  6 +use Exception;
  7 +
  8 +/**
  9 + * Class RpcException
  10 + *
  11 + * @package AukeySwrpc\Exceptions
  12 + * @author pengjch 202439 11:38:5
  13 + */
  14 +class RpcException extends Exception
  15 +{
  16 +
  17 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc\Exceptions;
  4 +
  5 +
  6 +use Exception;
  7 +
  8 +/**
  9 + * Class TypeException
  10 + *
  11 + * @package AukeySwrpc\Exceptions
  12 + * @author pengjch 202439 11:38:5
  13 + */
  14 +class TypeException extends Exception
  15 +{
  16 +
  17 +}
@@ -19,18 +19,18 @@ class RouteServiceProvider extends ServiceProvider{ @@ -19,18 +19,18 @@ class RouteServiceProvider extends ServiceProvider{
19 * web路由设置 19 * web路由设置
20 */ 20 */
21 protected function mapWebRoutes(){ 21 protected function mapWebRoutes(){
22 - Route::middleware('web')  
23 - ->namespace('Swrpc\Http\Controllers')  
24 - ->group(__DIR__ . '/Controllers/routes.php'); 22 +// Route::middleware('web')
  23 +// ->namespace('AukeySwrpc\Http\Controllers')
  24 +// ->group(__DIR__ . '/Controllers/routes.php');
25 } 25 }
26 26
27 /** 27 /**
28 * api路由设置 28 * api路由设置
29 */ 29 */
30 protected function mapApiRoutes(){ 30 protected function mapApiRoutes(){
31 - Route::prefix('api')  
32 - ->middleware('api')  
33 - ->namespace('Swrpc\Http\Controllers')  
34 - ->group(__DIR__ . '/Controllers/routes.php'); 31 +// Route::prefix('api')
  32 +// ->middleware('api')
  33 +// ->namespace('AukeySwrpc\Http\Controllers')
  34 +// ->group(__DIR__ . '/Controllers/routes.php');
35 } 35 }
36 } 36 }
  1 +<?php
  2 +
  3 +namespace AukeySwrpc;
  4 +
  5 +
  6 +use AukeySwrpc\Tracer\TracerContext;
  7 +use Zipkin\Endpoint;
  8 +use Zipkin\Reporters\Http;
  9 +use Zipkin\Samplers\BinarySampler;
  10 +use Zipkin\TracingBuilder;
  11 +
  12 +/**
  13 + * Class LogicService
  14 + *
  15 + * @package Swrpc
  16 + * @author pengjch 2024311 11:10:18
  17 + */
  18 +class LogicService
  19 +{
  20 + protected $params;
  21 + protected $module;
  22 + protected $tracerUrl;
  23 + protected $tracerContext;
  24 + protected $clients = [];
  25 +
  26 + /**
  27 + * @return static
  28 + * @author pengjch 2024311 11:4:5
  29 + */
  30 + public static function factory()
  31 + {
  32 + return new static();
  33 + }
  34 +
  35 + /**
  36 + * 初始化链路追踪器
  37 + *
  38 + * @param $func
  39 + * @author pengjch 2024311 11:3:36
  40 + */
  41 + public function initTracer($func)
  42 + {
  43 + $reporterUrl = $this->tracerUrl ?: 'http://127.0.0.1:9411/api/v2/spans';
  44 + $endpoint = Endpoint::create($this->module);
  45 + $reporter = new Http(['endpoint_url' => $reporterUrl]);
  46 + $sampler = BinarySampler::createAsAlwaysSample();
  47 + $tracing = TracingBuilder::create()
  48 + ->havingLocalEndpoint($endpoint)
  49 + ->havingSampler($sampler)
  50 + ->havingReporter($reporter)
  51 + ->build();
  52 + $tracer = $tracing->getTracer();
  53 + $span = $tracer->newTrace();
  54 + $span->setName($func);
  55 + $span->start();
  56 + $span->finish();
  57 + $tracer->flush();
  58 +
  59 + $ctx = $span->getContext();
  60 + if ($this->tracerContext) {
  61 + $this->tracerContext->setTraceID($ctx->getTraceId());
  62 + $this->tracerContext->setParentID($ctx->getSpanId());
  63 + $this->tracerContext->setReporterUrl($reporterUrl);
  64 + } else {
  65 + $this->tracerContext = TracerContext::create($ctx->getTraceId(), $ctx->getSpanId(), $reporterUrl);
  66 + }
  67 + }
  68 +
  69 + /**
  70 + * @param $context
  71 + * @return $this
  72 + * @author pengjch 2024311 11:18:43
  73 + */
  74 + public function setTracerContext($context)
  75 + {
  76 + $this->tracerContext = $context;
  77 + return $this;
  78 + }
  79 +
  80 + /**
  81 + * @param $func
  82 + * @return null
  83 + * @author pengjch 2024311 11:4:1
  84 + */
  85 + public function getTracerContext($func)
  86 + {
  87 + if (empty($this->tracerUrl)) {
  88 + return null;
  89 + }
  90 + if (empty($this->tracerContext)) {
  91 + $this->initTracer($func);
  92 + }
  93 + return $this->tracerContext;
  94 + }
  95 +
  96 + /**
  97 + * @param array $params
  98 + * @return $this
  99 + * @author pengjch 2024311 11:15:47
  100 + */
  101 + public function setParams(array $params)
  102 + {
  103 + $this->params = $params;
  104 + return $this;
  105 + }
  106 +
  107 + /**
  108 + * @param string $url
  109 + * @return static $this
  110 + * @author pengjch 2024311 11:15:35
  111 + */
  112 + public function setTracerUrl(string $url)
  113 + {
  114 + $this->tracerUrl = $url;
  115 + return $this;
  116 + }
  117 +
  118 + /**
  119 + * @param string $name
  120 + * @return $this
  121 + * @author pengjch 2024311 11:59:4
  122 + */
  123 + public function setModule(string $name)
  124 + {
  125 + $this->module = $name;
  126 + return $this;
  127 + }
  128 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc\Middlewares;
  4 +
  5 +
  6 +use Closure;
  7 +use AukeySwrpc\Request\Request;
  8 +use AukeySwrpc\Response;
  9 +
  10 +/**
  11 + * Interface MiddlewareInterface
  12 + *
  13 + * @package AukeySwrpc\Middlewares
  14 + * @author pengjch 202439 11:37:39
  15 + */
  16 +interface MiddlewareInterface
  17 +{
  18 + function handle(Request $request, Closure $next): Response;
  19 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc\Middlewares;
  4 +
  5 +
  6 +use Closure;
  7 +use AukeySwrpc\Request\Request;
  8 +use AukeySwrpc\Response;
  9 +use Zipkin\Endpoint;
  10 +use Zipkin\Propagation\TraceContext;
  11 +use Zipkin\Reporters\Http;
  12 +use Zipkin\Samplers\BinarySampler;
  13 +use Zipkin\TracingBuilder;
  14 +
  15 +/**
  16 + * 链路追踪中间件
  17 + * Class TraceMiddleware
  18 + *
  19 + * @package AukeySwrpc\Middlewares
  20 + * @author pengjch 2024310 16:41:6
  21 + */
  22 +class TraceMiddleware implements MiddlewareInterface
  23 +{
  24 + function handle(Request $request, Closure $next): Response
  25 + {
  26 + $context = $request->getTraceContext();
  27 + if (!$context) {
  28 + return $next($request);
  29 + }
  30 +
  31 + $traceContext = TraceContext::create($context->getTraceID(), $context->getParentID(), null, true);
  32 + $endpoint = Endpoint::create($request->getModule());
  33 + $reporter = new Http(['endpoint_url' => $context->getReporterUrl()]);
  34 + $sampler = BinarySampler::createAsAlwaysSample();
  35 + $tracing = TracingBuilder::create()
  36 + ->havingLocalEndpoint($endpoint)
  37 + ->havingSampler($sampler)
  38 + ->havingReporter($reporter)
  39 + ->build();
  40 +
  41 + $tracer = $tracing->getTracer();
  42 + $span = $tracer->newChild($traceContext);
  43 + $span->setName($request->getMethod());
  44 + $span->start();
  45 + $span->tag('请求参数', serialize($request->getParams()));
  46 + $request->setTraceContext($span->getContext()->getTraceId(), $span->getContext()
  47 + ->getSpanId(), $context->getReporterUrl());
  48 +
  49 + $start = microtime(true);
  50 + $result = $next($request);
  51 + $end = microtime(true);
  52 +
  53 + $span->tag('响应状态码code', $result->code);
  54 + $span->tag('响应提示语msg', $result->msg);
  55 + $span->tag('响应耗时', $end - $start);
  56 + $span->finish();
  57 + $tracer->flush();
  58 +
  59 + return $result;
  60 + }
  61 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc\Packer;
  4 +
  5 +
  6 +use AukeySwrpc\Request\Request;
  7 +
  8 +/**
  9 + * Interface PackerInterface
  10 + *
  11 + * @package AukeySwrpc\Packer
  12 + * @author pengjch 202439 11:37:10
  13 + */
  14 +interface PackerInterface
  15 +{
  16 + function pack(Request $data):string;
  17 + function unpack(string $data);
  18 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc\Packer;
  4 +
  5 +
  6 +use AukeySwrpc\Request\Request;
  7 +
  8 +/**
  9 + * Class SerializeEofPacker
  10 + *
  11 + * @package AukeySwrpc\Packer
  12 + * @author pengjch 202439 11:37:17
  13 + */
  14 +class SerializeEofPacker implements PackerInterface
  15 +{
  16 + /**
  17 + * @var string
  18 + */
  19 + protected $eof;
  20 +
  21 + public function __construct(array $options = [])
  22 + {
  23 + $this->eof = $options['settings']['package_eof'] ?? "\r\n";
  24 + }
  25 +
  26 + public function pack(Request $data): string
  27 + {
  28 + return serialize($data);
  29 + }
  30 +
  31 + public function unpack(string $data)
  32 + {
  33 + return unserialize($data);
  34 + }
  35 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc\Packer;
  4 +
  5 +
  6 +use AukeySwrpc\Request\Request;
  7 +
  8 +/**
  9 + * Class SerializeLengthPacker
  10 + *
  11 + * @package AukeySwrpc\Packer
  12 + * @author pengjch 202439 11:37:27
  13 + */
  14 +class SerializeLengthPacker implements PackerInterface
  15 +{
  16 + /**
  17 + * @var string
  18 + */
  19 + protected $type;
  20 +
  21 + /**
  22 + * @var int
  23 + */
  24 + protected $length;
  25 +
  26 + protected $defaultOptions
  27 + = [
  28 + 'package_length_type' => 'N',
  29 + 'package_body_offset' => 4,
  30 + ];
  31 +
  32 + public function __construct(array $options = [])
  33 + {
  34 + $options = array_merge($this->defaultOptions, $options['settings'] ?? []);
  35 +
  36 + $this->type = $options['package_length_type'];
  37 + $this->length = $options['package_body_offset'];
  38 + }
  39 +
  40 + public function pack(Request $data): string
  41 + {
  42 + $data = serialize($data);
  43 + return pack($this->type, strlen($data)) . $data;
  44 + }
  45 +
  46 + public function unpack(string $data)
  47 + {
  48 + $package = unpack('N', $data);
  49 + $len = $package[1];
  50 +
  51 + //合并unserialize和substr,以减少内存拷贝 https://wenda.swoole.com/detail/107587
  52 + if (function_exists('swoole_substr_unserialize')) {
  53 + return swoole_substr_unserialize($data, $this->length, $len);
  54 + }
  55 +
  56 + $data = substr($data, $this->length, $len);
  57 + return unserialize($data);
  58 + }
  59 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc\Register;
  4 +
  5 +
  6 +use SensioLabs\Consul\ServiceFactory;
  7 +use SensioLabs\Consul\Services\Agent;
  8 +use SensioLabs\Consul\Services\AgentInterface as AgentInterfaceAlias;
  9 +use SensioLabs\Consul\Services\Catalog;
  10 +use SensioLabs\Consul\Services\CatalogInterface;
  11 +use SensioLabs\Consul\Services\Health;
  12 +use AukeySwrpc\Exceptions\RpcException;
  13 +
  14 +class Consul implements RegisterInterface
  15 +{
  16 + protected $sf;
  17 + protected array $options;
  18 + protected array $serviceCache
  19 + = [
  20 + 'ttl' => 10,
  21 + 'services' => [],
  22 + 'lastUpdateTime' => 0,
  23 + ];
  24 +
  25 + public function __construct($uri = 'http://127.0.0.1:8500', $options = [])
  26 + {
  27 + $this->options = $options;
  28 + $this->sf = new ServiceFactory([
  29 + 'base_uri' => $uri
  30 + ]);
  31 + }
  32 +
  33 + public function getName(): string
  34 + {
  35 + return 'Consul';
  36 + }
  37 +
  38 + /**
  39 + * 注册节点
  40 + *
  41 + * @param string $module
  42 + * @param string $host
  43 + * @param $port
  44 + * @param int $weight
  45 + * @author pengjch 202439 23:17:5
  46 + */
  47 + public function register($module, $host, $port, $weight = 1)
  48 + {
  49 + $id = $host . '_' . $port;
  50 + /** @var Agent $agent */
  51 + $agent = $this->sf->get(AgentInterfaceAlias::class);
  52 + $agent->registerService([
  53 + 'ID' => $id,
  54 + 'Name' => $module,
  55 + 'Port' => $port,
  56 + 'Address' => $host,
  57 + 'Tags' => [
  58 + 'port_' . $port,
  59 + ],
  60 + 'Weights' => [
  61 + 'Passing' => $weight,
  62 + 'Warning' => 1,
  63 + ],
  64 + 'Check' => [
  65 + 'TCP' => $host . ':' . $port,
  66 + 'Interval' => $this->options['interval'] ?? '10s',
  67 + 'Timeout' => $this->options['timeout'] ?? '5s',
  68 + 'DeregisterCriticalServiceAfter' => $this->options['deregisterCriticalServiceAfter'] ?? '30s',
  69 + ],
  70 + ]);
  71 + }
  72 +
  73 + /**
  74 + * 注销节点
  75 + * http://127.0.0.1:8500/v1/agent/service/deregister/service_id
  76 + *
  77 + * @param $host
  78 + * @param $port
  79 + * @author pengjch 202439 23:16:51
  80 + */
  81 + public function unRegister($host, $port)
  82 + {
  83 + $id = $host . '_' . $port;
  84 + /** @var Agent $agent */
  85 + $agent = $this->sf->get(AgentInterfaceAlias::class);
  86 + $agent->deregisterService($id);
  87 + }
  88 +
  89 + /**
  90 + * 获取模块下所有的服务
  91 + *
  92 + * @param string $module
  93 + * @return array
  94 + * @author pengjch 2024310 9:44:16
  95 + */
  96 + public function getServices(string $module): array
  97 + {
  98 + $cache = $this->serviceCache;
  99 + $ttl = $this->options['ttl'] ?? $cache['ttl'];
  100 +
  101 + //本地缓存所有节点信息,避免每次请求都要从consul拉一遍数据
  102 + if ($cache['lastUpdateTime'] + $ttl < time()) {
  103 + $health = new Health();
  104 + $servers = $health->service($module)->json();
  105 + if (empty($servers)) {
  106 + return [];
  107 + }
  108 + $result = [];
  109 + foreach ($servers as $server) {
  110 + $result[] = Service::build($server['Service']['Address'], $server['Service']['Port'], $server['Service']['Weights']['Passing']);
  111 + }
  112 + $cache['service'] = $result;
  113 + $cache['lastUpdateTime'] = time();
  114 + }
  115 +
  116 + return $cache['service'];
  117 + }
  118 +
  119 + /**
  120 + * 随机获取一个服务
  121 + *
  122 + * @param string $module
  123 + * @return Service
  124 + * @author pengjch 2024310 9:44:27
  125 + */
  126 + public function getRandomService(string $module): Service
  127 + {
  128 + $services = $this->getServices($module);
  129 + if (!$services) {
  130 + throw new RpcException('It has not register module');
  131 + }
  132 +
  133 + return $services[rand(0, count($services) - 1)];
  134 + }
  135 +
  136 + /**
  137 + * 获取权重服务
  138 + *
  139 + * @param string $module
  140 + * @return Service
  141 + * @author pengjch 2024310 9:44:38
  142 + */
  143 + public function getWeightService(string $module): Service
  144 + {
  145 + $serviceArr = [];
  146 + $totalWeight = 0;
  147 + $services = $this->getServices($module);
  148 + if (!$services) {
  149 + throw new RpcException('It has not register module');
  150 + }
  151 +
  152 + /** @var Service $service */
  153 + foreach ($services as $service) {
  154 + $totalWeight += $service->getWeight();
  155 + $sort[] = $service->getWeight();
  156 + $serviceArr[] = $service->toArray();
  157 + }
  158 +
  159 + array_multisort($serviceArr, SORT_DESC, $sort);
  160 +
  161 + $start = 0;
  162 + $rand = rand(1, $totalWeight);
  163 + foreach ($serviceArr as $service) {
  164 + if ($start + $service['weight'] >= $rand) {
  165 + return Service::build($service['host'], $service['port'], $service['weight']);
  166 + }
  167 + $start = $start + $service['weight'];
  168 + }
  169 + }
  170 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc\Register;
  4 +
  5 +
  6 +/**
  7 + * Interface RegisterInterface
  8 + *
  9 + * @package AukeySwrpc\Register
  10 + * @author pengjch 202439 16:23:35
  11 + */
  12 +interface RegisterInterface
  13 +{
  14 + function getName(): string;
  15 +
  16 + function register($module, $host, $port, $weight = 1);
  17 +
  18 + function unRegister($host, $port);
  19 +
  20 + function getServices(string $module): array;
  21 +
  22 + function getRandomService(string $module): Service;
  23 +
  24 + function getWeightService(string $module): Service;
  25 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc\Register;
  4 +
  5 +
  6 +/**
  7 + * 注册中心服务
  8 + * Class Service
  9 + *
  10 + * @package AukeySwrpc\Register
  11 + * @author pengjch 2024311 10:25:46
  12 + */
  13 +class Service
  14 +{
  15 + protected $host;
  16 + protected $port;
  17 + protected $weight;
  18 +
  19 + public function __construct($host, $port, $weight)
  20 + {
  21 + $this->host = $host;
  22 + $this->port = $port;
  23 + $this->weight = $weight;
  24 + }
  25 +
  26 + public static function build($host, $port, $weight)
  27 + {
  28 + return new static($host, $port, $weight);
  29 + }
  30 +
  31 + public function getHost()
  32 + {
  33 + return $this->host;
  34 + }
  35 +
  36 + public function getPort()
  37 + {
  38 + return $this->port;
  39 + }
  40 +
  41 + public function getWeight()
  42 + {
  43 + return $this->weight;
  44 + }
  45 +
  46 + public function toArray(): array
  47 + {
  48 + return [
  49 + 'host' => $this->host,
  50 + 'port' => $this->port,
  51 + 'weight' => $this->weight
  52 + ];
  53 + }
  54 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc\Request;
  4 +
  5 +
  6 +/**
  7 + * Class AsyncRequest
  8 + *
  9 + * @package AukeySwrpc\Request
  10 + * @author pengjch 2024313 9:10:2
  11 + */
  12 +class AsyncRequest extends Request
  13 +{
  14 + public function init()
  15 + {
  16 + $this->setSync(false);
  17 + $this->setSystem(false);
  18 + }
  19 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc\Request;
  4 +
  5 +
  6 +use AukeySwrpc\Tracer\TracerContext;
  7 +
  8 +abstract class Request
  9 +{
  10 + protected string $module;
  11 + protected string $method;
  12 + protected array $params;
  13 + protected bool $isSync = true; //是否同步请求,默认是
  14 + protected bool $isSystem = false; //是否系统请求,默认否
  15 + protected $error;
  16 + protected ?TracerContext $traceContext;
  17 +
  18 + public static function create($method, $params, ?TracerContext $traceContext = null)
  19 + {
  20 + return new static ($method, $params, $traceContext);
  21 + }
  22 +
  23 + public function __construct($method, $params, ?TracerContext $traceContext = null)
  24 + {
  25 + $this->method = $method;
  26 + $this->params = $params;
  27 + $this->traceContext = $traceContext;
  28 + $this->init();
  29 + }
  30 +
  31 + abstract public function init();
  32 +
  33 + public function getModule(): string
  34 + {
  35 + return $this->module;
  36 + }
  37 +
  38 + public function getMethod(): string
  39 + {
  40 + return $this->method;
  41 + }
  42 +
  43 + public function getParams(): array
  44 + {
  45 + return $this->params;
  46 + }
  47 +
  48 + public function setParams(array $params)
  49 + {
  50 + $this->params = $params;
  51 + }
  52 +
  53 + public function setModule(string $name)
  54 + {
  55 + $this->module = $name;
  56 + }
  57 +
  58 + public function mergeParams(array $params)
  59 + {
  60 + $this->params = array_merge($this->params, $params);
  61 + }
  62 +
  63 + public function getTraceContext(): ?TracerContext
  64 + {
  65 + return $this->traceContext;
  66 + }
  67 +
  68 + public function setTraceContext($traceID, $parentID, $url)
  69 + {
  70 + $this->traceContext = TracerContext::create($traceID, $parentID, $url);
  71 + }
  72 +
  73 + public function setSync(bool $value)
  74 + {
  75 + $this->isSync = $value;
  76 + }
  77 +
  78 + public function isSync(): bool
  79 + {
  80 + return $this->isSync;
  81 + }
  82 +
  83 + public function setSystem(bool $value)
  84 + {
  85 + $this->isSystem = $value;
  86 + }
  87 +
  88 + public function isSystem(): bool
  89 + {
  90 + return $this->isSystem;
  91 + }
  92 +
  93 + public function getError()
  94 + {
  95 + return $this->error;
  96 + }
  97 +
  98 + public function setError($err)
  99 + {
  100 + $this->error = $err;
  101 + }
  102 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc\Request;
  4 +
  5 +/**
  6 + * Class SyncRequest
  7 + *
  8 + * @package AukeySwrpc\Request
  9 + * @author pengjch 2024313 9:9:54
  10 + */
  11 +class SyncRequest extends Request
  12 +{
  13 + public function init()
  14 + {
  15 + $this->setSync(true);
  16 + $this->setSystem(false);
  17 + }
  18 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc\Request;
  4 +
  5 +
  6 +/**
  7 + * Class AsyncRequest
  8 + *
  9 + * @package AukeySwrpc\Request
  10 + * @author pengjch 2024313 9:10:2
  11 + */
  12 +class SystemRequest extends Request
  13 +{
  14 + public function init()
  15 + {
  16 + $this->setSync(true);
  17 + $this->setSystem(true);
  18 + }
  19 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc;
  4 +
  5 +
  6 +/**
  7 + * Class Response
  8 + *
  9 + * @package Swrpc
  10 + * @author pengjch 202439 11:36:9
  11 + */
  12 +class Response
  13 +{
  14 + const RES_ERROR = 0;
  15 + const RES_SUCCESS = 1;
  16 +
  17 + public string $msg;
  18 + public int $code;
  19 + public array $data;
  20 +
  21 + public function __construct($code, $msg, $data)
  22 + {
  23 + $this->data = $data;
  24 + $this->code = $code;
  25 + $this->msg = $msg;
  26 + }
  27 +
  28 + public static function error($msg, $code = self::RES_ERROR, $data = []): Response
  29 + {
  30 + return new static($code, $msg, $data);
  31 + }
  32 +
  33 + public static function success($data = [], $msg = 'success', $code = self::RES_SUCCESS): Response
  34 + {
  35 + return new static($code, $msg, $data);
  36 + }
  37 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc;
  4 +
  5 +
  6 +use Monolog\Handler\StreamHandler;
  7 +use Monolog\Logger;
  8 +use Psr\Log\LoggerInterface;
  9 +use AukeySwrpc\Middlewares\TraceMiddleware;
  10 +use AukeySwrpc\Packer\SerializeLengthPacker;
  11 +use AukeySwrpc\Register\RegisterInterface;
  12 +use AukeySwrpc\Middlewares\MiddlewareInterface;
  13 +use AukeySwrpc\Packer\PackerInterface;
  14 +use AukeySwrpc\Request\Request;
  15 +
  16 +/**
  17 + * Class Server
  18 + *
  19 + * @package Swrpc
  20 + * @author pengjch 202439 11:35:52
  21 + */
  22 +class Server
  23 +{
  24 + use Event;
  25 +
  26 + protected string $module;
  27 + protected string $host;
  28 + protected int $port;
  29 + protected int $weight = 1;
  30 + protected array $options;
  31 + protected array $defaultOptions
  32 + = [
  33 + 'open_length_check' => true,
  34 + 'package_length_type' => 'N',
  35 + 'package_length_offset' => 0, //第N个字节是包长度的值
  36 + 'package_body_offset' => 4, //第几个字节开始计算长度
  37 + 'package_max_length' => 81920, //协议最大长度
  38 + ];
  39 +
  40 + /** @var PackerInterface $packer */
  41 + protected $packer;
  42 +
  43 + /** @var Service $service */
  44 + protected $service;
  45 +
  46 + /** @var LoggerInterface $logger */
  47 + protected $logger;
  48 +
  49 + /** @var RegisterInterface $register */
  50 + protected $register;
  51 +
  52 + /** @var \Swoole\Server $server */
  53 + protected \Swoole\Server $server;
  54 +
  55 + private array $middlewares;
  56 +
  57 + public function __construct(
  58 + string $module,
  59 + string $host,
  60 + int $port,
  61 + array $options = [],
  62 + $mode = SWOOLE_PROCESS,
  63 + $socketType = SWOOLE_SOCK_TCP,
  64 + LoggerInterface $logger = null
  65 + ) {
  66 + $this->module = $module;
  67 + $this->host = $host;
  68 + $this->port = $port;
  69 +
  70 + $this->setDefaultOptions($options);
  71 + $this->setDefaultLogger($logger);
  72 + $this->setCoreMiddleware();
  73 +
  74 + $this->service = new Service($this->logger);
  75 +
  76 + $server = new \Swoole\Server($host, $port, $mode ?: SWOOLE_PROCESS, $socketType ?: SWOOLE_SOCK_TCP);
  77 + $server->set($this->options);
  78 + $server->on('Start', [$this, 'onStart']);
  79 + $server->on('Shutdown', [$this, 'onShutdown']);
  80 + $server->on('WorkerStart', [$this, 'onWorkerStart']);
  81 + $server->on('Connect', [$this, 'OnConnect']);
  82 + $server->on('Receive', [$this, 'OnReceive']);
  83 + $server->on('Close', [$this, 'OnClose']);
  84 + $server->on('Task', [$this, 'OnTask']);
  85 + $server->on('Finish', [$this, 'OnFinish']);
  86 + $this->server = $server;
  87 + }
  88 +
  89 + /**
  90 + * 设置节点权重
  91 + *
  92 + * @param int $weight
  93 + * @return Server
  94 + * @author pengjch 2024313 10:55:39
  95 + */
  96 + public function weight(int $weight): Server
  97 + {
  98 + $this->weight = $weight;
  99 + return $this;
  100 + }
  101 +
  102 + /**
  103 + * 设置默认选项
  104 + *
  105 + * @param $options
  106 + * @author pengjch 2024311 10:35:3
  107 + */
  108 + protected function setDefaultOptions($options)
  109 + {
  110 + if (empty($options)) {
  111 + $options = $this->defaultOptions;
  112 + }
  113 +
  114 + $this->options = $options;
  115 +
  116 + //请求数量超过10000重启
  117 + if (empty($this->options['max_request'])) {
  118 + $this->options['max_request'] = 10000;
  119 + }
  120 + //默认task数量
  121 + if (empty($this->options['task_worker_num'])) {
  122 + $this->options['task_worker_num'] = swoole_cpu_num() * 2;
  123 + }
  124 + //task请求数超过10000则重启
  125 + if (empty($this->options['task_max_request'])) {
  126 + $this->options['task_max_request'] = 10000;
  127 + }
  128 + //10s没有数据传输就进行检测
  129 + if (empty($this->options['tcp_keepidle'])) {
  130 + $this->options['tcp_keepidle'] = 10;
  131 + }
  132 + //3s探测一次
  133 + if (empty($this->options['tcp_keepinterval'])) {
  134 + $this->options['tcp_keepinterval'] = 3;
  135 + }
  136 + //探测的次数,超过5次后还没回包close此连接
  137 + if (empty($this->options['tcp_keepcount'])) {
  138 + $this->options['tcp_keepcount'] = 5;
  139 + }
  140 + }
  141 +
  142 + /**
  143 + * 设置默认日志处理器
  144 + *
  145 + * @param LoggerInterface|null $logger
  146 + * @author pengjch 2024311 10:34:19
  147 + */
  148 + protected function setDefaultLogger(LoggerInterface $logger = null)
  149 + {
  150 + if (empty($logger)) {
  151 + $logger = new Logger('swrpc');
  152 + $logger->pushHandler(new StreamHandler(STDOUT, Logger::DEBUG));
  153 + }
  154 + $this->logger = $logger;
  155 + }
  156 +
  157 + /**
  158 + * 设置核心中间件
  159 + *
  160 + * @author pengjch 2024311 10:34:5
  161 + */
  162 + protected function setCoreMiddleware()
  163 + {
  164 + $this->middlewares[] = TraceMiddleware::class;
  165 + }
  166 +
  167 + /**
  168 + * 添加中间件,支持匿名函数和实现类
  169 + * addMiddleware
  170 + *
  171 + * @param mixed ...$middlewares
  172 + * @author pengjch 202439 11:35:11
  173 + */
  174 + public function addMiddleware(...$middlewares)
  175 + {
  176 + foreach ($middlewares as $middleware) {
  177 + if (is_string($middleware) && class_exists($middleware)) {
  178 + $middleware = new $middleware();
  179 + }
  180 + if (!($middleware instanceof \Closure) && !($middleware instanceof MiddlewareInterface)) {
  181 + $this->logger->warning('Skip illegal Middleware.');
  182 + continue;
  183 + }
  184 + $this->middlewares[] = $middleware;
  185 + }
  186 + }
  187 +
  188 + /**
  189 + * 添加服务
  190 + * addService
  191 + *
  192 + * @param $service
  193 + * @param string $prefix
  194 + * @return Server
  195 + * @author pengjch 202439 11:35:2
  196 + */
  197 + public function addService($service, $prefix = ''): Server
  198 + {
  199 + $this->service->addInstance($service, $prefix);
  200 + return $this;
  201 + }
  202 +
  203 + /**
  204 + * @param $key
  205 + * @return mixed|null
  206 + * @author pengjch 2024312 16:11:12
  207 + */
  208 + public function getService($key)
  209 + {
  210 + return $this->service->getService($key);
  211 + }
  212 +
  213 + /**
  214 + * 注册发现中心
  215 + *
  216 + * @param $register
  217 + * @return Server
  218 + * @author pengjch 202439 16:38:51
  219 + */
  220 + public function addRegister($register): Server
  221 + {
  222 + $this->register = $register;
  223 + return $this;
  224 + }
  225 +
  226 + /**
  227 + * 添加日志处理器
  228 + *
  229 + * @param $logger
  230 + * @author pengjch 202439 12:20:57
  231 + */
  232 + public function addLogger($logger)
  233 + {
  234 + $this->logger = $logger;
  235 + }
  236 +
  237 + /**
  238 + * 添加包解析器
  239 + *
  240 + * @param $packer
  241 + * @author pengjch 202439 12:45:53
  242 + */
  243 + public function addPacker($packer)
  244 + {
  245 + $this->packer = $packer;
  246 + }
  247 +
  248 + /**
  249 + * 注册服务到consul
  250 + * onWorkerStart 和 onStart 回调是在不同进程中并行执行的,不存在先后顺序
  251 + *
  252 + * @param \Swoole\Server $server
  253 + * @author pengjch 202439 23:11:10
  254 + */
  255 + public function onStart(\Swoole\Server $server)
  256 + {
  257 + if ($this->register) {
  258 + $this->logger->info(sprintf('Register server[%s:%d] to %s.', $this->host, $this->port, $this->register->getName()));
  259 + $this->register->register($this->module, $this->host, $this->port, $this->weight);
  260 + }
  261 + }
  262 +
  263 + /**
  264 + * 注销服务
  265 + * 强制 kill 进程不会回调 onShutdown
  266 + * 需要使用 kill -15 来发送 SIGTERM 信号到主进程才能按照正常的流程终止
  267 + *
  268 + * @param \Swoole\Server $server
  269 + * @author pengjch 202439 23:14:40
  270 + */
  271 + public function onShutdown(\Swoole\Server $server)
  272 + {
  273 + if ($this->register) {
  274 + $this->logger->info(sprintf('UnRegister server[%s:%d] from register.', $this->host, $this->port));
  275 + $this->register->unRegister($this->host, $this->port);
  276 + }
  277 + }
  278 +
  279 + /**
  280 + * server接收请求
  281 + *
  282 + * @param \Swoole\Server $server
  283 + * @param $fd
  284 + * @param $reactor_id
  285 + * @param $data
  286 + * @return mixed
  287 + * @author pengjch 202439 11:34:0
  288 + */
  289 + public function onReceive(\Swoole\Server $server, $fd, $reactor_id, $data)
  290 + {
  291 + /** @var Request $request */
  292 + $request = $this->packer->unpack($data);
  293 + //系统请求
  294 + if ($request->isSystem()) {
  295 + return $server->send($fd, serialize($this->doSystemRequest($request)));
  296 + }
  297 + //同步请求
  298 + if ($request->isSync()) {
  299 + return $server->send($fd, serialize($this->doRequest($request)));
  300 + }
  301 + //异步请求
  302 + $server->task($request);
  303 + return $server->send($fd, serialize(Response::success(['result' => 'success'])));
  304 + }
  305 +
  306 + /**
  307 + * 执行请求
  308 + *
  309 + * @param Request $request
  310 + * @return Response
  311 + * @author pengjch 2024313 9:37:20
  312 + */
  313 + public function doRequest(Request $request): Response
  314 + {
  315 + try {
  316 + $handler = $this->getRequestHandler();
  317 + } catch (\ReflectionException $e) {
  318 + return Response::error($e->getMessage());
  319 + }
  320 +
  321 + $response = $handler($request);
  322 + if (!($response instanceof Response)) {
  323 + $msg = 'The middleware must return the response type';
  324 + $this->logger->error($msg);
  325 + $response = Response::error($msg);
  326 + }
  327 +
  328 + return $response;
  329 + }
  330 +
  331 + /**
  332 + * 系统请求
  333 + *
  334 + * @param Request $request
  335 + * @return Response
  336 + * @author pengjch 2024323 10:46:55
  337 + */
  338 + public function doSystemRequest(Request $request): Response
  339 + {
  340 + if ($request->getMethod() == 'stats') {
  341 + return Response::success(['result' => $this->server->stats()]);
  342 + } else {
  343 + return Response::error($request->getMethod() . ' is not supported');
  344 + }
  345 + }
  346 +
  347 + /**
  348 + * @return mixed
  349 + * @throws \ReflectionException
  350 + * @author pengjch 2024312 16:36:52
  351 + */
  352 + public function getRequestHandler()
  353 + {
  354 + return array_reduce(array_reverse($this->middlewares), function ($stack, $next) {
  355 + return function ($request) use ($stack, $next) {
  356 + if ($next instanceof \Closure) {
  357 + return $next($request, $stack);
  358 + } elseif (is_string($next) && class_exists($next)) {
  359 + return (new $next())->handle($request, $stack);
  360 + } else {
  361 + return $next->handle($request, $stack);
  362 + }
  363 + };
  364 + }, function ($request) {
  365 + return $this->service->call($request);
  366 + });
  367 + }
  368 +
  369 + /**
  370 + * 异步处理请求
  371 + *
  372 + * @param $server
  373 + * @param $taskID
  374 + * @param $reactorID
  375 + * @param $data
  376 + * @return Response
  377 + * @author pengjch 2024313 9:40:37
  378 + */
  379 + public function OnTask($server, $taskID, $reactorID, $data): Response
  380 + {
  381 + $this->logger->debug('AsyncTask: Start', ['taskID' => $taskID]);
  382 + return $this->doRequest($data);
  383 + }
  384 +
  385 + /**
  386 + * 完成异步任务回调
  387 + *
  388 + * @param $server
  389 + * @param $taskID
  390 + * @param $data
  391 + * @author pengjch 2024313 9:49:44
  392 + */
  393 + public function OnFinish($server, $taskID, $data)
  394 + {
  395 + $this->logger->debug('AsyncTask: Finish', ['taskID' => $taskID, 'data' => $data]);
  396 + }
  397 +
  398 + /**
  399 + * OnClose
  400 + *
  401 + * @param $server
  402 + * @param $fd
  403 + * @author pengjch 202439 11:34:48
  404 + */
  405 + public function OnClose($server, $fd)
  406 + {
  407 + $this->logger->debug('Client: Close');
  408 + }
  409 +
  410 + /**
  411 + * OnConnect
  412 + *
  413 + * @param $server
  414 + * @param $fd
  415 + * @author pengjch 202439 11:34:52
  416 + */
  417 + public function OnConnect($server, $fd)
  418 + {
  419 + $this->logger->debug('Client: Connect.');
  420 + }
  421 +
  422 + /**
  423 + * start
  424 + *
  425 + * @author pengjch 202439 11:34:56
  426 + */
  427 + public function start(): bool
  428 + {
  429 + //可用服务数量
  430 + if ($this->service->count() == 0) {
  431 + $this->logger->error('There is no service available.');
  432 + return false;
  433 + }
  434 + //默认使用固定包头+包体方式解决粘包问题
  435 + if (empty($this->packer)) {
  436 + $this->packer = new SerializeLengthPacker([
  437 + 'package_length_type' => $this->options['package_length_type'] ?? 'N',
  438 + 'package_body_offset' => $this->options['package_body_offset'] ?? 4,
  439 + ]);
  440 + }
  441 +
  442 + $this->logger->info(sprintf('Rpc server[%s:%s] start.', $this->host, $this->port));
  443 + $this->server->start();
  444 + return true;
  445 + }
  446 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc;
  4 +
  5 +
  6 +use Psr\Log\LoggerInterface;
  7 +use ReflectionClass;
  8 +use ReflectionMethod;
  9 +use AukeySwrpc\Request\Request;
  10 +
  11 +/**
  12 + * Class Service
  13 + *
  14 + * @package Swrpc
  15 + * @author pengjch 202439 11:39:41
  16 + */
  17 +class Service
  18 +{
  19 + private array $services = [];
  20 + protected array $filers
  21 + = [
  22 + 'factory',
  23 + 'initTracer',
  24 + 'setModule',
  25 + 'setTracerUrl',
  26 + 'setParams',
  27 + 'setTracerContext',
  28 + 'getTracerContext'
  29 + ];
  30 +
  31 + /** @var LoggerInterface $logger */
  32 + private $logger;
  33 +
  34 + public function __construct($logger)
  35 + {
  36 + $this->logger = $logger;
  37 + }
  38 +
  39 + /**
  40 + * 注册服务实例
  41 + *
  42 + * @param $obj
  43 + * @param $prefix
  44 + * @return bool
  45 + * @author pengjch 202438 13:43:21
  46 + */
  47 + public function addInstance($obj, $prefix = ''): bool
  48 + {
  49 + if (is_string($obj)) {
  50 + $obj = new $obj();
  51 + }
  52 + if (!is_object($obj)) {
  53 + $this->logger->error('Service is not an object.', ['service' => $obj]);
  54 + return false;
  55 + }
  56 + if (!($obj instanceof LogicService)) {
  57 + $this->logger->error('The Service does not inherit LogicService', ['service' => get_class($obj)]);
  58 + return false;
  59 + }
  60 + $className = get_class($obj);
  61 + $methods = get_class_methods($obj);
  62 + foreach ($methods as $method) {
  63 + if (in_array($method, $this->filers)) {
  64 + continue;
  65 + }
  66 + if (strlen($prefix) > 0) {
  67 + $key = $prefix . '_' . $className . '_' . $method;
  68 + } else {
  69 + $key = $className . '_' . $method;
  70 + }
  71 + $this->services[$key] = $className;
  72 + $this->logger->info(sprintf('import %s => %s.', $key, $className));
  73 + }
  74 +
  75 + return true;
  76 + }
  77 +
  78 + /**
  79 + * 获取服务
  80 + *
  81 + * @param $key
  82 + * @return mixed|null
  83 + * @author pengjch 202438 13:43:17
  84 + */
  85 + public function getService($key)
  86 + {
  87 + return $this->services[$key] ?? null;
  88 + }
  89 +
  90 + /**
  91 + * 获取所有服务
  92 + * getServices
  93 + *
  94 + * @return array
  95 + * @author pengjch 202438 15:23:58
  96 + */
  97 + public function getServices(): array
  98 + {
  99 + return $this->services;
  100 + }
  101 +
  102 + /**
  103 + * count
  104 + *
  105 + * @return int
  106 + * @author pengjch 202439 12:56:46
  107 + */
  108 + public function count(): int
  109 + {
  110 + return count($this->services);
  111 + }
  112 +
  113 + /**
  114 + * @param $key
  115 + * @return bool
  116 + * @author pengjch 202438 14:32:50
  117 + */
  118 + public function isExist($key): bool
  119 + {
  120 + return isset($this->services[$key]);
  121 + }
  122 +
  123 + /**
  124 + * 调用服务
  125 + *
  126 + * @param Request $request
  127 + * @return Response
  128 + * @throws \ReflectionException
  129 + * @author pengjch 202439 10:17:59
  130 + */
  131 + public function call(Request $request): Response
  132 + {
  133 + if ($err = $request->getError()) {
  134 + return Response::error($err);
  135 + }
  136 +
  137 + $service = $this->getService($request->getMethod());
  138 + if (!$service) {
  139 + $this->logger->debug('service is not exist.', ['method' => $request->getMethod()]);
  140 + return Response::error('service is not exist.');
  141 + }
  142 +
  143 + $methodArr = explode('_', $request->getMethod());
  144 + $methodName = array_pop($methodArr);
  145 + $reflect = new ReflectionClass($service);
  146 + $instance = $reflect->newInstanceArgs();
  147 + if (!method_exists($instance, $methodName)) {
  148 + $this->logger->debug('method is not exist.', ['method' => $request->getMethod()]);
  149 + return Response::error(sprintf('%s method[%s] is not exist.', $service, $methodName));
  150 + }
  151 +
  152 + $ctx = $request->getTraceContext();
  153 + if ($ctx && method_exists($instance, 'setTracerContext')) {
  154 + $instance->setTracerUrl($ctx->getReporterUrl())->setTracerContext($ctx);
  155 + }
  156 +
  157 + try {
  158 + $methodObj = new ReflectionMethod($reflect->getName(), $methodName);
  159 + $result = $methodObj->invokeArgs($instance, $request->getParams());
  160 + } catch (\Throwable $e) {
  161 + return Response::error($e->getMessage());
  162 + }
  163 +
  164 + return Response::success([
  165 + 'result' => $result
  166 + ]);
  167 + }
  168 +}
  1 +<?php
  2 +
  3 +namespace AukeySwrpc\Tracer;
  4 +
  5 +
  6 +/**
  7 + * 链路追踪上下文
  8 + * Class TracerContext
  9 + *
  10 + * @package AukeySwrpc\Tracer
  11 + * @author pengjch 2024311 10:21:34
  12 + */
  13 +class TracerContext
  14 +{
  15 + protected $traceID;
  16 + protected $parentID;
  17 + protected $reporterUrl;
  18 +
  19 + public function __construct($traceID, $parentID, $reporterUrl)
  20 + {
  21 + $this->traceID = $traceID;
  22 + $this->parentID = $parentID;
  23 + $this->reporterUrl = $reporterUrl;
  24 + }
  25 +
  26 + public static function create($traceID, $parentID, $reporterUrl)
  27 + {
  28 + return new static($traceID, $parentID, $reporterUrl);
  29 + }
  30 +
  31 + public function setTraceID($traceID)
  32 + {
  33 + $this->traceID = $traceID;
  34 + }
  35 +
  36 + public function setParentID($parentID)
  37 + {
  38 + $this->parentID = $parentID;
  39 + }
  40 +
  41 + public function setReporterUrl($url)
  42 + {
  43 + $this->reporterUrl = $url;
  44 + }
  45 +
  46 + public function getTraceID()
  47 + {
  48 + return $this->traceID;
  49 + }
  50 +
  51 + public function getParentID()
  52 + {
  53 + return $this->parentID;
  54 + }
  55 +
  56 + public function getReporterUrl()
  57 + {
  58 + return $this->reporterUrl;
  59 + }
  60 +}