小言_互联网的博客

hyperf搭建与使用RPC微服务

351人阅读  评论(0)


前言

首先,微服务是一种思想,是一种将单个应用程序开发成为一组小服务得方法,每个小服务都在自己的进程中运行并与轻量化机制(通常是HTTP资源API)进行通讯。这些服务围绕着业务功能构建,并且可以由全自动部署机制独立部署。


一、微服务的场景演化

1、一开始

最初的需求:一开始的需求是相当的简单,只需做一个简单的网上商城并挂在公网上就可以赚钱了。这个时候如下图的架构即可满足要求。

2、随着业务发展…

这个时候需要加入数据分析以及移动端相关开发模块。

可以看出,这个阶段存在许多不合理的地方:
移动端和网站有许多同样冗余的代码。
单个应用为了为其他应用提供接口,包含了很多不属于它的逻辑。
所有的应用都在一个数据库上操作,数据库出现性能瓶颈。特别是数据分析跑起来的时候,数据库性能急剧下降。

3、改造…

随着问题的逐渐增多,需要进行微服务改造,需要对项目冗余的代码进行抽象整理出来。微服务改造实际上也是一个抽象的功能,


但是这样子会有一个问题,数据库是共用的,随着时间的推移,总会有一个服务直接从数据库取另一个服务数据的现象。
如果一直保持共用数据库的模式,则整个架构会越来越僵化,失去了架构的意义。所以在这里我们一并把数据库进行拆分。如下图

二、微服务与单体架构风格之间的区别

这种单体应用程序是最自然的构建系统的方式,所有请求的处理都到单体应用程序的进程内,采用应用程序开发语言的基本特性把它拆分成不同的包、类和函数中从而实现模块化,可以在开发者的便携机上开发和测试单体应用。可以采用部署流水线把测试后的应用程序部署到生产环境中,可以增加更多负载均衡器后面的实例数量来水平的扩展服务能力。

单体应用程序可以做的很好,但人们也越来越发现问题,尤其是把应用程序部署到云上以后。变更被捆绑在一起,单体应用程序中一个小的改变都需要整个程序重新部署,时间长了保持模块化的架构也会比较困难,本应影响一个模块的变更也很难做到仅影响单个模块。单个模块的伸缩需要整个程序来伸缩,这也导致需要更多的资源。这些问题就导致了人们自然而然地想到了微服务架构,把程序构建成一系列服务,每个服务可以独立部署和伸缩,每个服务也定义了清晰的边界,不同的服务可以采用不同的语言编写,不同的团队来维护。

正是单体架构的这些问题,才使得微服务风格的架构得以发展,微服务除了服务是可独立部署、可独立扩展的之外,每个服务都提供一个固定的模块边界。甚至允许不同的服务用不同的的语言开发,由不同的团队管理。下面文章探讨一下微服务所具有的共同特征。

三、关于微服务存在的一些问题

1、稳定性下降。服务数量变多导致其中一个服务的概率变大,并且一个服务故障可能导致整个系统崩溃。事实上,在大访问前提下,故障总会出现的。
2、微服务架构整个应用分散服务,定位故障点非常困难。
3、测试方面:服务拆分后,几乎所有功能都会涉及多个服务。原本单个程序的测试变为服务间调用的测试。测试变得更加复杂。

解决方案:
①尽量减少故障发生的概率
②降低故障造成的影响。

四、RPC的那些事

1.概念

顾名思义,PPC是Remote Procedure Call Protocol的缩写,既远程过程调用。

RPC框架,要向调用方屏蔽各种复杂性,要向服务提供方也屏蔽各类复杂性:

1、服务调用方client感觉就像调用本地函数一样,来调用服务。
2、服务提供方server感觉就像实现一个本地函数一样,来实现服务。

2、RPC或者RESTful API

RPC就用于调用者与服务之间的通讯,RPC协议可基于TCP、UDP或者HTTP实现,但是更推荐选择TCP。

1、TCP的支持保持连接,当调用服务的时候不需要每次都进行三次握手才实现。从性能和网络消耗来说RPC都具备了很好的优势。
2、RESTful API 基于HTTP的,也就是说每次调用服务都需要进行三次握手建立起通信才可以实现调用,当我们的并发量高的时候这就会浪费很多带宽资源

