一、前言
gRPC (gRPC Remote Procedure Calls ) 是Google发起的一个开源远程过程调用(Remote procedure call)框架。
腾讯云七层CLB支持gRPC协议,不妨搭建一套七层HTTP gRPC环境,做模拟测试。
二、gRPC通信模式以及grpc-gateway
gRPC的四种通信模式如下:
- 一元RPC:传入一个请求对象,返回一个请求对象
- 服务端流RPC:传入一个请求对象,服务端可以返回多个结果对象
- 客户端流RPC:传入多个请求对象,服务端返回一个结果对象
- 双向流RPC:传入多个请求对象,返回多个结果对象。
本文将以HTTP为例,让gRPC同时支持HTTP请求作为入口,那么我们需要用到gRPC-Gateway,调用过程如下:
客户端通过提交API数据(Json格式)给gRPC的反向代理入口,grpc-gateway将请求转化为gRPC格式,再递交给内部gRPC服务处理,响应给客户端之前,响应内容也会先转换成Json格式再响应。
三、环境搭建
首先将simplebank项目克隆下来:
git clone https://github.com/techschool/simplebank.git
1.安装go语言环境
在go官方下载页面选择合适版本下载,目前最新的是1.21.1:
wget https://go.dev/dl/go1.21.1.linux-amd64.tar.gz
移除旧版本解压新版本:
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.21.1.linux-amd64.tar.gz
增加PATH路径并使其生效:
echo 'export PATH=$PATH:/usr/local/go/bin' >> /etc/profile
source /etc/profile
验证版本:
go version
2.安装grpc-gateway
进入到项目创建目录和工具文件:
cd simplebank
mkdir tools
touch tools/tools.go
在tools/tools.go文件写入如下内容:
package tools
import (
_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway"
_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2"
_ "google.golang.org/grpc/cmd/protoc-gen-go-grpc"
_ "google.golang.org/protobuf/cmd/protoc-gen-go"
)
自动查找并下载缺少的包:
go mod tidy
此操作会将包依赖添加到go.mod文件中。
将所有二进制文件安装到$GOBIN文件夹:
go install \
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway \
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 \
google.golang.org/protobuf/cmd/protoc-gen-go \
google.golang.org/grpc/cmd/protoc-gen-go-grpc
这些二进制文件稍后被protoc用于生成Golang代码。
3.接口路径和请求方法
接口路径和允许的请求方法都在proto/service_simple_bank.proto中定义,无需修改:
service SimpleBank {
rpc CreateUser (CreateUserRequest) returns (CreateUserResponse) {
option (google.api.http) = {
post: "/v1/create_user"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
description: "Use this API to create a new user";
summary: "Create new user";
};
}
rpc UpdateUser (UpdateUserRequest) returns (UpdateUserResponse) {
option (google.api.http) = {
patch: "/v1/update_user"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
description: "Use this API to update user";
summary: "Update user";
};
}
rpc LoginUser (LoginUserRequest) returns (LoginUserResponse) {
option (google.api.http) = {
post: "/v1/login_user"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
description: "Use this API to login user and get access token & refresh token";
summary: "Login user";
};
}
rpc VerifyEmail (VerifyEmailRequest) returns (VerifyEmailResponse) {
option (google.api.http) = {
get: "/v1/verify_email"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
description: "Use this API to verify user's email address";
summary: "Verify email";
};
}
}
4.安装并运行redis
只是作为模拟环境测试用,以容器方式运行即可:
docker run --name redis -p 6379:6379 -d redis
5.运行postgre
同理,容器方式运行postgre
docker network create bank-network
docker run --name postgres --network bank-network -p 5432:5432 -e POSTGRES_USER=root -e POSTGRES_PASSWORD=secret -d postgres
并进入容器内修改root密码并创建数据库:
docker exec -ti postgres /bin/bash
psql
ALTER USER root WITH PASSWORD 'secret';
CREATE DATABASE simple_bank;
6.运行gRPC
进入到simplebanck目录,启动gRPC服务:
make server
可以看到HTTP网关监听端口为8080,gRPC服务监听端口为9090。
7.使用postman调用并抓包验证
1)路径写错的情况
首先模拟不加任何路径的情况,很显然gRPC服务返回了状态码5以及Not Found的错误信息:
通过抓包可以看到,HTTP协议响应的是404,gRPC服务把gRPC的状态码附带到json内容里面返回给客户端:
2)协议用错的情况
我们将请求改成GET请求,显而易见,gRPC返回状态码12到json里以及协议不被允许的错误信息给客户端:
抓包依然可以看到,HTTP返回501,gRPC返回12:
3)错误入参的情况
将用户名第一个字母大写,拿到gRPC状态码3的报错,并提示只允许小写:
日志上也可以清晰看到,HTTP返回400 Bad Request,gRPC返回状态码3。
4)正常的情况
正常情况下,HTTP状态码为200 OK,gRPC没有返回状态码(实际上是0,没有做代码处理将它返回给客户端):
5)创建已存在用户的情况
重复创建同一个用户,gRPC返回6,HTTP返回409:
8.gRPC状态码说明
从上面的验证不难看出,每种错误都会附带一个gRPC状态码和HTTP状态码,以下是gRPC状态码的一些标准定义:
返回码 | 注释 |
---|---|
OK(0) | 操作成功完成 |
CANCELLED(1) | 操作被取消(通常是被调用者取消) |
UNKNOWN(2) | 未知错误。 |
INVALID_ARGUMENT(3) | 客户端给出了一个无效参数。 |
DEADLINE_EXCEEDED(4) | 在操作完成前超过最后期限。 |
NOT_FOUND(5) | 某些请求实体(例如文件或者目录)无法找到 |
ALREADY_EXISTS(6) | 某些我们试图创建的实体(例如文件或者目录)已经存在 |
PERMISSION_DENIED(7) | 调用者没有权限来执行指定操作。 |
RESOURCE_EXHAUSTED(8) | 某些资源已经被耗尽,可能是用户配额,或者可能是整个文件系统没有空间。 |
FAILED_PRECONDITION(9) | 操作被拒绝,因为系统不在这个操作执行所要求的状态下。 |
ABORTED(10) | 操作中途失败,通常是因为并发问题如时序器检查失败,事务失败等。 |
OUT_OF_RANGE(11) | 操作试图超出有效范围,例如,搜索或者读取超过文件结尾。 |
UNIMPLEMENTED(12) | 操作没有实现,或者在当前服务中没有支持/开启。 |
INTERNAL(13) | 内部错误。 |
UNAVAILABLE(14) | 服务当前不可用。 |
DATA_LOSS(15) | 无法恢复的数据丢失或者损坏。 |
UNAUTHENTICATED(16) | 请求没有操作要求的有效的认证凭证。 |
表单来源于gRPC官方文档。
四、作为RS挂载到CLB
1.在HTTPS监听器下创建一条默认规则
2.指定URL和gRPC状态码
状态码默认值为12,数值范围为0-99,输入值可为数值、多个数值或者范围以及相互组合,如20或20,25或0-99或12,25,30-40的组合。 当gRPC返回状态码与设置的状态码匹配时,认为后端服务器存活。
- 如果后端代码没有对探测请求场景做响应处理,默认选择12即可,表示:操作没有实现,或者在当前服务中没有支持/开启。
- 检查路径同理,如果后端有具体的URL路径,则填写即可,没有则填写为/。
3.绑定RS到监听器
这里绑定的是9090端口,前面已经说过,9090为gRPC的内部服务端口,8080端口只作为HTTP协议入口,监听器后端协议选择的是gRPC,所以端口要与之对应,如果要检查8080或使用8080作为入口访问,则后端协议选择HTTP,健康检查为HTTP或TCP即可。
可以看到前端页面显示健康检查已是正常状态。