介绍swrpc是基于swoole开发的高性能rpc包。swrpc提供了注册发现、链接跟踪、中间件等功能,可以方便的集成到第三方框架中。比如laravel、yii等。https://github.com/wuzhc/swrpc功能支持多进程模式或协程模式支持同步和异步调用支持自定义中间件支持服务端按类对外提供服务支持链接跟踪支持注册服务发现支持客户端负载均衡,包括随机和权重模式支持自定义日志、包协议、序列化方式等安装phpcomposer.pharrequire"wuzhc/swprc:~1.0.1"-vvv快速体验假设我们有两个模块User和School,为了获取用户所在学校名称,我们需要在User模块发起rpc请求,调用School模块的服务。学校模块true,'pid_file'=>__DIR__。'/swrpc.pid',];$server=newServer('School_Module','127.0.0.1',9501,1,$options);$server->addService(SchoolService::class);$服务器->开始();将SchoolService添加到服务器,服务器将自动检索该类中所有可用的公共方法。User模块User模块作为客户端,调用School模块服务如下$module='School_Module';//请求目标模块名,需要和服务器定义的保持一致$client=Client::create($module,'127.0.0.1',9501);返回$client->send(Request::create('SchoolService_getUserSchool',[$userID]));}}//调用echoUserService::factory()->getUserSchoolName();多进程和协程模式多进程或协程模式需要和swoole配置保持一致,详见swoole配置多进程模式创建10个进程来处理请求$options=['worker_num'=>10'pid_file'=>__DIR__。'/swrpc.pid',];$server=newServer('School_Module','127.0.0.1',9501,1,$options);协程模式当前的swrpc协程模式是在单进程中运行$options=['enable_coroutine'=>true,'pid_file'=>__DIR__。'/swrpc.pid',];$server=newServer('School_Module','127.0.0.1',9501,1,$options);同步调用和异步调用在客户端发起同步调用,客户端会等待服务器返回结果$client=\Swrpc\Client::create($模块,'127.0.0.1',9501);返回$client->send(SyncRequest::create('SchoolService_getUserSchool',[$userID]));在客户端发起异步调用,客户端会立即得到响应,请求会被swoole的任务进程处理$client=\Swrpc\Client::create($module,'127.0.0.1',9501);return$client->send(AsyncRequest::create('SchoolService_getUserSchool',[$userID]));自定义中间件中间件允许程序对请求进行前操作和后操作。底层采用责任链设计模式,所以为了执行下一个中间件,必须返回$next($request)。如果想早点Return,返回结果必须是Swrpc\Response类型//中间件也可以定义匿名函数,或者实现Swrpc\Middlewares\MiddlewareInterface接口的类$middleware=function(\Swrpc\Request$request,关闭$next){$start=microtime(true);//预操作,记录请求开始时间$result=$next($request);echo'time-consuming:'.(microtime(true)-$start).PHP_EOL;//操作后,记录请求结束时间,从而计算请求耗时return$result;//继续处理下一个中间件};$server=newServer('School_Module','127.0.0.1',9501,1,$options);$server->addService(SchoolService::class);$server->addMiddleware($middleware);//添加中间件$server->start();如果想提前终止中间件,可以在匿名函数中使用或者在类方法中返回\Swrpc\Response对象,如下$middleware=function(\Swrpc\Request$request,Closure$next){if(empty($request->getParams())){return\Swrpc\Response::error('参数不能为空');//advance返回必须是Response类型}return$next($request);};服务端按类对外提供服务从上面的例子我们把整个SchoolService类添加到服务端,这样服务端就可以对外提供SchoolService类的所有公共服务方法$server=newServer('School_Module','127.0.0.1',9501,1,$options);$server->addService(SchoolService::class);//提供SchoolService的所有公共方法函数$server->addService(AreaService::class);//提供AreaService的所有公共方法函数$server->start();注册服务发现如果服务器启动时设置了注册中心,则启动成功后会自动向注册中心注册服务器地址。$server=newServer('School_Module','127.0.0.1',9501,1,$options);$server->addRegister(newConsul(['weights'=>['Passing'=>10,'警告'=>1]]));$server->addService(SchoolService::class);$服务器->开始();如上,使用Consul作为服务的注册中心,可以通过http://127.0.0.1:8500查看注册信息,如果想使用etcd等其他注册中心,只需要实现Swrpc\Middlewares\RegisterInterface接口,然后通过$server->addRegister()添加到服务器客户端负载均衡中如果服务器启动了多个节点,比如School模块启动了3个节点并注册到注册中心,那么我们就可以得到所有服务器节点信息从注册中心,然后做一些策略处理。$register=newConsul();$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随机modelinktracking当我们有很多服务,需要互相调用的时候有时候,如果其中一个调用失败,我们就得不到我们想要的结果,调试是哪个链接错了会比较麻烦,可能需要打日志in到每台机器上看有没有错误日志,或者看返回的错误信息是哪个服务提供的。链路跟踪记录了整个调用链的过程,如果某个环节出现问题,我们可以快速从调用链中获取调用中断点.类UserService扩展LogicService{publicfunctiongetUserSchoolName(){$userID=123;$module='School_Module';//请求目标模块名,需要和服务器定义的保持一致$client=Client::create($module,'127.0.0.1',9501);返回$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,就可以使用Swrpc\Server;useMonolog\Handler\StreamHandler;useMonolog\Logger;$logger=newLogger('swrpc');$logger->pushHandler(newStreamHandler(fopen('xxxx.log','w+'),Logger::DEBUG));$server=newServer('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即可。注意服务端和客户端需要使用相同的协议,否则无法解析。使用Swrpc\Server;$packer=new\Swrpc\Packer\SerializeLengthPacker();$server=newServer('127.0.0.1',9501,['enable_coroutine'=>true]);$server->addService(UserService::班级);$server->addPacker($packer);//覆盖默认的安全证书配置参考:https://wiki.swoole.com/#/ser...server$options=['ssl_cert_file'=>__DIR__.'/config/ssl.crt','ssl_key_file'=>__DIR__.'/config/ssl.key','pid_file'=>__DIR__。'/swrpc.pid',];$server=newServer('School_Module','127.0.0.1',9501,$options,SWOOLE_PROCESS,SWOOLE_SOCK_TCP|SWOOLE_SSL);$server->addService(SchoolService::class);$服务器->开始();注意:HTTPS应用浏览器必须信任在wss应用中,发起WebSocket连接的页面必须使用HTTPS;不信任SSL证书的浏览器将无法使用wss;文件必须是PEM格式,不支持DER格式,可以使用openssl工具进行转换测试,使用phpuint实现配置文件简单测试用例phpunit.xmlphpphpunit.phartests--debugphpunittestreportClient(SwrpcTests\Client)[x]Clientconnect[x]Clientsyncrequest[x]ClientasyncrequestPacker(SwrpcTests\Packer)[x]Serializelengthpack[x]Serializelenghtunpack[x]Serializeeofpack[x]SerializeeofunpackServer(SwrpcTests\Server)[x]Serverregistertoconsul[x]Serverunregisterfromconsul[x]Serveraddservice[x]服务器添加中间件