服务对外的话采用RESTful API会比RPC更具备优势,因此看自己团队的服务是对内还是对外

3、使用hyperf框架搭建RPC服务

1、hyperf给我们提供了RPC的底层服务,我们并不需要去关心底层通讯细节和调用的过程。
2、hyperf通过定义接口,实现接口,启动RPC Server 提供接口服务。我们只需要简单的写好几个类就可以实现一个简单RPC模块。

搭建环境:

一、分别在两台虚拟机上搭建两套hyperf环境。
搭建过程:

centor7下安装docker

 yum -y install docker-ce --nobest

启动docker

systemctl start docker
systemctl enable docker

下载hyperf环境镜像

docker pull hyperf/hyperf

检查镜像是否下载成功

docker images

回到根目录,运行容器

docker run -itd --network host -v $PWD/first_hyperf:/first_hyperf  --name first_hyperf  hyperf/hyperf

这个时候我们已经进入到容器里面了。

我们进入到我们新建的first_hyperf文件里面去。

将composer源置为国内源,加快下载速度。

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

新建hyperf项目

composer create-project hyperf/hyperf-skeleton client

安装配置如下:


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): 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): 2

Which config center do you want to use ?
  [1] Apollo
  [2] Aliyun ACM
  [n] None of the above
  Make your selection or type a composer package name and version (n): 1

Do you want to use hyperf/constants component ?
  [1] yes
  [n] None of the above
  Make your selection (n): 1

Do you want to use hyperf/async-queue component ? (A simple redis queue component)
  [1] yes
  [n] None of the above
  Make your selection or type a composer package name and version (n): 1

Do you want to use hyperf/amqp component ?
  [1] yes
  [n] None of the above
  Make your selection or type a composer package name and version (n): 1

Do you want to use hyperf/model-cache component ?
  [1] yes
  [n] None of the above
  Make your selection or type a composer package name and version (n): 1

Do you want to use hyperf/elasticsearch component ?
  [1] 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 ? (A open tracing protocol component, adapte with Zipkin etc.)
  [1] yes
  [n] None of the above
  Make your selection or type a composer package name and version (n): 1

安装 JSON RPC 服务端:

composer require hyperf/rpc-server

安装 JSON RPC 客户端:

composer require hyperf/rpc-client

运行hyperf框架

# 进入安装好的 Hyperf 项目目录
cd hyperf-skeleton
# 启动 Hyperf
php bin/hyperf.php start



运行成功。
关闭容器

docker stop  容器id

重新进入容器

docker exec -it 容器id  bash

安装完毕,另外一台服务器也同理安装。

开始搭建RPC

服务端代码编写

编辑IndexController.php文件:

<?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
 */
namespace App\Controller;
use Hyperf\Utils\Context;
use Hyperf\Utils\Coroutine;
use Hyperf\Utils\Parallel;
use Hyperf\Utils\ApplicationContext;
use App\JsonRpc\CalculatorService;
use App\JsonRpc\MathValue;

class IndexController extends AbstractController
{
   
    public function index()
    {
   
        $user = $this->request->input('user', 'Hyperf');
        $method = $this->request->getMethod();

        return [
            'method' => $method,
            'message' => "Hello {
     $user}.",
        ];
    }
    public function add()
    {
   
        $client = ApplicationContext::getContainer()->get(CalculatorService::class);
        $value = $client->add(10, 20);
        return $value;
    }

}

在app下新增JsonRpc目录并新增CalculatorService.php和CalculatorServiceInterface.php文件。

CalculatorService.php

<?php


namespace App\JsonRpc;
use Hyperf\RpcServer\Annotation\RpcService;


/**
 * 注意,如希望通过服务中心来管理服务,需在注解内增加 publishTo 属性
 * @RpcService(name="CalculatorService", protocol="jsonrpc", server="jsonrpc")
 */
class CalculatorService implements CalculatorServiceInterface
{
   

    public function add(int $v1, int $v2): int
    {
   
        return $v1 + $v2;
        // TODO: Implement add() method.
    }

}

