使用consul注册中心,Hyperf框架之间进行RPC互相调用

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情

前言

目前一直在靠Hyperf框架吃饭,准备总结一下在Hyperf框架里面做的一些事情吧。今天说一下在使用Consul注册中心让两个Hyperf框架进行RPC调用,一直都是拿别人都写好的代码再跑,自己亲自动手实现一下。

目前的环境是Windows 10这个环境

先创建一个consul容器

给微服务应用提供注册发现的功能,阿里云ACK容器云也在使用这个技术方案,并且在Hyperf的文档中也较为详细的介绍了它的使用方法。

docker run -d --name=consul -p 8500:8500  consul 

可以直接在本机上访问:127.0.0.1:8500进入conusl的web控制台,查看注册的服务。

image.png 目前是已经成功了,但是没有启动其中两个服务的情况的截图

创建项目

创建两个服务,名字分别为adminapp,最开始的安装程序都是一样的,在电脑上要安装PHP7.4的环境极其相关的包管理工具composer。 然后使用composer来创建hyperf框架:

composer create-project hyperf/hyperf-skeleton app
composer create-project hyperf/hyperf-skeleton admin

会弹出很多询问框,除了下面图片中询问是否安装PRC协议 要选择2安装JSON-RPC,其他都要选择默认 image.png

然后在Win10系统中不能直接运行Hyperf,需要用到Docker,使用docker把命令行创建的宿主机项目文件映射到容器中运行,注意还要暴露端口:

docker run -d  -it --name rpc-app -v /D/CodeProject/PhpProject/hyperf-jsonrpc-consul/app:/opt/www  -p 19601:9501  -w /opt/www  hyperf/hyperf:7.4-alpine-v3.12-swoole
docker run -d  -it --name rpc-admin -v /D/CodeProject/PhpProject/hyperf-jsonrpc-consul/admin:/opt/www -p 19501:9501  -w /opt/www  hyperf/hyperf:7.4-alpine-v3.12-swoole

接下来进入容器。设置composer源

composer config -g repo.packagist composer <https://mirrors.aliyun.com/composer>

接着根据官方文档和其他教程安装其他的依赖包。

composer require hyperf/consul
composer require hyperf/service-governance
composer require hyperf/service-governance-consul

公共配置文件

主要在两个hyperf框架中的config/autoload/文件夹中添加consul.php配置文件,文件内容都是一致的:

<?php

return [
  'uri' => env('CONSUL_URL', 'http://127.0.0.1:8500'),
];

然后在.env文件中设置这个CONSUL_URL路径。 使用docker查看一下consul在容器中的内网地址: docker inspect consul

image.png

APP_NAME=skeleton
APP_ENV=dev
CONSUL_URL=http://172.17.0.2:8500

DB_DRIVER=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=hyperf
DB_USERNAME=root
DB_PASSWORD=
DB_CHARSET=utf8mb4
DB_COLLATION=utf8mb4_unicode_ci
DB_PREFIX=

REDIS_HOST=172.17.0.5
REDIS_AUTH=
REDIS_PORT=6379
REDIS_DB=0

config/server.php文件,两个框架一样的配置就放在前面展示:

<?php

declare(strict_types=1);

use Hyperf\Server\Server;
use Hyperf\Server\Event;
use Swoole\Constant;

return [
    'mode' => SWOOLE_PROCESS,
    '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'],
            ],
        ],
        [
            'name' => 'jsonrpc',
            'type' => Server::SERVER_BASE,
            'host' => '0.0.0.0',
            'port' => 9502,
            'sock_type' => SWOOLE_SOCK_TCP,
            'callbacks' => [
                Event::ON_RECEIVE => [\Hyperf\JsonRpc\TcpServer::class, 'onReceive'],
            ],
            'settings' => [
                'open_eof_split' => true,
                'package_eof' => "\r\n",
            ],
        ],
    ],
    'settings' => [
        Constant::OPTION_ENABLE_COROUTINE => true,
        Constant::OPTION_WORKER_NUM => env('APP_ENV') == 'prod' ? swoole_cpu_num() : 2,
        Constant::OPTION_PID_FILE => BASE_PATH . '/runtime/hyperf.pid',
        Constant::OPTION_OPEN_TCP_NODELAY => true,
        Constant::OPTION_MAX_COROUTINE => 100000,
        Constant::OPTION_OPEN_HTTP2_PROTOCOL => true,
        Constant::OPTION_MAX_REQUEST => 100000,
        // Constant::OPTION_SOCKET_BUFFER_SIZE => 20 * 1024 * 1024,
        // Task Worker 数量,根据您的服务器配置而配置适当的数量
        Constant::OPTION_TASK_WORKER_NUM => env('APP_ENV') == 'prod' ? 8 : 1,
        // 因为 `Task` 主要处理无法协程化的方法,所以这里推荐设为 `false`,避免协程下出现数据混淆的情况
        Constant::OPTION_TASK_ENABLE_COROUTINE => false,
        //最大上传文件大小
        Constant::OPTION_PACKAGE_MAX_LENGTH => 2 * 1024 * 1024 * 1024,
        //缓存区域
        Constant::OPTION_BUFFER_OUTPUT_SIZE => 20 * 1024 * 1024,
        // 静态资源
        // Constant::OPTION_DOCUMENT_ROOT => BASE_PATH . '/public',

        Constant::OPTION_ENABLE_STATIC_HANDLER => true,
        //Constant::OPTION_STATIC_HANDLER_LOCATIONS => ['/'],
    ],
    'callbacks' => [
        Event::ON_BEFORE_START => [Hyperf\Framework\Bootstrap\ServerStartCallback::class, 'beforeStart'],
        Event::ON_WORKER_START => [Hyperf\Framework\Bootstrap\WorkerStartCallback::class, 'onWorkerStart'],
        Event::ON_PIPE_MESSAGE => [Hyperf\Framework\Bootstrap\PipeMessageCallback::class, 'onPipeMessage'],
        Event::ON_WORKER_EXIT => [Hyperf\Framework\Bootstrap\WorkerExitCallback::class, 'onWorkerExit'],
        // Task callbacks
        Event::ON_TASK => [Hyperf\Framework\Bootstrap\TaskCallback::class, 'onTask'],
        Event::ON_FINISH => [Hyperf\Framework\Bootstrap\FinishCallback::class, 'onFinish'],
    ],
];

