三千年读史无外乎功名利禄,九万里悟道终归是诗酒田园。

【最佳实践】腾讯云CLB绑定gRPC后端服务

一、前言

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即可。

可以看到前端页面显示健康检查已是正常状态。

赞(12)
转载请注明出处:RokasYang's Blog » 【最佳实践】腾讯云CLB绑定gRPC后端服务