CalculatorServiceInterface.php

<?php
declare(strict_types=1);

namespace App\JsonRpc;


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

}

route.php

<?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
 */
use Hyperf\HttpServer\Router\Router;

Router::addRoute(['GET', 'POST', 'HEAD'], '/', 'App\Controller\IndexController@index');
Router::addRoute(['GET', 'POST', 'HEAD'],'/add', 'App\Controller\IndexController@add');
Router::get('/favicon.ico', function () {
   
    return '';
});

server.php

<?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
 */
use Hyperf\Server\Server;
use Hyperf\Server\SwooleEvent;

return [
    'mode' => SWOOLE_PROCESS,
    'servers' => [
        [
            'name' => 'http',
            'type' => Server::SERVER_HTTP,
            'host' => '0.0.0.0',
            'port' => 9601,
            'sock_type' => SWOOLE_SOCK_TCP,
            'callbacks' => [
                SwooleEvent::ON_REQUEST => [Hyperf\HttpServer\Server::class, 'onRequest'],
            ],
        ],
        [
            'name' => 'jsonrpc-http',
            'type' => Server::SERVER_HTTP,
            'host' => '0.0.0.0',
            'port' => 9604,
            'sock_type' => SWOOLE_SOCK_TCP,
            'callbacks' => [
                SwooleEvent::ON_REQUEST => [\Hyperf\JsonRpc\HttpServer::class, 'onRequest'],
            ],
        ],
        [
            'name' => 'jsonrpc',
            'type' => Server::SERVER_BASE,
            'host' => '0.0.0.0',
            'port' => 9603,
            'sock_type' => SWOOLE_SOCK_TCP,
            'callbacks' => [
                SwooleEvent::ON_RECEIVE => [\Hyperf\JsonRpc\TcpServer::class, 'onReceive'],
            ],
            'settings' => [
                'open_eof_split' => true,
                'package_eof' => "\r\n",
                'package_max_length' => 1024 * 1024 * 2,
            ],
        ],
    ],
    'settings' => [
        'enable_coroutine' => true,
        'worker_num' => swoole_cpu_num(),
        'pid_file' => BASE_PATH . '/runtime/hyperf.pid',
        'open_tcp_nodelay' => true,
        'max_coroutine' => 100000,
        'open_http2_protocol' => true,
        'max_request' => 100000,
        'socket_buffer_size' => 2 * 1024 * 1024,
        'buffer_output_size' => 2 * 1024 * 1024,
    ],
    'callbacks' => [
        SwooleEvent::ON_WORKER_START => [Hyperf\Framework\Bootstrap\WorkerStartCallback::class, 'onWorkerStart'],
        SwooleEvent::ON_PIPE_MESSAGE => [Hyperf\Framework\Bootstrap\PipeMessageCallback::class, 'onPipeMessage'],
        SwooleEvent::ON_WORKER_EXIT => [Hyperf\Framework\Bootstrap\WorkerExitCallback::class, 'onWorkerExit'],
    ],
];

消费端代码编写

IndexController.php

<?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
 */
namespace App\Controller;
use Hyperf\Utils\Context;
use Hyperf\Utils\Coroutine;
use Hyperf\Utils\Parallel;
use Hyperf\Utils\ApplicationContext;
use App\JsonRpc\CalculatorServiceInterface;
use function PHPStan\dumpType;

class IndexController extends AbstractController
{
   
    public function index()
    {
   
        $user = $this->request->input('user', 'Hyperf');
        $method = $this->request->getMethod();

        return [
            'method' => $method,
            'message' => "Hello {
   $user}.",
        ];
    }
    public function add()
    {
   
        $client = ApplicationContext::getContainer()->get(CalculatorServiceInterface::class);
        $value = $client->add(10, 20);
        return $value;
    }

}

CalculatorServiceInterface.php

<?php

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


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

server.php

<?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
 */
use Hyperf\Server\Server;
use Hyperf\Server\SwooleEvent;

