gRPC初探 | 青训营

147 阅读3分钟

一、简介

gRPC 是一个由Google推出的 RPC 框架。RPC(Remote Procedure Call)是指远程调用进程,可以理解为远程调用一个函数。在不同的服务器间,互相调用函数,来达到调用不同的服务,这也被称为微服务。

而 gRPC 将这一过程简化,让你远程调用时就如同本地调用一般得心应手。而且 gRPC 支持多种语言,包括 C/C++、Go、Java、C# 等。

二、安装

1. 系统环境配置

https://github.com/protocolbuffers/protobuf/releases/download/v3.9.0/protoc-3.9.0-win64.zip

下载 protoc 的压缩包并安装,并且将 bin 目录配置到环境变量。

2. 项目环境配置

go install github.com/golang/protobuf/protoc-gen-go

安装 protoc 的 Go 版本生成器,输入命令后根据提示一步步安装即可。可能 Go 编译器会先让你 go get 一些依赖包,然后再用 go install 安装。

三、使用

1. 创建 hello.proto 文件

syntax = "proto3";  
//package hello;  指定proto默认包,不是必须
option go_package = "/hello";  
import "Req.proto";  
import "Res.proto";  
  
service HelloServer {  
    rpc Hello(HelloRequest) returns (HelloResponse) {}  
}

2. 创建Req.proto

syntax = "proto3";  
  
option go_package = "/hello";  
  
message HelloRequest {  
    string name = 1;  
    string message = 2;  
}

3. 创建Res.proto

syntax = "proto3";  
  
option go_package = "/hello";  
  
message HelloResponse {  
    string name = 1;  
    string message = 2;  
}

4. 命令生成

protoc -I . --go_out=plugins=grpc:. ./hello.proto ./Res.proto ./Req.proto

解释一下命令的各个参数

  • -I 是 import 包的路径,也就是输入路径。 . 表示当前目录
  • --go_out=plugins:grpc:. 表示生成 Go 的 RPC 文件在当前目录
  • 后面三个参数是 proto 的源文件

5. 客户端配置

func main() {
    conn, _ := grpc.Dial(":8080", grpc.WithTransportCredentials(insecure.NewCredentials()))  
    defer conn.Close()  
  
    client := hello.NewHelloServerClient(conn)  
    res, _ := client.Hello(context.Background(), &hello.HelloRequest{  
        Name: "cwy",  
        Message: "ok",  
    })  
    fmt.Println(res)
}

和服务端建立连接以后,直接使用 client 调用方法。

6. 服务端配置

type HelloServer struct {  
}  
  
func (HelloServer) Hello(c context.Context, req *hello.HelloRequest) (*hello.HelloResponse, error) {  
    return &hello.HelloResponse{  
        Name: req.Name,  
        Message: req.Message,  
    }, nil  
}  
  
func main() {  
    listen, _ := net.Listen("tcp", ":8080")  
    server := grpc.NewServer()  
    hello.RegisterHelloServerServer(server, HelloServer{})  
    fmt.Println("Run:8080")  
  
    server.Serve(listen)  
}

gRPC 生成的服务是一个接口,所以需要在服务端定义一个结构体来实现。

主函数里监听对应的端口,然后定义一个 gRPC 服务,最后调用生成代码里的注册函数,将服务与实现的结构体绑定,最后在运行服务。

四、proto3 语法介绍

1. 基础语法

proto 作为 gRPC 的源文件,也是类似一门编程语言,有一定的语法。

syntax = "proto3";
//package hello; 指定proto默认包,不是必须 
option go_package = "/hello"; 

import "Req.proto"; 
import "Res.proto"; 

service HelloServer { 
    rpc Hello(HelloRequest) returns (HelloResponse) {} 
}

以开头的 proto 文件为例。

  • syntax 指定 proto 版本,有 proto2 和 proto3 可选,如果不写,默认是 proto2
  • package 指定 proto 的默认包,proto3 可以忽略
  • option go_package 指定生成的代码的 Go 包
  • import 导入其他 proto 文件
  • service 生成一个服务,对应 Go 里面的接口
  • rpc 对应 Go 里面的函数
  • message 生成一个消息,对应 Go 里面的结构体

2. message 消息

message <message_name> {
    <field_rule> <field_type> <field_name> = <field_number>;
}

message 对应 Go 里的结构体,gRPC 会生成相应的 struct

  • <field_rule>:规则,如 required、optional、repeated
  • <field_type>:数据类型
  • <field_name>:字段名,和结构体内部的属性一样
  • <field_number>:字段的编号,同一个 message 内必须唯一,用以标识字段

3. 基本类型

message test {
    double t1 = 1;
    float t2 = 2;
    int32 t3 = 3;
    int64 t4 = 4;
    uint32 t5 = 5;
    uint64 t6 = 6;
    sint32 t7 = 7;
    sint64 t8 = 8;
    fixed32 t9 = 9;
    fixed64 t10 = 10;
    sfixed32
    sfixed64
    bool
    string
    bytes
}

普通的类型大家一定很熟悉,有一些奇怪的类型是如下意思:

  • sint:变长编码。对于负数的处理比 int 编码效率高
  • fixed:定长编码。对于比 228/256 大的数的编码效率比 uint 高
  • sfixed:定长,相当于 int
  • string:必须是 utf-8 或者 7-bit ASCII 编码
  • bytes:任意字节数据

4. 复合类型

message test {
    enum En {
        option allow_alias = true // 开启重复
        t1 = 0;
        t2 = 1;
        t3 = 1;
    }
    En t1 = 1;
    repeated En t2 = 2; // 生成一个 En 的切片
    map<int32, string> m = 3;
}
  • repeated 是声明数组的方式
  • map是无序的,key 必须是可哈希的 string 或者 整型,value 可以是任意类型
  • message 还能嵌套 message 外部无法访问