上个月入职了一家新公司,技术栈用的B站的 Kratos ,发现这个框架为了微服务而封装了很多东西,最主要的一块就是它会同时启动 http 服务跟 gRPC 服务,外部访问通过 RESTful API 方式请求 http 接口进行通信,内部访问通过 gRPC 远程过程调用来进行通信,一份 proto 文件生成两份服务定义,这种方式还是挺新颖的。
因 gRPC 可实现多语言通信,突发奇想用 Hyperf 的 gRPC-client 来请求 Kratos 的 gRPC-server ,通过实践发现还是行得通的,具体步骤如下:
首先利用 Docker 容器来搭建 Kratos 服务的 proto 文件生成环境
docker pull golang:1.20.6
docker run -itd -v D:\project:/go --name golang golang:1.20.6
docker ps
docker exec -it Golang容器ID /bin/bash
进入容器后,因为用的是 Go 官方构建的 Debian 发行版,需要通过 apt 来安装 protoc 及设置 Go 的环境变量
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
apt-get update
apt-get install protobuf-compiler
接下来开始准备安装 Kratos 命令行工具生成项目
go install github.com/go-kratos/kratos/cmd/kratos/v2@latest
kratos new demo
运行完毕后,就在当前目录中生成了一个叫 demo 的 Kratos 项目,接着我们安装一系列的 protoc-gen-go 工具及 wire 依赖注入工具
通过查看项目中的 Makefile 文件我们知道,Kratos 集成了一个 make init 命令来进行这一步骤,然后可以使用 make all 命令来生成 api 及 config 的 pb.go 文件及构建执行环境
make init
make all
因为我使用 Win10 环境已经安装过 Go 及 Goland,所以不需要在容器中进行启动服务了。
先将默认生成的 api/helloworld/v1/greeter.proto 中的 SayHello 接口去除掉 option 定义,方便 gRPC 直接调用
syntax = "proto3";
package helloworld.v1;
import "google/api/annotations.proto";
option go_package = "demo/api/helloworld/v1;v1";
option java_multiple_files = true;
option java_package = "dev.kratos.api.helloworld.v1";
option java_outer_classname = "HelloworldProtoV1";
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {
/*option (google.api.http) = {
get: "/helloworld/{name}"
};*/
}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
再通过配置 Goland 的运行/调试配置来解决本地服务启动
启动服务后监听 http 端口 8000 及 gRPC 端口 9000
接下来再利用 Docker 容器来搭建 Hyperf 服务,新起一个命令行窗口
docker pull hyperf/hyperf:8.2-alpine-v3.18-swoole
docker run --name hyperf-grpc-demo -v D:\project:/data/project -p 9501:9501 -it --privileged -u root --entrypoint /bin/sh hyperf/hyperf:8.2-alpine-v3.18-swoole
docker ps
docker exec -it Hyperf容器ID /bin/bash
直接利用已经集成的 composer 环境来创建 Hyperf 项目
composer create-project hyperf/hyperf-skeleton hyperf-grpc-demo
途中有命令行提示,一路回车下去,碰到提示 Which RPC protocol do you want to use ? 选择 gRPC 选项
Hyperf 项目创建好之后,需要切换到 Golang 容器中使用 protoc 来生成 gRPC 的 PHP 文件
cd /go/demo
protoc --proto_path=./api --proto_path=./third_party --php_out=../hyperf-grpc-demo/grpc/ --openapi_out=fq_schema_naming=true,default_response=false:. ./api/helloworld/v1/greeter.proto
可将 protoc 命令配置到 Makefile 中进行便捷操作
至此 PHP 对应的 gRPC 文件也生成好了,需要调整 Hyperf 项目中的 composer.json 文件自动加载配置
"autoload": {
"psr-4": {
"App\\": "app/",
"GPBMetadata\\": "grpc/GPBMetadata",
"Helloworld\\": "grpc/Helloworld"
},
"files": [
]
},
调整完后记得执行 composer dump-autoload 命令让新的自动加载生效
接着开始编写 gRPC-client 的请求代码,这里我们直接调整 Hyperf 默认的 app/Controller/IndexController.php
在开始编写代码之前,先查看宿主机的局域网IP地址,新起一个命令行窗口执行 ipconfig ,记录下 192.168.XXX.XXX 这段IP地址
namespace App\Controller;
use App\Grpc\GreeterClient;
use Helloworld\V1\HelloRequest;
class IndexController extends AbstractController
{
public function index()
{
// 这个client是协程安全的,可以复用
// 注意这里的IP地址需要填写为刚刚通过 ipconfig 记录下的局域网IP地址
// 端口为 Kratos 指定的 gRPC 服务 9000
$client = new GreeterClient('192.168.1.20:9000', [
'credentials' => null,
]);
$user = $this->request->input('user', 'Hyperf');
$method = $this->request->getMethod();
$request = new HelloRequest();
$request->setName($user);
[$reply, $status] = $client->SayHello($request);
$message = $reply->getMessage();
return [
'method' => $method,
'memory_get_usage' => memory_get_usage(true),
'message' => $message,
];
}
}
再将 gRPC-client 调用过程完善
namespace App\Grpc;
use Helloworld\V1\HelloReply;
use Helloworld\V1\HelloRequest;
use Hyperf\GrpcClient\BaseClient;
class GreeterClient extends BaseClient
{
public function SayHello(HelloRequest $request)
{
return $this->_simpleRequest(
'/helloworld.v1.Greeter/SayHello',
$request,
[HelloReply::class, 'decode']
);
}
}
最后切换至 Hyperf 容器中将 Hyperf 的服务进行启动
php bin/hyperf.php start
我们打开浏览器,直接访问 http://127.0.0.1:9501
如果看到返回的 json 串中提示 "Hello Hyperf" ,并且 Kratos 的服务日志显示
INFO ts=2023-07-29T16:46:14+08:00 caller=biz/greeter.go:44 service.id=Fantasticbin-PC service.name= service.version= trace.id= span.id= msg=CreateGreeter: Hyperf
gRPC 调用大功告成!