携手创作,共同成长!这是我参与「掘金日新计划 · 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控制台,查看注册的服务。
目前是已经成功了,但是没有启动其中两个服务的情况的截图
创建项目
创建两个服务,名字分别为admin
,app
,最开始的安装程序都是一样的,在电脑上要安装PHP7.4的环境极其相关的包管理工具composer。 然后使用composer来创建hyperf框架:
composer create-project hyperf/hyperf-skeleton app
composer create-project hyperf/hyperf-skeleton admin
会弹出很多询问框,除了下面图片中询问是否安装PRC协议 要选择2安装JSON-RPC,其他都要选择默认
然后在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
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个文件有些不同。
最后启动两个服务看到已经注册成功了:
用postman测试一下效果:
总结
GitHub地址:github.com/koala9527/h…
其实弄了两三天的时间,总共有好几个小时,一直都在报错,最后是漏了在services.php配置中少了drivers
字段的配置,在启动的时候一直连接127.0.0.1:8500默认的端口,报错拒绝连接,依据其他教程例子试了好久才试出来, 下面准备接着复现使用golang的Gin框架和Hyperf框架之间使用PRC互相调用,这个已经在公司的项目中已经实现过了,应该并不难,只不过是单向的调用,弄完了想再加上Python试一下,敬请期待。