grpc基本使用

1. 基本概念

RPC:即远程过程调用,能像调用本地一样调用远程方法。相比传统的http请求,rpc请求报文体积更小、传输效率更高,使用了HTTP2协议,跨平台等优点。rpc更侧重于内部调用,http主要侧重于对外、接口、第三方调用等。 Protocol Buffers:一种开源的、跨平台的二进制结构的协议。rpc调用过程中的传输协议,用于解析、传输、生成远程调用中的数据结构的字节流。相比json、xml等文本格式,protocol buffers由于是二进制流文件,会更加高效。

目前主流的rpc框架对比:

grpc四种模式

简单模式 (Simple RPC):就是简单模式,类似于常规的 http 请求,客户端发送请求,服务端响应请求。 流程图:

服务端流式(Server-side streaming RPC):客户端发送一个请求到服务端,服务源源不断的返回数据流给客户端,直到服务端关闭。 流程图:

客户端流式(Client-side streaming RPC):与服务端数据流模式相反,这次是客户端源源不断的向服务端发送数据流,而在发送结束后,由服务端返回一个响应。 流程图:

双向流式(Bidirectional streaming RPC):双方使用读写流去发送一个消息序列,两个流独立操作,双方可以同时发送和同时接收。 应用场景: 流程图:

2. 插件安装

1. 安装grpc

go install google.golang.org/grpc@latest

2. 安装protobuff

下载地址:https://github.com/protocolbuffers/protobuf/releases/tag/v21.5

安装protoc-gen-go插件

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

安装protoc-gen-go-grpc插件:

go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

3. 检测环境

检测protoc

❯ protoc --version
libprotoc 3.21.5

检测protoc-gen-go-grpc:

❯ protoc-gen-go-grpc --version
protoc-gen-go-grpc 1.2.0

3. 开发流程

调用流程图:

3.1 目录结构

3.2 编写proto文件

syntax = "proto3";//声明proto版本
option go_package = "../pb";//声明生成的pb文件路径

package main;

message UserRequest{
    // 定义请求参数
    string name = 1;
    int64 id = 2;
}

message UserResponse{
   // 定义响应参数
   int64 id = 1;//表示一个字段唯一的数字标识符,每一个结构体里面不可重复,定义后不能修改
   string name = 2;
   int64 age = 3;
   // 字段修饰符
   // repeated 表示可变数组,类似于切片类型
   repeated string hobby = 4;
}

service UserInfo{
    // 接口内的方法
    // 定义请求参数为UserRequest,响应参数为UserResponse
    rpc GetUserInfo (UserRequest) returns (UserResponse){

    }
}

枚举的第一个常量映射为0:每个枚举类型必须将其第一个类型映射为0:

  • 必须有有一个0值,我们可以用这个0值作为默认值。
  • 这个零值必须为第一个元素,为了兼容proto2语义,枚举类的第一个值总是默认值。

3.3 生成pb文件

生成命令 protoc --go_out=plugins=grpc:../pb user.proto

3.4 开发server服务端方法

  1. 通过 net.Listen(...) 监听客户端的请求
  2. 通过 grpc.NewServer() 创建一个 gRPC Server 实例
  3. 通过 pb.RegisterUserInfoServer(s, &User{}) 将该服务注册到 gRPC 框架中
  4. 通过 s.Serve(listen) 启动 gRPC 服务
package main

import (
   "context"
   "google.golang.org/grpc"
   "google.golang.org/grpc/grpclog"
   "grpc/m/pb"
   "net"
)

type UserInfoService struct {
}

const (
   Address = "127.0.0.1:50052"
)

var u = UserInfoService{}

func (s *UserInfoService) GetUserInfo(ctx context.Context, req *pb.UserRequest) (resp *pb.UserResponse, err error) {

   // 模拟在数据库查询用户信息
   if req.Name == "test" {
      resp = &pb.UserResponse{
         Id:   req.Id,
         Name: req.Name,
         Age:  19,
         // 切片字段
         Hobby: []string{"FanOne", "FanOneTwo"},
      }
   }
   err = nil
   return
}

func main() {
   listen, err := net.Listen("tcp", Address)
   if err != nil {
      grpclog.Fatalf("Failed to listen: %v", err)
   }
   s := grpc.NewServer()
   pb.RegisterUserInfoServer(s, &u)
   _ = s.Serve(listen)
}

3.5 客户端调用

package main

import (
   "context"
   "fmt"
   "google.golang.org/grpc"
   "grpc/m/pb"
)

const (
   address = "127.0.0.1:50052"
)

