hyperf实现Rpc服务(docker+consul)

4 篇文章 0 订阅
1 篇文章 0 订阅

hyperf实现Rpc服务(docker+consul)

前言

在开始搭建hyperf的rpc环境之前,需要准备两台服务器,配置好php与swoole的环境,并且在其中一台服务器中部署好consul服务,其中php的环境配置与consul的搭建,可以在小编的其他篇章的文章中获取,基本环境如下。

服务端 Server :

IP : port (tcp)服务
192.168.67.128 : 9503docker,php,swoole

客户端 Client :

IP : port (tcp)服务
192.168.67.130 : 9508docker,php,swoole,consul

先切换阿里云镜像

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

下拉项目

composer create-project hyperf/hyperf-skeleton 

此处假装已经部署好了hyperf的基本环境,并且可以正常运行hyperf项目

开始部署rpc服务

1 . 1 安装组件

在 server端 与 client 端都需要加载

composer require hyperf/consul
composer require hyperf/json-rpc
composer require hyperf/rpc-server
composer require hyperf/rpc-client
#安装统一接入层
composer require hyperf/service-governance
#选择安装对应的适配器
composer require hyperf/service-governance-consul

1 . 2 发布配置文件

在 server端 与 client 端都需要发布配置

生成 autoload/services.php文件

php bin/hyperf.php vendor:publish hyperf/service-governance

生成 autoload/cunsul.php文件

php bin/hyperf.php vendor:publish hyperf/consul

1 . 3 server服务端 (192.168.67.128)

提供rpc所需要服务以及配置设置,这里依照官网的案例,重点是补充官网中没提到的细节操作

1 . 3 . 1 服务提供

创建 app\JsonRpc目录,并且定义接口与提供的服务,具体配置参考官网文档

<?php

namespace App\JsonRpc;

use Hyperf\RpcServer\Annotation\RpcService;

/**
 * 注意,如希望通过服务中心来管理服务,需在注解内增加 publishTo 属性
 * @RpcService(name="CalculatorService", protocol="jsonrpc-http", server="jsonrpc-http" , publishTo="consul")
 */
class CalculatorService implements CalculatorServiceInterface
{
    // 实现一个加法方法,这里简单的认为参数都是 int 类型
    public function add(int $a, int $b): int
    {
        // 这里是服务方法的具体实现
        return $a + $b;
    }
}

定义接口契约 CalculatorServiceInterface

<?php

declare(strict_types=1);
namespace App\JsonRpc;


interface CalculatorServiceInterface
{
    public function add(int $v1, int $v2): int;

    public function getIpList(int $numbers);
}
1 . 3 . 2 接口与服务的绑定

在服务端,需要在 \config\autoload\dependencies.php 中使得接口与服务的对象绑定

<?php

declare(strict_types=1);

return[
    App\JsonRpc\CalculatorServiceInterface::class => App\JsonRpc\CalculatorService::class,
];
1 . 3 . 3 配置 server.php

在config/autoload/server.php中配置提供tcp服务的端口与监听

[
            'name' => 'jsonrpc',
            'type' => Server::SERVER_BASE,
            'host' => '0.0.0.0',
            'port' => 9503,
            'sock_type' => SWOOLE_SOCK_TCP,
            'callbacks' => [
                Event::ON_RECEIVE => [\Hyperf\JsonRpc\TcpServer::class, 'onReceive'],
                ],
            ],
            'settings' => [
                //'open_eof_split' => true,
                'open_eof_check' => true,
                'package_eof' => "\r\n",
                'package_max_length' => 1024 * 1024 * 20,
                /*'open_length_check' => true,
                'package_length_type' => 'N',
                'package_length_offset' => 0,
                'package_body_offset' => 4,
                'package_max_length' => 1024 * 1024 * 20,*/
            ],
        ]
注意一 :

如果使用consul,项目如果hyperf运行在docker环境内,不自定义服务的 ip 和 port的话 ,在服务启动的时候,hyperf会获取到docker 172.17.0.XXX 的地址注册到consul中,这个地址是无法满足对外提供的,需要配置自定义 ip 和port

1 . 3 . 3 . 1 自定义 ip 和 port

在 app\Listener 目录中创建 RpcRegisterServiceListener

<?php
declare(strict_types=1);

namespace App\Listener;


use Hyperf\ServiceGovernance\Listener\RegisterServiceListener;
use InvalidArgumentException;


/**
 * Rpc服务注册到第三方注册中心重写
 *
 * Class RpcRegisterServiceListener
 *
 * @package App\Listener
 */
class RpcRegisterServiceListener extends RegisterServiceListener
{