App端配置

1.暴露服务
在app服务中新建app/JsonRpc/AdminServiceInterface.php文件来映射admin服务注册到注册中心的服务及其中的服务方法:

<?php

namespace App\JsonRpc;

use Hyperf\RpcClient\AbstractServiceClient;

interface AdminServiceInterface
{
    /**
     * @param string $msg 测试消息
     */
    public function getTestMsg(string $msg);
}

2.调用远程服务方法
然后在app/Controller/IndexController.php使用注解引入使用这个方法:

<?php

declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  [email protected]
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */
namespace App\Controller;

use Hyperf\Di\Annotation\Inject;
use  App\JsonRpc\AdminServiceInterface;

class IndexController extends AbstractController
{
    /**
     * 后台服务
     * @Inject 
     * @var AdminServiceInterface
     */
    protected $admin_service_interface;

    public function index()
    {
        $user = $this->request->input('user', 'Hyperf');
        $method = $this->request->getMethod();
        $msg = $this->admin_service_interface->getTestMsg($user);
        return [
            'method' => $method,
            'message' => "Hello {$msg}.",
        ];
    }
}

3.注册自身对外的服务方法
接着这个app服务还需要新建一个文件app/Service/JsonRpc/AdminService.php文件给admin服务调用:

<?php

declare(strict_types=1);

namespace App\Service\JsonRpc;

use Hyperf\RpcServer\Annotation\RpcService;

/**
 * 产品服务移动端
 * @RpcService(name="AppAdminService", protocol="jsonrpc", server="jsonrpc", publishTo="consul")
 */
class AdminService 
{
    public function getTestMsg(string $msg)
    {
        return 'from_app_rpc:'.$msg;
    }
}

注意这里使用RpcService注解方法指定了这个服务方法注册的名字,协议,协议网管。下面就配置consul相关的配置文件 4.消费者映射配置 config/autoliad/services.php用于找到consul中暴露出来的服务与写好的的接口文件形成映射关系:

<?php


return [
    'consumers' => value(function () {
        $consumers = [];
        $services = [
            'AdminAppService' => App\JsonRpc\AdminServiceInterface::class
        ];
        foreach ($services as $name => $interface) {
            $protocol = 'jsonrpc';
            $consumers[] = [
                // name 需与服务提供者的 name 属性相同
                'name' => $name,
                // 服务接口名,可选,默认值等于 name 配置的值,如果 name 直接定义为接口类则可忽略此行配置,如 name 为字符串则需要配置 service 对应到接口类
                'service' => $interface,
                // 服务提供者的服务协议,可选,默认值为 jsonrpc-http
                'protocol' => $protocol,
                // 负载均衡算法,可选,默认值为 random
                'load_balancer' => 'random',
                // 这个消费者要从哪个服务中心获取节点信息,如不配置则不会从服务中心获取节点信息
                'registry' => [
                    'protocol' => 'consul',
                    'address' => env('CONSUL_URL', 'http://127.0.0.1:8500'),
                ],
                // 配置项,会影响到 Packer 和 Transporter
                'options' => [
                    'connect_timeout' => 25.0,
                    'recv_timeout' => 25.0,
                    'settings' => [
                        // 根据协议不同,区分配置
                        'open_eof_split' => true,
                        'package_eof' => "\r\n",
                        // 'open_length_check' => true,
                        // 'package_length_type' => 'N',
                        // 'package_length_offset' => 0,
                        // 'package_body_offset' => 4,
                    ],
                    // 当使用 JsonRpcPoolTransporter 时会用到以下配置
                    'pool' => [
                        'min_connections' => 1,
                        'max_connections' => 96,
                        'connect_timeout' => 20.0,
                        'wait_timeout' => 20.0,
                        'heartbeat' => -1,
                        'max_idle_time' => 60.0,
                    ],
                ],
            ];
        }
        return $consumers;
    }),
    'providers' => [],
    'drivers' => [
        'consul' => [
            'uri' => env('CONSUL_URL', 'http://127.0.0.1:8500'),	//此处为自己的consul地址
            'token' => '',
            'check' => [
                'deregister_critical_service_after' => '90m',
                'interval' => '1s',
            ],
        ],
    ],
];

Admin端配置

因为文件太长了,这里就不展示具体的代码了,主要全部的代码会放在最后GitHub上面。主要是控制器方法,jsonRpc服务方法映射文件,暴露服务的方法,services.php配置文件4个文件有些不同。
最后启动两个服务看到已经注册成功了:

image.png 用postman测试一下效果:

image.png

image.png

总结

GitHub地址:github.com/koala9527/h…

其实弄了两三天的时间,总共有好几个小时,一直都在报错,最后是漏了在services.php配置中少了drivers字段的配置,在启动的时候一直连接127.0.0.1:8500默认的端口,报错拒绝连接,依据其他教程例子试了好久才试出来, 下面准备接着复现使用golang的Gin框架和Hyperf框架之间使用PRC互相调用,这个已经在公司的项目中已经实现过了,应该并不难,只不过是单向的调用,弄完了想再加上Python试一下,敬请期待。

猜你喜欢

转载自juejin.im/post/7125274400676380709