func main() {

   conn, err := grpc.Dial(address, grpc.WithInsecure())
   if err != nil {
      panic("grpc链接失败" + err.Error())
   }
   defer conn.Close()
   client := pb.NewUserInfoClient(conn)
   req := new(pb.UserRequest)
   req.Name = "test"
   req.Id = 123

   resp, err := client.GetUserInfo(context.Background(), req)
   if err != nil {
      fmt.Println("响应异常", err)
   }
   fmt.Println("响应结果", resp)
}

grpc.WithInsecure():表示跳过了对服务器证书的验证,新版本已经废弃,使用grpc.WithTransportCredentials(insecure.NewCredentials()),认证后面会讲到

4. 安全认证

1. SSL/TLS 认证

  • 保密(message privacy),保密通过加密encryption实现,所有信息都加密传输,第三方无法嗅探;
  • 完整性(message integrity),通过MAC校验机制,一旦被篡改,通信双方会立刻发现;
  • 认证(mutual authentication),双方认证,双方都可以配备证书,防止身份被冒充;

普通的TLS认证

  1. 生成公钥 openssl ecparam -genkey -name secp384r1 -out server.key

  2. 自签公钥 openssl req -new -x509 -sha256 -key server.key -out server.pem -days 3650

Go 版本 1.15 开始,不推荐使用 GODEBUG=x509ignoreCN=0来处理,而是使用SAN证书,兼容处理方式:环境变量 GODEBUG 为 x509ignoreCN=0

推荐采用新版本SAN认证,基于Ca的认证方式 创建证书步骤:

  1. 新建ca.conf,生成ca证书
[req]
default_bits       = 4096
distinguished_name = req_distinguished_name

[req_distinguished_name]
countryName                 = CN
countryName_default         = CN
stateOrProvinceName         = HuBei
stateOrProvinceName_default = WuHan
localityName                = WuHan
localityName_default        = WuHan
organizationName            = WangXu
organizationName_default    = WangXu
commonName                  = carter
commonName_max              = 64
commonName_default          = localhost

生成ca openssl genrsa -out ca.key 4096

生成csr openssl req -new -sha256 -out ca.csr -key ca.key -config ca.conf

生成crt

openssl x509 \
-req \
-days 365 \
-in ca.csr \
-signkey ca.key \
-out ca.crt
  1. 新加server.conf,生成server证书
[req]
default_bits       = 4096
distinguished_name = req_distinguished_name

[req_distinguished_name]
countryName                 = CN
countryName_default         = CN
stateOrProvinceName         = HuBei
stateOrProvinceName_default = WuHan
localityName                = WuHan
localityName_default        = WuHan
organizationName            = WangXu
organizationName_default    = WangXu
commonName                  = carter
commonName_max              = 64
commonName_default          = localhost

[req_ext]
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
IP  = 127.0.0.1

生成server.key

openssl genrsa -out server.key 2048

生成server.csr

openssl req \
-new \
-sha256 \
-out server.csr \
-key server.key \
-config server.conf

生成server.crt/pem

openssl x509 -req -sha256 -CA ca.crt -CAkey ca.key -CAcreateserial -days 365 \
-in server.csr -out server.crt -extensions req_ext -extfile server.conf

生成client.key openssl ecparam -genkey -name secp384r1 -out client.key

生成client.csr openssl req -new -key client.key -out client.csr -config server.conf

生成client.crt

openssl x509 -req -sha256 -CA ca.crt -CAkey ca.key -CAcreateserial -days 365 \
-in client.csr -out client.crt

最终会生成这些:

  1. 自定义 Token 认证 实现Token认证的前提是,需要定义一个结构体,并实现grpc.PerRPCCredentials接口

5. 失败重试

gRPC的重试策略分为两类 需要设置 GRPC_GO_RETRY=on

  1. 重试策略,失败后进行重试。

"retryPolicy":{
    "maxAttempts": 4,//最大重试次数
    "initialBackoff": "0.1s",
    "maxBackoff": "1s",
    "backoffMutiplier": 2,
    "retryableStatusCodes": [
        "UNAVAILABLE"
        ]
}

config说明:

  • maxAttempts:最大重试次数
  • initialBackoff:第一次重试等待的间隔
  • retryableStatusCodes:服务端返回什么错误码才重试
  • MaxBackoff : 最大退避时间
  • BackoffMultiplier : 退避时间增加倍率
  1. 对冲策略,在不等待响应的情况主动发送单次调用的多个请求。

gRPC 调用最多发送 4 次请求,每次间隔 0.5s,如果没有指定 hedgingDelay 或者为 “0s" 的话,就同时发送四个 请求

"hedgingPolicy":{
    "maxAttempts": 4,//重试次数
    "hedgingDelay": "0.5s",//时间间隔
    "nonFatalStatusCodes":[
    "UNAVAILABLE",
    "INTERNAL",
    "ABORTED"
    ]
}

打 赏