hyperf从零开始构建微服务(二)——构建服务消费者

2021年09月15日 阅读数:1
这篇文章主要向大家介绍hyperf从零开始构建微服务(二)——构建服务消费者,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

阅读目录

咱们说过,服务提供者能够提供各类服务,它能够和数据库进行交互;服务消费者是纯消费的服务,只须要远程访问服务提供者便可。php

下面咱们按步骤构建消费者模块。html

源码已上传至github,https://github.com/bailangzhan/hyperf-rpc前端

一、构建服务消费者

除了对时区进行设置,其余的组件暂时都不安装,选择“n”便可。node

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

二、安装json rpc依赖

cd shop_consumer_user
composer require hyperf/json-rpc

三、安装 JSON RPC 客户端

shop_consumer_user 不须要对外提供服务,因此咱们只安装客户端,不须要安装hyperf/rpc-server组件git

composer require hyperf/rpc-client

四、server配置

server的配置这里用默认的就行了,9501端口提供http服务,不须要改动github

'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-一、编写服务消费者类

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

【UserServiceInterface.php】

<?php
namespace App\JsonRpc;
interface UserServiceInterface
{
    public function createUser(string $name, int $gender);
    public function getUserInfo(int $id);
}
<span class="redactor-invisible-space">

</span>【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从哪一个节点哪一个端口调用便可。redis

5-二、consumer配置

config/autoload/services.php内定义consumers以下:(没有services.php文件的能够自行建立)数据库

<?php
return [
    'consumers' => [
        [
            // 对应消费者类的 $serviceName
            'name' => 'UserService',
            // 直接对指定的节点进行消费,经过下面的 nodes 参数来配置服务提供者的节点信息
            'nodes' => [
                ['host' => '127.0.0.1', 'port' => 9600],
            ],
        ]
    ],
];

5-三、配置 UserServiceInterface

为了能够方便的注入 UserServiceInterface,咱们在 config/autoload/dependencies.php 内定义 UserServiceInterface 和 UserService 的关系以下:json

App\JsonRpc\UserServiceInterface::class => App\JsonRpc\UserService::class,

5-四、编写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);
    }
}

六、postman访问测试

启动shop_consumer_user项目的同时,务必要保证 shop_provider_user 也启动了,否则请求会失败。

七、自动配置服务消费者

你可能已经注意到 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

  1. 把 config/autoload/dependencies.php 内定义 UserServiceInterface 和 UserService 的关系屏蔽
  2. 在 App\JsonRpc\UserService::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) "App\JsonRpc\UserService::getUserInfo"
发现控制台输出了咱们在 App\JsonRpc\UserService::getUserInfo() 方法内打印的数据,
走的是手动建立的consumer

在没有特殊状况下,后续consumer咱们仅作配置,不在手动建立,由于没有建立的必要。

八、配置优化

咱们注意到 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的数据格式,有兴趣的小伙伴能够先研究下怎么解决这个问题,咱们下一节继续。

上一篇: Typescript详解
下一篇: K8s工作流程详解