    /**
     * 重写获取服务与配置信息 -- 使用自定义ip:port注册服务
     *
     * @return array
     */
    protected function getServers(): array
    {
        $result = [];
        $servers = $this->config->get('server.servers', []);
        foreach ($servers as $server) {
            if (! isset($server['name'], $server['host'], $server['port'])) {
                continue;
            }
            if (! $server['name']) {
                throw new InvalidArgumentException('Invalid server name');
            }
            if ($server['name'] != 'jsonrpc') {
                $result[] = $server;
                continue;
            }
            $host = $server['custom_ip'] ?? $server['host']; // 使用自定义ip
            if (in_array($host, ['0.0.0.0', 'localhost'])) {
                $host = $this->getInternalIp();
            }
            if (! filter_var($host, FILTER_VALIDATE_IP)) {
                throw new InvalidArgumentException(sprintf('Invalid host %s', $host));
            }
            $port = $server['custom_port'] ?? $server['port']; // 使用自定义port
            if (! is_numeric($port) || ($port < 0 || $port > 65535)) {
                throw new InvalidArgumentException(sprintf('Invalid port %s', $port));
            }
            $port = (int) $port;
            $result[$server['name']] = [$host, $port];
        }

        return $result;
    }
}

在 \config\autoload\dependencies.php 中绑定

<?php

declare(strict_types=1);

return[
    Hyperf\ServiceGovernance\Listener\RegisterServiceListener::class => App\Listener\RpcRegisterServiceListener::class,
];

在 .env 中添加配置项

RPC_CUSTOM_IP=192.168.67.128
RPC_CUSTOM_PORT=9503

在 \config\autoload\server.php 的 jsonrpc 中添加配置

[
            'name' => 'jsonrpc',
            'type' => Server::SERVER_BASE,
            .
    		.
			//自定义 ip 和 port
            'custom_ip' => env('RPC_CUSTOM_IP',''),
            'custom_port' => env('RPC_CUSTOM_PORT',''),
            .
            .
        ]
注意二 :

在 settings 的配置项中,开启的粘包处理,如果使用了 consul ,切记不能使用 包头 + 包体 的方式,protocol中的配置也只能是jsonrpc,不能是 jsonrpc-tcp-length-check 只能使用 eof 的方式来处理粘包,不然consul无法接收到请求的数据,则会抛出异常 Cannot select any node from load balancer.

如果使用nodes的方式,则可以使用包头 + 包体 的方式

'settings' => [
                //'open_eof_split' => true,
                'open_eof_check' => true,
                'package_eof' => "\r\n",
                'package_max_length' => 1024 * 1024 * 20,
                //使用consul不能使用,nodes的方式可以使用
                /*'open_length_check' => true,
                'package_length_type' => 'N',
                'package_length_offset' => 0,
                'package_body_offset' => 4,
                'package_max_length' => 1024 * 1024 * 20,*/
            ],
1 . 4 定义代理消费者类

在 \config\autoload\services.php 中,定义rpc服务配置,主要针对生产者把配置数据注册进consul中

<?php
return [
    'enable' => [
        // 开启服务发现
        'discovery' => true,
        // 开启服务注册
        'register' => true,
    ],
    'consumers' => [],
    'providers' => [],
    // 服务驱动相关配置
    'drivers' => [
        'consul' => [
            'uri' => 'http://192.168.67.130:8500',
            'token' => '',
            'check' => [
                'deregister_critical_service_after' => '90m',
                'interval' => '1s',
            ],
        ],
    ],
];

到此,服务端的配置基本就弄好了,现在启动服务,正常运作,consul就会查询到注册的服务

在这里插入图片描述

2 . 1 client 客户端 (192.168.67.130)

2 . 1 . 1 拿到 server端定义的接口

创建 \app\JsonRpc\CalculatorServiceInterface.php

<?php

declare(strict_types=1);
namespace App\JsonRpc;


interface CalculatorServiceInterface
{
    public function add(int $v1, int $v2): int;

    public function getIpList(int $numbers);
}
2 . 1 . 2 客户端调用
<?php

declare(strict_types=1);

namespace App\Controller;
use Hyperf\Utils\ApplicationContext;
use App\JsonRpc\MathValue;
use Hyperf\Di\Annotation\Inject;
use App\JsonRpc\CalculatorServiceInterface;

class IndexController extends AbstractController
{
    use ApiResponse;


    public function add()
    {
        try{
            $client = ApplicationContext::getContainer()->get(CalculatorServiceInterface::class);

            $data = $client->add(1,3);

            return $data;
        }catch (\Exception $e){
            return $e->getMessage();
        }
    }

    public function getIpList()
    {
        try{
            $client = ApplicationContext::getContainer()->get(CalculatorServiceInterface::class);

            $result = $client->getIpList(30);

            return  $this->success($result);
        }catch (\Exception $e){
            return $e->getMessage();
        }
    }

}
2 . 1 . 3 定义代理消费者类

