• 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');"
    > InstallerScript::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 => [HyperfHttpServerServer::class, 'onRequest'],
            ],
        ],
    ],
    

    5、编写业务代码

    5-1、编写服务消费者类

    app下新建JsonRpc目录,编写UserService.php和UserServiceInterface.php文件

    【UserServiceInterface.php】
    
    <?php
    namespace AppJsonRpc;
    interface UserServiceInterface
    {
        public function createUser(string $name, int $gender);
        public function getUserInfo(int $id);
    }
    <span class="redactor-invisible-space">
    
    </span>【UserService.php】
    
    <?php
    namespace AppJsonRpc;
    use HyperfRpcClientAbstractServiceClient;
    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 的关系如下:

    AppJsonRpcUserServiceInterface::class => AppJsonRpcUserService::class,
    

    5-4、编写UserController,实现获取用户和创建用户的接口调用

    【appControllerUserController.php】
    
    <?php
    declare(strict_types=1);
    namespace AppController;
    use AppJsonRpcUserServiceInterface;
    use HyperfDiAnnotationInject;
    use HyperfHttpServerAnnotationAutoController;
    /**
     * Class UserController
     * @package AppController
     * @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、自动配置服务消费者

    你可能已经注意到 appJsonRpcUserService 类的方法并没有实际意义,只是构建参数发起请求并返回响应结果,千篇一律的操作着实增加了复杂度。hyperf支持自动配置服务消费者代理类(生产者暂不支持自动配置)。

    自动配置非常简单,只需要在 consumer 配置项增加service配置即可,如下:

    return [
        'consumers' => [
            [
                // 对应消费者类的 $serviceName
                'name' => 'UserService',
                // 服务接口名,可选,默认值等于 name 配置的值,如果 name 直接定义为接口类则可忽略此行配置,
                // 如 name 为字符串则需要配置 service 对应到接口类
                'service' => AppJsonRpcUserServiceInterface::class,
                // 直接对指定的节点进行消费,通过下面的 nodes 参数来配置服务提供者的节点信息
                'nodes' => [
                    ['host' => '127.0.0.1', 'port' => 9600],
                ],
            ]
        ],
    ];
    

    现在我们做两件事,测试consumer走的是自动配置还是手动创建的UserService

    1. 把 config/autoload/dependencies.php 内定义 UserServiceInterface 和 UserService 的关系屏蔽
    2. 在 AppJsonRpcUserService::getUserInfo() 方法内打印点数据测试
    GET请求 http://127.0.0.1:9501/user/getUserInfo?id=2
    结果发现控制台并没有任何输出,走的是自动配置的consumer
    

    反过来

    1. 我们再把 config/autoload/dependencies.php 内定义 UserServiceInterface 和 UserService 的关系放开
    2. 把 config/autoload/services.php 文件内 consumers 的配置项 service 屏蔽
    GET请求 http://127.0.0.1:9501/user/getUserInfo?id=2
    string(36) "AppJsonRpcUserService::getUserInfo"
    发现控制台输出了我们在 AppJsonRpcUserService::getUserInfo() 方法内打印的数据,
    走的是手动创建的consumer
    

    在没有特殊情况下,后续consumer我们仅做配置,不在手动创建,因为没有创建的必要。

    8、配置优化

    我们注意到 config/autoload/services.php 文件内 consumers 的配置,一个服务是一个配置,服务消费者需要消费的服务可能很多,所以我们很有必要优化下这里的写法,下面是参考官网的写法:

    // 服务定义
    $consumerServices = [
        'UserService' => AppJsonRpcUserServiceInterface::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的数据格式,有兴趣的小伙伴可以先研究下怎么解决这个问题,我们下一节继续。

  • 相关阅读:
    在 Spring 中使用 Quartz
    Quartz 快速进阶
    任务调度概述
    Spring Boot 2.x 整合 Mybatis 3.x
    pwd函数实现
    07-图4 哈利·波特的考试 (25 分)
    06-图3 六度空间 (30 分)
    linux中的目录
    Linux中的文件
    06-图2 Saving James Bond
  • 原文地址:https://www.cnblogs.com/wwolf/p/15272453.html
Copyright © 2020-2023  润新知