return [
    'mode' => SWOOLE_PROCESS,
    'servers' => [
        [
            'name' => 'http',
            'type' => Server::SERVER_HTTP,
            'host' => '0.0.0.0',
            'port' => 9501,
            'sock_type' => SWOOLE_SOCK_TCP,
            'callbacks' => [
                SwooleEvent::ON_REQUEST => [Hyperf\HttpServer\Server::class, 'onRequest'],
            ],
        ],
        [
            'name' => 'jsonrpc-http',
            'type' => Server::SERVER_HTTP,
            'host' => '0.0.0.0',
            'port' => 9504,
            'sock_type' => SWOOLE_SOCK_TCP,
            'callbacks' => [
                SwooleEvent::ON_REQUEST => [\Hyperf\JsonRpc\HttpServer::class, 'onRequest'],
            ],
        ],
        [
            'name' => 'jsonrpc',
            'type' => Server::SERVER_BASE,
            'host' => '0.0.0.0',
            'port' => 9503,
            'sock_type' => SWOOLE_SOCK_TCP,
            'callbacks' => [
                SwooleEvent::ON_RECEIVE => [\Hyperf\JsonRpc\TcpServer::class, 'onReceive'],
            ],
            'settings' => [
                'open_eof_split' => true,
                'package_eof' => "\r\n",
            ],
        ],


    ],
    'settings' => [
        'enable_coroutine' => true,
        'worker_num' => swoole_cpu_num(),
        'pid_file' => BASE_PATH . '/runtime/hyperf.pid',
        'open_tcp_nodelay' => true,
        'max_coroutine' => 100000,
        'open_http2_protocol' => true,
        'max_request' => 100000,
        'socket_buffer_size' => 2 * 1024 * 1024,
        'buffer_output_size' => 2 * 1024 * 1024,
    ],
    'callbacks' => [
        SwooleEvent::ON_WORKER_START => [Hyperf\Framework\Bootstrap\WorkerStartCallback::class, 'onWorkerStart'],
        SwooleEvent::ON_PIPE_MESSAGE => [Hyperf\Framework\Bootstrap\PipeMessageCallback::class, 'onPipeMessage'],
        SwooleEvent::ON_WORKER_EXIT => [Hyperf\Framework\Bootstrap\WorkerExitCallback::class, 'onWorkerExit'],
    ],
];

service.php

<?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' => [
        [
            // The service name, this name should as same as with the name of service provider.
            'name' => 'CalculatorService',
            // The service registry, if `nodes` is missing below, then you should provide this configs.
            // 服务接口名,可选,默认值等于 name 配置的值,如果 name 直接定义为接口类则可忽略此行配置,如 name 为字符串则需要配置 service 对应到接口类
            'service' => \App\JsonRpc\CalculatorServiceInterface::class,
            // 服务提供者的服务协议,可选,默认值为 jsonrpc-http
            'protocol' => 'jsonrpc',
            // 负载均衡算法,可选,默认值为 random
            'load_balancer' => 'random',
            // 对应容器对象 ID,可选,默认值等于 service 配置的值,用来定义依赖注入的 key
            'id' => \App\JsonRpc\CalculatorServiceInterface::class,
            // If `registry` is missing, then you should provide the nodes configs.
            'nodes' => [
                // Provide the host and port of the service provider.
                 ['host' => '192.168.227.129', 'port' => 9603]
            ],
            // 配置项,会影响到 Packer 和 Transporter
            'options' => [
                'connect_timeout' => 5.0,
                'recv_timeout' => 5.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' => 32,
                    'connect_timeout' => 10.0,
                    'wait_timeout' => 3.0,
                    'heartbeat' => -1,
                    'max_idle_time' => 60.0,
                ],
            ]

        ],
    ],
];

分别进入这两台主机,分别把服务端和消费者端启动起来。


服务端运行截图

消费者端运行截图

另外一台主机同理运行。

验证:
我们访问消费者端:

我们达到了我们的目的,我们访问消费者端,然后消费者端通过jsonrpc发送tcp请求到服务器端的计算服务。


总结

以上就是关于rpc技术的 演化以及一些 使用场景,并 简单介绍什么是rpc以及如何使用hyperf框架搭建rpc服务的过程。希望对大家的学习有些许的帮助。


转载:https://blog.csdn.net/weixin_40670060/article/details/109582821
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场