正在显示
1 个修改的文件
包含
0 行增加
和
346 行删除
README.md
已删除
100755 → 0
| 1 | -## 简介 | ||
| 2 | -AukeySwrpc是一个基于swoole开发的高性能rpc包,AukeySwrpc提供了注册发现,链路追踪,中间件等等功能,可以很容易集成到第三方框架,如laravel,yii等等。 | ||
| 3 | - | ||
| 4 | - | ||
| 5 | - | ||
| 6 | -## 功能 | ||
| 7 | - | ||
| 8 | -- 支持多进程模式或协程模式 | ||
| 9 | -- 支持同步,异步调用 | ||
| 10 | -- 支持自定义中间件 | ||
| 11 | -- 支持服务端按类提供对外服务 | ||
| 12 | -- 支持链路追踪 | ||
| 13 | -- 支持注册服务发现 | ||
| 14 | -- 支持客户端负载均衡,包含随机,权重两种模式 | ||
| 15 | -- 支持自定义日志,包协议,序列化方式等等 | ||
| 16 | - | ||
| 17 | - | ||
| 18 | - | ||
| 19 | -## 安装 | ||
| 20 | - | ||
| 21 | -```bash | ||
| 22 | -php composer.phar require wuzhc/swprc ~1.0 -vvv | ||
| 23 | -``` | ||
| 24 | - | ||
| 25 | - | ||
| 26 | - | ||
| 27 | -## 快速体验 | ||
| 28 | - | ||
| 29 | -假设我们User和School两个模块,为了获得用户学校名称,我们需要在User模块发起rpc请求调用School模块的服务。 | ||
| 30 | - | ||
| 31 | -### School模块 | ||
| 32 | - | ||
| 33 | -```php | ||
| 34 | -<?php | ||
| 35 | -use AukeySwrpc\LogicService; | ||
| 36 | -class SchoolService extends LogicService | ||
| 37 | -{ | ||
| 38 | - public function getUserSchool($userID) { | ||
| 39 | - $name = $userID == 123 ? '火星' : '水星'; | ||
| 40 | - return $name.'学校'; | ||
| 41 | - } | ||
| 42 | -} | ||
| 43 | -``` | ||
| 44 | - | ||
| 45 | -School模块将作为rpc服务端,对外提供服务,启动如下: | ||
| 46 | - | ||
| 47 | -```php | ||
| 48 | -<?php | ||
| 49 | -namespace AukeySwrpcTests; | ||
| 50 | -use AukeySwrpc\Server; | ||
| 51 | - | ||
| 52 | -$basePath = dirname(dirname(__FILE__)); | ||
| 53 | -require_once $basePath . "/vendor/autoload.php"; | ||
| 54 | - | ||
| 55 | -$options = [ | ||
| 56 | - 'enable_coroutine' => true, | ||
| 57 | - 'pid_file' => __DIR__ . '/AukeySwrpc.pid', | ||
| 58 | -]; | ||
| 59 | -$server = new Server('School_Module', '127.0.0.1', 9501, 1, $options); | ||
| 60 | -$server->addService(\AukeySwrpcTests\services\SchoolService::class); | ||
| 61 | -$server->start(); | ||
| 62 | -``` | ||
| 63 | - | ||
| 64 | -将SchoolService添加到server,server会自动检索类中所有可用的public方法。 | ||
| 65 | - | ||
| 66 | - | ||
| 67 | - | ||
| 68 | - | ||
| 69 | - | ||
| 70 | -### User模块 | ||
| 71 | - | ||
| 72 | -User模块作为客户端,调用School模块服务如下 | ||
| 73 | - | ||
| 74 | -```php | ||
| 75 | -<?php | ||
| 76 | -namespace AukeySwrpcTests; | ||
| 77 | -use AukeySwrpc\Request; | ||
| 78 | -use AukeySwrpc\LogicService; | ||
| 79 | -use AukeySwrpc\Client; | ||
| 80 | - | ||
| 81 | -class UserService extends LogicService | ||
| 82 | -{ | ||
| 83 | - public function getUserSchoolName() | ||
| 84 | - { | ||
| 85 | - $userID = 123; | ||
| 86 | - $module = 'School_Module'; //请求目标模块名称,需要和服务端定义的一致 | ||
| 87 | - $client = Client::create($module, '127.0.0.1', 9501); | ||
| 88 | - return $client->send(Request::create('\AukeySwrpcTests\services\SchoolService_getUserSchool', [$userID])); | ||
| 89 | - } | ||
| 90 | -} | ||
| 91 | - | ||
| 92 | -//调用 | ||
| 93 | -echo UserService::factory()->getUserSchoolName(); | ||
| 94 | -``` | ||
| 95 | -注意: | ||
| 96 | -- Request.method 为服务类命名 + 下划线 + 方法名,例如上面的`\AukeySwrpcTests\services\SchoolService_getUserSchool`,如果服务类有命名空间,记得一定要带上命名空间 | ||
| 97 | - | ||
| 98 | - | ||
| 99 | - | ||
| 100 | -## 多进程和协程模式 | ||
| 101 | - | ||
| 102 | -多进程或协程模式需要和swoole配置一致,具体参考swoole配置 | ||
| 103 | - | ||
| 104 | -### 多进程模式 | ||
| 105 | - | ||
| 106 | -创建10进程来处理请求 | ||
| 107 | - | ||
| 108 | -```php | ||
| 109 | -$options = [ | ||
| 110 | - 'worker_num' => 10 | ||
| 111 | - 'pid_file' => __DIR__ . '/AukeySwrpc.pid', | ||
| 112 | -]; | ||
| 113 | -$server = new Server('School_Module', '127.0.0.1', 9501, 1, $options); | ||
| 114 | -``` | ||
| 115 | - | ||
| 116 | -### 协程模式 | ||
| 117 | - | ||
| 118 | -目前AukeySwrpc协程模式是运行在单进程的 | ||
| 119 | - | ||
| 120 | -```php | ||
| 121 | -$options = [ | ||
| 122 | - 'enable_coroutine' => true, | ||
| 123 | - 'pid_file' => __DIR__ . '/AukeySwrpc.pid', | ||
| 124 | -]; | ||
| 125 | -$server = new Server('School_Module', '127.0.0.1', 9501, 1, $options); | ||
| 126 | -``` | ||
| 127 | - | ||
| 128 | - | ||
| 129 | - | ||
| 130 | -## 同步调用和异步调用 | ||
| 131 | - | ||
| 132 | -在客户端发起同步调用,客户端会一直等待服务端返回结果 | ||
| 133 | - | ||
| 134 | -```php | ||
| 135 | -$client = \AukeySwrpc\Client::create($module, '127.0.0.1', 9501); | ||
| 136 | -return $client->send(SyncRequest::create('SchoolService_getUserSchool', [$userID])); | ||
| 137 | -``` | ||
| 138 | - | ||
| 139 | -在客户端发起异步调用,客户端会立马得到响应结果,请求将被swoole的task进程处理 | ||
| 140 | - | ||
| 141 | -```php | ||
| 142 | -$client = \AukeySwrpc\Client::create($module, '127.0.0.1', 9501); | ||
| 143 | -return $client->send(AsyncRequest::create('SchoolService_getUserSchool', [$userID])); | ||
| 144 | -``` | ||
| 145 | - | ||
| 146 | - | ||
| 147 | - | ||
| 148 | -## 自定义中间件 | ||
| 149 | - | ||
| 150 | -中间件允许程序可以对请求进行前置操作和后置操作,底层使用了责任链设计模式,所以为了执行下一个中间件,必须返回`$next($request)`,如果想提前返回,则返回结果必须是`AukeySwrpc\Response`类型 | ||
| 151 | - | ||
| 152 | -```php | ||
| 153 | -//中间件除了用匿名函数定义,还可以用实现AukeySwrpc\Middlewares\MiddlewareInterface接口的类 | ||
| 154 | -$middleware = function (\AukeySwrpc\Request $request, Closure $next) { | ||
| 155 | - $start = microtime(true); //前置操作,记录请求开始时间 | ||
| 156 | - $result = $next($request); | ||
| 157 | - echo '耗时:'.(microtime(true) - $start).PHP_EOL; //后置操作,记录请求结束时间,从而计算请求耗时 | ||
| 158 | - return $result; //继续下个中间件的处理 | ||
| 159 | -}; | ||
| 160 | -$server = new Server('School_Module', '127.0.0.1', 9501, 1, $options); | ||
| 161 | -$server->addService(SchoolService::class); | ||
| 162 | -$server->addMiddleware($middleware); //添加中间件 | ||
| 163 | -$server->start(); | ||
| 164 | -``` | ||
| 165 | -如果要提前中止中间件,可以提前在匿名函数或类方法中返回\AukeySwrpc\Response对象,如下 | ||
| 166 | -```php | ||
| 167 | -$middleware = function (\AukeySwrpc\Request $request, Closure $next) { | ||
| 168 | - if (empty($request->getParams())) { | ||
| 169 | - return \AukeySwrpc\Response::error('参数不能为空'); //提前返回,必须是Response类型 | ||
| 170 | - } | ||
| 171 | - return $next($request); | ||
| 172 | -}; | ||
| 173 | -``` | ||
| 174 | - | ||
| 175 | - | ||
| 176 | - | ||
| 177 | -## 服务端按类提供对外服务 | ||
| 178 | - | ||
| 179 | -从上面的例子中,我们把SchoolService整个类添加的server中,这样server就能对外提供SchoolService类所有public方法的功能。 | ||
| 180 | - | ||
| 181 | -```php | ||
| 182 | -$server = new Server('School_Module', '127.0.0.1', 9501, 1, $options); | ||
| 183 | -$server->addService(SchoolService::class); //提供SchoolService所有public方法功能 | ||
| 184 | -$server->addService(AreaService::class); //提供AreaService所有public方法功能 | ||
| 185 | -$server->start(); | ||
| 186 | -``` | ||
| 187 | -客户端使用参考上面的快速体验 | ||
| 188 | - | ||
| 189 | - | ||
| 190 | - | ||
| 191 | -## 注册服务发现 | ||
| 192 | - | ||
| 193 | -如果服务端启动的时候有设置注册中心,则启动成功会自动向注册中心注册服务端地址。目前AukeySwrpc提供了`Consul`作为注册中心,使用如下 | ||
| 194 | - | ||
| 195 | -```php | ||
| 196 | -$server = new Server('School_Module', '127.0.0.1', 9501, 1, $options); | ||
| 197 | -$server->addRegister(new Consul()); | ||
| 198 | -$server->addService(SchoolService::class); | ||
| 199 | -$server->start(); | ||
| 200 | -``` | ||
| 201 | - | ||
| 202 | -如上,使用Consul作为服务的注册中心,通过`http://127.0.0.1:8500`可以查看注册信息,如果想用etcd等其他注册中心,只要实现`AukeySwrpc\Middlewares\RegisterInterface`接口即可,然后在通过`$server->addRegister()`添加到server | ||
| 203 | - | ||
| 204 | - | ||
| 205 | - | ||
| 206 | - | ||
| 207 | - | ||
| 208 | - | ||
| 209 | - | ||
| 210 | - | ||
| 211 | - | ||
| 212 | -## 客户端负载均衡 | ||
| 213 | - | ||
| 214 | -如果服务端启动多个节点,例如School模块启动3个节点,并且注册到了注册中心,那么我们可以从注册中心获取所有服务端节点信息,然后做一些策略处理。 | ||
| 215 | - | ||
| 216 | -```php | ||
| 217 | -$register = new Consul(); | ||
| 218 | -$client = \AukeySwrpc\Client::createBalancer('School_Module', $register, \AukeySwrpc\Client::STRATEGY_WEIGHT); | ||
| 219 | -$result = $client->send(Request::create('SchoolService_getUserSchool', [$userID]); | ||
| 220 | -``` | ||
| 221 | - | ||
| 222 | -目前AukeySwrpc提供两种简单策略模式,`\AukeySwrpc\Client::STRATEGY_WEIGHT权重模式`,`\AukeySwrpc\Client::STRATEGY_RANDOM`随机模式 | ||
| 223 | - | ||
| 224 | - | ||
| 225 | - | ||
| 226 | -## 链路追踪 | ||
| 227 | - | ||
| 228 | -当我们的服务非常多并且需要互相调用时候,如果其中某个调用失败,会导致我们得不到我们想要的结果,而要调试出是哪个环节出了问题也比较麻烦,可能你需要登录每台机器看下有没有错误日志,或者看返回的错误信息是哪个服务提供的。链路追踪记录了整个调用链过程,如果某个环节出错,我们可以快速从调用链得到调用中断地方。 | ||
| 229 | - | ||
| 230 | -```php | ||
| 231 | -class UserService extends LogicService | ||
| 232 | -{ | ||
| 233 | - public function getUserSchoolName() | ||
| 234 | - { | ||
| 235 | - $userID = 123; | ||
| 236 | - $module = 'School_Module'; //请求目标模块名称,需要和服务端定义的一致 | ||
| 237 | - $client = Client::create($module, '127.0.0.1', 9501); | ||
| 238 | - return $client->send(Request::create('SchoolService_getUserSchool', [$userID], $this->getTracerContext(__FUNCTION__))); //getTracerContext()用于提供追踪上下文 | ||
| 239 | - } | ||
| 240 | -} | ||
| 241 | - | ||
| 242 | -$users = UserService::factory() | ||
| 243 | - ->setModule('User_Module') //当前模块,用于调用链的起点 | ||
| 244 | - ->setTracerUrl('http://127.0.0.1:9411/api/v2/spans') //zipkin链路追踪地址 | ||
| 245 | - ->getUserSchoolName(); | ||
| 246 | -``` | ||
| 247 | - | ||
| 248 | - | ||
| 249 | - | ||
| 250 | -如图,User_Module调用Class_Module,Class_Module又去调用School_Module | ||
| 251 | - | ||
| 252 | - | ||
| 253 | - | ||
| 254 | -每个调用还记录响应结果 | ||
| 255 | - | ||
| 256 | - | ||
| 257 | - | ||
| 258 | - | ||
| 259 | -## 自定义日志处理器 | ||
| 260 | -默认使用`Monolog/Logger`作为日志处理器,日志信息会输出到控制台。可根据自己需求覆盖默认处理器,只要日志类 | ||
| 261 | -实现`Psr\Log\LoggerInterface`即可 | ||
| 262 | -```php | ||
| 263 | -use AukeySwrpc\Server; | ||
| 264 | -use Monolog\Handler\StreamHandler; | ||
| 265 | -use Monolog\Logger; | ||
| 266 | - | ||
| 267 | -$logger = new Logger('AukeySwrpc'); | ||
| 268 | -$logger->pushHandler(new StreamHandler(fopen('xxxx.log','w+'), Logger::DEBUG)); | ||
| 269 | - | ||
| 270 | -$server = new Server('127.0.0.1', 9501, ['enable_coroutine'=>true]); | ||
| 271 | -$server->addService(UserService::class); | ||
| 272 | -$server->addLogger($logger); //覆盖默认日志处理器 | ||
| 273 | -$server->start(); | ||
| 274 | -``` | ||
| 275 | - | ||
| 276 | - | ||
| 277 | - | ||
| 278 | -## 序列化方式 | ||
| 279 | - | ||
| 280 | -默认使用固定头+包体来解决**tcp粘包**问题,默认配置为`'package_length_type' => 'N'`,`'package_body_offset' => 4` | ||
| 281 | -默认序列化数据会使用`serialize()`,如果swoole版本在4.5以上的自动使用swoole_substr_unserialize(),可以实现的类来覆盖默认配置,只要实现`src/Packer/PackerInterface`即可,注意服务端和客户端需要使用一样的协议,否则解析不了。 | ||
| 282 | - | ||
| 283 | -```php | ||
| 284 | -use AukeySwrpc\Server; | ||
| 285 | - | ||
| 286 | -$packer = new \AukeySwrpc\Packer\SerializeLengthPacker(); | ||
| 287 | -$server = new Server('127.0.0.1', 9501, ['enable_coroutine'=>true]); | ||
| 288 | -$server->addService(UserService::class); | ||
| 289 | -$server->addPacker($packer); //覆盖默认值 | ||
| 290 | -``` | ||
| 291 | - | ||
| 292 | - | ||
| 293 | - | ||
| 294 | -## 安全证书配置 | ||
| 295 | - | ||
| 296 | -参考:<https://wiki.swoole.com/#/server/setting?id=ssl_cert_file> | ||
| 297 | - | ||
| 298 | -### 服务端 | ||
| 299 | - | ||
| 300 | -```php | ||
| 301 | -$options = [ | ||
| 302 | - 'ssl_cert_file' => __DIR__.'/config/ssl.crt', | ||
| 303 | - 'ssl_key_file' => __DIR__.'/config/ssl.key', | ||
| 304 | - 'pid_file' => __DIR__ . '/AukeySwrpc.pid', | ||
| 305 | -]; | ||
| 306 | -$server = new Server('School_Module', '127.0.0.1', 9501, $options, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); | ||
| 307 | -$server->addService(SchoolService::class); | ||
| 308 | -$server->start(); | ||
| 309 | -``` | ||
| 310 | - | ||
| 311 | -注意: | ||
| 312 | - | ||
| 313 | -- 文件必须为 `PEM` 格式,不支持 `DER` 格式,可使用 `openssl` 工具进行转换 | ||
| 314 | - | ||
| 315 | - | ||
| 316 | - | ||
| 317 | -## 测试 | ||
| 318 | - | ||
| 319 | -使用phpuint实现的简单测试案例,配置文件`phpunit.xml`,根据你的服务器配置ip地址 | ||
| 320 | - | ||
| 321 | -```bash | ||
| 322 | -php phpunit.phar tests --debug | ||
| 323 | -``` | ||
| 324 | - | ||
| 325 | - | ||
| 326 | - | ||
| 327 | -### phpunit 测试报告 | ||
| 328 | - | ||
| 329 | -``` | ||
| 330 | -Client (AukeySwrpcTests\Client) | ||
| 331 | - [x] Client connect | ||
| 332 | - [x] Client sync request | ||
| 333 | - [x] Client async request | ||
| 334 | - | ||
| 335 | -Packer (AukeySwrpcTests\Packer) | ||
| 336 | - [x] Serialize length pack | ||
| 337 | - [x] Serialize lenght unpack | ||
| 338 | - [x] Serialize eof pack | ||
| 339 | - [x] Serialize eof unpack | ||
| 340 | - | ||
| 341 | -Server (AukeySwrpcTests\Server) | ||
| 342 | - [x] Server register to consul | ||
| 343 | - [x] Server unregister from consul | ||
| 344 | - [x] Server add service | ||
| 345 | - [x] Server add middleware | ||
| 346 | -``` |
-
请 注册 或 登录 后发表评论