简介
swrpc是一个基于swoole开发的高性能rpc包,swrpc提供了注册发现,链路追踪,中间件等等性能,能够很容易集成到第三方框架,如laravel,yii等等。
https://github.com/wuzhc/swrpc
性能
- 反对多过程模式或协程模式
- 反对同步,异步调用
- 反对自定义中间件
- 反对服务端按类提供对外服务
- 反对链路追踪
- 反对注册服务发现
- 反对客户端负载平衡,蕴含随机,权重两种模式
- 反对自定义日志,包协定,序列化形式等等
装置
<code class="bash">php composer.phar require "wuzhc/swprc:~1.0.1" -vvv
疾速体验
假如咱们User和School两个模块,为了取得用户学校名称,咱们须要在User模块发动rpc申请调用School模块的服务。
School模块
<code class="php"><?php use Swrpc\LogicService; class SchoolService extends LogicService { public function getUserSchool($userID) { $name = $userID == 123 ? '火星' : '水星'; return $name.'学校'; } }
School模块将作为rpc服务端,对外提供服务,启动如下:
<code class="php"><?php use Swrpc\Server; $basePath = dirname(dirname(__FILE__)); require_once $basePath . "/vendor/autoload.php"; $options = [ 'enable_coroutine' => true, 'pid_file' => __DIR__ . '/swrpc.pid', ]; $server = new Server('School_Module', '127.0.0.1', 9501, 1, $options); $server->addService(SchoolService::class); $server->start();
将SchoolService增加到server,server会自动检索类中所有可用的public办法。
User模块
User模块作为客户端,调用School模块服务如下
<code class="php"><?php use Swrpc\Request; use Swrpc\LogicService; use Swrpc\Client; class UserService extends LogicService { public function getUserSchoolName() { $userID = 123; $module = 'School_Module'; //申请指标模块名称,须要和服务端定义的统一 $client = Client::create($module, '127.0.0.1', 9501); return $client->send(Request::create('SchoolService_getUserSchool', [$userID])); } } //调用 echo UserService::factory()->getUserSchoolName();
多过程和协程模式
多过程或协程模式须要和swoole配置统一,具体参考swoole配置
多过程模式
创立10过程来解决申请
<code class="php">$options = [ 'worker_num' => 10 'pid_file' => __DIR__ . '/swrpc.pid', ]; $server = new Server('School_Module', '127.0.0.1', 9501, 1, $options);
协程模式
目前swrpc协程模式是运行在单过程的
<code class="php">$options = [ 'enable_coroutine' => true, 'pid_file' => __DIR__ . '/swrpc.pid', ]; $server = new Server('School_Module', '127.0.0.1', 9501, 1, $options);
同步调用和异步调用
在客户端发动同步调用,客户端会始终期待服务端返回后果
<code class="php">$client = \Swrpc\Client::create($module, '127.0.0.1', 9501); return $client->send(SyncRequest::create('SchoolService_getUserSchool', [$userID]));
在客户端发动异步调用,客户端会立马失去响应后果,申请将被swoole的task过程解决
<code class="php">$client = \Swrpc\Client::create($module, '127.0.0.1', 9501); return $client->send(AsyncRequest::create('SchoolService_getUserSchool', [$userID]));
自定义中间件
中间件容许程序能够对申请进行前置操作和后置操作,底层应用了责任链设计模式,所以为了执行下一个中间件,必须返回$next($request)
,如果想提前返回,则返回后果必须是Swrpc\Response
类型
<code class="php">//中间件除了用匿名函数定义,还能够用实现Swrpc\Middlewares\MiddlewareInterface接口的类 $middleware = function (\Swrpc\Request $request, Closure $next) { $start = microtime(true); //前置操作,记录申请开始工夫 $result = $next($request); echo '耗时:'.(microtime(true) - $start).PHP_EOL; //后置操作,记录申请完结工夫,从而计算申请耗时 return $result; //持续下个中间件的解决 }; $server = new Server('School_Module', '127.0.0.1', 9501, 1, $options); $server->addService(SchoolService::class); $server->addMiddleware($middleware); //增加中间件 $server->start();
如果要提前停止中间件,能够提前在匿名函数或类办法中返回\Swrpc\Response对象,如下
<code class="php">$middleware = function (\Swrpc\Request $request, Closure $next) { if (empty($request->getParams())) { return \Swrpc\Response::error('参数不能为空'); //提前返回,必须是Response类型 } return $next($request); };
服务端按类提供对外服务
从下面的例子中,咱们把SchoolService整个类增加的server中,这样server就能对外提供SchoolService类所有public办法的性能。
<code class="php">$server = new Server('School_Module', '127.0.0.1', 9501, 1, $options); $server->addService(SchoolService::class); //提供SchoolService所有public办法性能 $server->addService(AreaService::class); //提供AreaService所有public办法性能 $server->start();
注册服务发现
如果服务端启动的时候有设置注册核心,则启动胜利会主动向注册核心注册服务端地址。
<code class="php">$server = new Server('School_Module', '127.0.0.1', 9501, 1, $options); $server->addRegister(new Consul(['weights' => ['Passing' => 10, 'Warning' => 1]])); $server->addService(SchoolService::class); $server->start();
如上,应用Consul作为服务的注册核心,通过http://127.0.0.1:8500能够查看注册信息,如果想用etcd等其余注册核心,只有实现Swrpc\Middlewares\RegisterInterface
接口即可,而后在通过$server->addRegister()
增加到server
客户端负载平衡
如果服务端启动多个节点,例如School模块启动3个节点,并且注册到了注册核心,那么咱们能够从注册核心获取所有服务端节点信息,而后做一些策略解决。
<code class="php">$register = new Consul(); $client = \Swrpc\Client::createBalancer('School_Module', $register, \Swrpc\Client::STRATEGY_WEIGHT); $result = $client->send(Request::create('SchoolService_getUserSchool', [$userID]);
目前swrpc提供两种简略策略模式,\Swrpc\Client::STRATEGY_WEIGHT权重模式
,\Swrpc\Client::STRATEGY_RANDOM
随机模式
链路追踪
当咱们的服务十分多并且须要相互调用时候,如果其中某个调用失败,会导致咱们得不到咱们想要的后果,而要调试出是哪个环节出了问题也比拟麻烦,可能你须要登录每台机器看下有没有谬误日志,或者看返回的错误信息是哪个服务提供的。链路追踪记录了整个调用链过程,如果某个环节出错,咱们能够疾速从调用链失去调用中断中央。
<code class="php">class UserService extends LogicService { public function getUserSchoolName() { $userID = 123; $module = 'School_Module'; //申请指标模块名称,须要和服务端定义的统一 $client = Client::create($module, '127.0.0.1', 9501); return $client->send(Request::create('SchoolService_getUserSchool', [$userID], $this->getTracerContext(__FUNCTION__))); //getTracerContext()用于提供追踪上下文 } } $users = UserService::factory() ->setModule('User_Module') //以后模块,用于调用链的终点 ->setTracerUrl('http://127.0.0.1:9411/api/v2/spans') //zipkin链路追踪地址 ->getUserSchoolName();
如图,User_Module调用Class_Module,Class_Module又去调用School_Module
每个调用还记录响应后果
自定义日志处理器
默认应用Monolog/Logger
作为日志处理器,日志信息会输入到控制台。可依据本人需要笼罩默认处理器,只有日志类
实现Psr\Log\LoggerInterface
即可
<code class="php">use Swrpc\Server; use Monolog\Handler\StreamHandler; use Monolog\Logger; $logger = new Logger('swrpc'); $logger->pushHandler(new StreamHandler(fopen('xxxx.log','w+'), Logger::DEBUG)); $server = new Server('127.0.0.1', 9501, ['enable_coroutine'=>true]); $server->addService(UserService::class); $server->addLogger($logger); //笼罩默认日志处理器 $server->start();
序列化形式
默认应用固定头+包体来解决tcp粘包问题,默认配置为'package_length_type' => 'N'
,'package_body_offset' => 4
默认序列化数据会应用serialize()
,如果swoole版本在4.5以上的主动应用swoole_substr_unserialize(),能够实现的类来笼罩默认配置,只有实现src/Packer/PackerInterface
即可,留神服务端和客户端须要应用一样的协定,否则解析不了。
<code class="php">use Swrpc\Server; $packer = new \Swrpc\Packer\SerializeLengthPacker(); $server = new Server('127.0.0.1', 9501, ['enable_coroutine'=>true]); $server->addService(UserService::class); $server->addPacker($packer); //笼罩默认值
平安证书配置
参考:https://wiki.swoole.com/#/ser…
服务端
<code class="php">$options = [ 'ssl_cert_file' => __DIR__.'/config/ssl.crt', 'ssl_key_file' => __DIR__.'/config/ssl.key', 'pid_file' => __DIR__ . '/swrpc.pid', ]; $server = new Server('School_Module', '127.0.0.1', 9501, $options, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); $server->addService(SchoolService::class); $server->start();
留神:
HTTPS
利用浏览器必须信赖证书能力浏览网页;wss
利用中,发动WebSocket
连贯的页面必须应用HTTPS
;- 浏览器不信赖
SSL
证书将无奈应用wss
; - 文件必须为
PEM
格局,不反对DER
格局,可应用openssl
工具进行转换
测试
应用phpuint实现的简略测试案例,配置文件phpunit.xml
<code class="bash">php phpunit.phar tests --debug
phpunit 测试报告
Client (SwrpcTests\Client) [x] Client connect [x] Client sync request [x] Client async request Packer (SwrpcTests\Packer) [x] Serialize length pack [x] Serialize lenght unpack [x] Serialize eof pack [x] Serialize eof unpack Server (SwrpcTests\Server) [x] Server register to consul [x] Server unregister from consul [x] Server add service [x] Server add middleware