在 \config\autoload\services.php 中,定义rpc服务配置,主要针对消费者从consul中发现配置

<?php

declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */

return [
    'consumers' =>  [
        // jsonrpc 通过consul中台进行请求
        [
            'name'  =>  'CalculatorService',
            'service'   =>  \App\JsonRpc\CalculatorServiceInterface::class,
            'protocol'  =>  'jsonrpc', // 默认为jsonrpc-http 所需必须进行指定
            //'protocol' => 'jsonrpc-tcp-length-check',
            'registry' => [
                'protocol' => 'consul',
                'address' => 'http://192.168.67.130:8500', // 这里需要自己的consul地址
            ],
            // 如果没有指定上面的 registry 配置,即为直接对指定的节点进行消费,通过下面的 nodes 参数来配置服务提供者的节点信息
            'nodes' => [
                ['host' => '192.168.67.128', 'port' => 9503],
            ],
            // 配置项,会影响到 Packer 和 Transporter
            'options' => [
                'connect_timeout' => 5.0,
                'recv_timeout' => 5.0,
                'settings' => [
                    // 根据协议不同,区分配置
                    //'open_eof_split' => true,
                    'open_eof_check' => true,
                    'package_eof' => "\r\n",
                    /*'open_length_check' => true,
                    'package_length_type' => 'N',
                    'package_length_offset' => 0,
                    'package_body_offset' => 4,
                    'package_max_length' => 1024 * 1024 * 20,*/
                ],
                // 重试次数,默认值为 2,收包超时不进行重试。暂只支持 JsonRpcPoolTransporter
                'retry_count' => 2,
                // 重试间隔,毫秒
                'retry_interval' => 100,
                // 使用多路复用 RPC 时的心跳间隔,null 为不触发心跳
                'heartbeat' => 30,
                // 当使用 JsonRpcPoolTransporter 时会用到以下配置
                'pool' => [
                    'min_connections' => 1,
                    'max_connections' => 32,
                    'connect_timeout' => 10.0,
                    'wait_timeout' => 3.0,
                    'heartbeat' => -1,
                    'max_idle_time' => 60.0,
                ],
            ],
        ],
    ]
];

注意 :在 settings 的配置项中,开启的粘包处理,如果使用了 consul ,切记不能使用 包头 + 包体 的方式,protocol中的配置也只能是jsonrpc,不能是 jsonrpc-tcp-length-check 只能使用 eof 的方式来处理粘包,不然consul无法接收到请求的数据,则会抛出异常 Cannot select any node from load balancer.

如果使用nodes的方式,则可以使用包头 + 包体 的方式

2 . 1 . 4 请求客户端

客户端首先去 consul 中获取到对应的 ip 和 port,在拿 ip 和 port 去远程调用 另一台服务器的方法,并返回到客户端中

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Docker+Consul+Nginx+keepalived是一种常用的云原生架构方案,它结合了容器化、服务发现、负载均衡和高可用等多种技术手段,可用于构建高可用、弹性、可扩展的应用系统。 该方案的主要组件包括: 1. Docker:用于容器化应用程序和服务,提供了高效、轻量、可移植的应用打包和部署方式。 2. Consul:用于服务发现和配置管理,支持多数据中心、跨平台、高度可扩展的分布式系统。 3. Nginx:用于负载均衡和反向代理,支持高并发、高可用的流量分发。 4. keepalived:用于实现高可用的服务和节点,提供了基于 VRRP 协议的故障转移和自动切换功能。 在该方案中,Docker 容器作为应用程序和服务的运行环境,使用 Consul 进行服务注册和发现,并通过 Nginx 进行流量分发和负载均衡。同时,使用 keepalived 实现高可用的服务和节点,确保系统的稳定性和可用性。 项目描述可以按照以下步骤进行撰写: 1. 项目背景和目的:简要介绍本项目的背景和目的,说明为什么选择 Docker+Consul+Nginx+keepalived 方案。 2. 技术架构:详细介绍该方案的技术架构和组件,包括 DockerConsul、Nginx 和 keepalived 的作用和使用方式。 3. 系统功能:描述系统的主要功能和特点,包括服务发现、负载均衡、高可用等方面。 4. 实现方式:介绍系统的具体实现方式和实现步骤,包括 Docker 镜像的构建、应用程序的容器化、Consul 的配置和使用、Nginx 的配置和使用、keepalived 的配置和使用等。 5. 测试和验证:对系统进行测试和验证,验证系统的功能和性能是否符合预期,是否满足高可用和弹性的要求。 6. 总结和展望:对本项目进行总结和展望,分析该方案的优缺点和适用范围,展望未来的发展方向和趋势。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值