正在显示
28 个修改的文件
包含
2436 行增加
和
8 行删除
@@ -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 | } |
src/BaseService.php
0 → 100644
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 | +} |
src/Client.php
0 → 100755
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 | +} |
src/ClientManger.php
0 → 100644
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 | +} |
src/Commands/SwrpcGenCom.php
0 → 100644
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 | +} |
src/Commands/SwrpcServerCom.php
0 → 100644
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 | +} |
src/Event.php
0 → 100755
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 | +} |
src/Exceptions/RequestException.php
0 → 100755
src/Exceptions/RpcException.php
0 → 100755
src/Exceptions/TypeException.php
0 → 100755
@@ -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 | } |
src/LogicService.php
0 → 100755
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 | +} |
src/Middlewares/MiddlewareInterface.php
0 → 100755
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 | +} |
src/Middlewares/TraceMiddleware.php
0 → 100755
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 | +} |
src/Packer/PackerInterface.php
0 → 100755
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 | +} |
src/Packer/SerializeEofPacker.php
0 → 100755
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 | +} |
src/Packer/SerializeLengthPacker.php
0 → 100755
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 | +} |
src/Register/Consul.php
0 → 100755
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 | +} |
src/Register/RegisterInterface.php
0 → 100755
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 | +} |
src/Register/Service.php
0 → 100755
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 | +} |
src/Request/AsyncRequest.php
0 → 100755
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 | +} |
src/Request/Request.php
0 → 100755
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 | +} |
src/Request/SyncRequest.php
0 → 100755
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 | +} |
src/Request/SystemRequest.php
0 → 100755
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 | +} |
src/Response.php
0 → 100755
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 | +} |
src/Server.php
0 → 100755
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 | +} |
src/Service.php
0 → 100755
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 | +} |
src/Tracer/TracerContext.php
0 → 100755
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 | +} |
-
请 注册 或 登录 后发表评论