hyperf 从零开始构建微服务(二)——构建服务消费者
阅读目录
我们说过,服务提供者可以提供各种服务,它可以和数据库进行交互;服务消费者是纯消费的服务,只需要远程访问服务提供者即可。
下面我们按步骤构建消费者模块。
源码已上传至github,https://github.com/bailangzhan/hyperf-rpc
1、构建服务消费者
除了对时区进行设置,其他的组件暂时都不安装,选择“n”即可。
composer create-project hyperf/hyperf-skeleton shop_consumer_user Creating a "hyperf/hyperf-skeleton" project at "./shop_consumer_user" Installing hyperf/hyperf-skeleton (v2.2.1) - Installing hyperf/hyperf-skeleton (v2.2.1): Extracting archive Created project in /data/web/test/hyperf-rpc/shop_consumer_user > @php -r "file_exists('.env') || copy('.env.example', '.env');" > Installer\Script::install Setting up optional packages Setup data and cache dir Removing installer development dependencies What time zone do you want to setup ? [n] Default time zone for php.ini Make your selection or type a time zone name, like Asia/Shanghai (n): Asia/Shanghai Do you want to use Database (MySQL Client) ? [y] yes [n] None of the above Make your selection or type a composer package name and version (yes): n Do you want to use Redis Client ? [y] yes [n] None of the above Make your selection or type a composer package name and version (yes): n Which RPC protocol do you want to use ? [1] JSON RPC with Service Governance [2] JSON RPC [3] gRPC [n] None of the above Make your selection or type a composer package name and version (n): n Which config center do you want to use ? [1] Apollo [2] Aliyun ACM [3] ETCD [4] Nacos [n] None of the above Make your selection or type a composer package name and version (n): n Do you want to use hyperf/constants component ? [y] yes [n] None of the above Make your selection (n): n Do you want to use hyperf/async-queue component ? (A simple redis queue component) [y] yes [n] None of the above Make your selection or type a composer package name and version (n): n Do you want to use hyperf/amqp component ? [y] yes [n] None of the above Make your selection or type a composer package name and version (n): n Do you want to use hyperf/model-cache component ? [y] yes [n] None of the above Make your selection or type a composer package name and version (n): n Do you want to use hyperf/elasticsearch component ? [y] yes [n] None of the above Make your selection or type a composer package name and version (n): n Do you want to use hyperf/tracer component ? (An open tracing protocol component, adapte with Zipkin etc.) [y] yes [n] None of the above Make your selection or type a composer package name and version (n): n
2、安装json rpc依赖
cd shop_consumer_user composer require hyperf/json-rpc
3、安装 JSON RPC 客户端
shop_consumer_user 不需要对外提供服务,所以我们只安装客户端,不需要安装hyperf/rpc-server组件
composer require hyperf/rpc-client
4、server配置
server的配置这里用默认的就好了,9501端口提供http服务,不需要改动
'servers' => [ [ 'name' => 'http', 'type' => Server::SERVER_HTTP, 'host' => '0.0.0.0', 'port' => 9501, 'sock_type' => SWOOLE_SOCK_TCP, 'callbacks' => [ Event::ON_REQUEST => [Hyperf\HttpServer\Server::class, 'onRequest'], ], ], ],
5、编写业务代码
5-1、编写服务消费者类
app下新建JsonRpc目录,编写UserService.php和UserServiceInterface.php文件
【UserServiceInterface.php】 <?php namespace App\JsonRpc; interface UserServiceInterface { public function createUser(string $name, int $gender); public function getUserInfo(int $id); } 【UserService.php】 <?php namespace App\JsonRpc; use Hyperf\RpcClient\AbstractServiceClient; class UserService extends AbstractServiceClient implements UserServiceInterface { /** * 定义对应服务提供者的服务名称 * @var string */ protected $serviceName = 'UserService'; /** * 定义对应服务提供者的服务协议 * @var string */ protected $protocol = 'jsonrpc-http'; /** * @param string $name * @param int $gender * @return mixed */ public function createUser(string $name, int $gender) { return $this->__request(__FUNCTION__, compact('name', 'gender')); } /** * @param int $id * @return mixed */ public function getUserInfo(int $id) { return $this->__request(__FUNCTION__, compact('id')); } }
hyperf 官方的hyperf/rpc-client组件已经帮我们实现了rpc远程调用的实现,所以我们只需要再配置一下服务消费者,告诉hyperf从哪个节点哪个端口调用即可。
5-2、consumer配置
config/autoload/services.php内定义consumers如下:(没有services.php文件的可以自行创建)
<?php return [ 'consumers' => [ [ // 对应消费者类的 $serviceName 'name' => 'UserService', // 直接对指定的节点进行消费,通过下面的 nodes 参数来配置服务提供者的节点信息 'nodes' => [ ['host' => '127.0.0.1', 'port' => 9600], ], ] ], ];
5-3、配置 UserServiceInterface
为了可以方便的注入 UserServiceInterface,我们在 config/autoload/dependencies.php 内定义 UserServiceInterface 和 UserService 的关系如下:
App\JsonRpc\UserServiceInterface::class => App\JsonRpc\UserService::class,
5-4、编写UserController,实现获取用户和创建用户的接口调用
【app\Controller\UserController.php】 <?php declare(strict_types=1); namespace App\Controller; use App\JsonRpc\UserServiceInterface; use Hyperf\Di\Annotation\Inject; use Hyperf\HttpServer\Annotation\AutoController; /** * Class UserController * @package App\Controller * @AutoController() */ class UserController extends AbstractController { /** * @Inject() * @var UserServiceInterface */ private $userServiceClient; public function createUser() { $name = (string) $this->request->input('name', ''); $gender = (int) $this->request->input('gender', 0); return $this->userServiceClient->createUser($name, $gender); } public function getUserInfo() { $id = (int) $this->request->input('id'); return $this->userServiceClient->getUserInfo($id); } }
6、postman访问测试
启动shop_consumer_user项目的同时,务必要保证 shop_provider_user 也启动了,不然请求一定会失败。
7、自动配置服务消费者
你可能已经注意到 app\JsonRpc\UserService 类的方法并没有实际意义,只是构建参数发起请求并返回响应结果,千篇一律的操作着实增加了复杂度。hyperf支持自动配置服务消费者代理类(生产者暂不支持自动配置)。
自动配置非常简单,只需要在 consumer 配置项增加service配置即可,如下:
return [ 'consumers' => [ [ // 对应消费者类的 $serviceName 'name' => 'UserService', // 服务接口名,可选,默认值等于 name 配置的值,如果 name 直接定义为接口类则可忽略此行配置, // 如 name 为字符串则需要配置 service 对应到接口类 'service' => \App\JsonRpc\UserServiceInterface::class, // 直接对指定的节点进行消费,通过下面的 nodes 参数来配置服务提供者的节点信息 'nodes' => [ ['host' => '127.0.0.1', 'port' => 9600], ], ] ], ];
现在我们做两件事,测试consumer走的是自动配置还是手动创建的UserService
- 把 config/autoload/dependencies.php 内定义 UserServiceInterface 和 UserService 的关系屏蔽
- 在 App\JsonRpc\UserService::getUserInfo() 方法内打印点数据测试
GET请求 http://127.0.0.1:9501/user/getUserInfo?id=2 结果发现控制台并没有任何输出,走的是自动配置的consumer
反过来
- 我们再把 config/autoload/dependencies.php 内定义 UserServiceInterface 和 UserService 的关系放开
- 把 config/autoload/services.php 文件内 consumers 的配置项 service 屏蔽
GET请求 http://127.0.0.1:9501/user/getUserInfo?id=2 string(36) "App\JsonRpc\UserService::getUserInfo" 发现控制台输出了我们在 App\JsonRpc\UserService::getUserInfo() 方法内打印的数据, 走的是手动创建的consumer
在没有特殊情况下,后续consumer我们仅做配置,不在手动创建,因为没有创建的必要。
8、配置优化
我们注意到 config/autoload/services.php 文件内 consumers 的配置,一个服务是一个配置,服务消费者需要消费的服务可能很多,所以我们很有必要优化下这里的写法,下面是参考官网的写法:
// 服务定义 $consumerServices = [ 'UserService' => \App\JsonRpc\UserServiceInterface::class, ]; return [ 'consumers' => value(function () use ($consumerServices) { $consumers = []; foreach ($consumerServices as $name => $interface) { $consumers[] = [ 'name' => $name, 'service' => $interface, 'nodes' => [ ['host' => '127.0.0.1', 'port' => 9600], ], ]; } return $consumers; }), ];
这样一来,我们每次只需要在数组 $consumerServices 内添加需要新的服务即可。
最后,我们来看一个比较大的问题。
consumer拿到的结果,又是字符串又是对象,还动不动直接 Internal Server Error. 数据格式的不统一非常不利于前端小伙伴解析。
统一结果处理
为了规范,我们制定了一个简单的标准,统一返回带有code,message,data的数据格式,有兴趣的小伙伴可以先研究下怎么解决这个问题,我们下一节继续。
- 评论区