gRPC框架学习:5、 go+gRPC+proto详细使用实例

gRPC框架学习:5、 go+gRPC+proto详细使用实例


文章目录

  • gRPC框架学习:5、 go+gRPC+proto详细使用实例
    • 1. 前言
    • 2. 完成proto文件
    • 3. 下载安装proto编译器及go相关插件
    • 4. 编译proto文件生成go相关文件
    • 5. 使用框架代码时的包依赖坑点
    • 6. 创建服务端
      • (1). 代码
      • (2). 可能的错误
    • 7. 创建客户端
    • 8. 结果

1. 前言

在掌握rpc、gRPC和proto的一些基本概念、可以书写简单(看懂)proto文件、可以编译proto文件生成对应语言文件后(有点类似SOAP协议需要SWDL文档,proto文档可以结合json并且proto文件书写起来稍微简单一些),我们结合实例进行快速使用,看下如何序列化反序列化需要传递的数据以及如何快速生成rpc服务及如何进行多语言交互。

我们这里以Go语言生成rpc服务,然后使用go客户端做测试,理论上还可以使用c++、Java、python等进行测试,等后面有机会用到的话再单独出实例分析总结。

其它语言的一些支持需要的可以在这里查看并结合Quick Start的文档自行做下测试:https://www.grpc.io/docs/languages

我们这里仿照官方的hello world实例写一个简单的rpc服务提供获取服务版本号的功能,以此总结一下从proto文件到编译生成go文件,到利用生成的go文件创建服务端程序和客户端程序这个流程做一下总结,过程中会对注意事项和踩坑点做详细总结便于下次快速搭建。

2. 完成proto文件

这个其实也是编程的一部分,proto是一个简单的语言,利用该语言和我们需要设计的接口写.proto文件。

关于proto的语法和规范我们之前已经总结了,这里就不多说了,建议先看这部分对相关概念有了解和学习。

syntax = "proto3";

option go_package = "./;test_grpc";
package test_grpc;

service TestGRPC {
  rpc GetVersion (GetVersionRequest) returns (GetVersionResponse);
}

message BaseResponse {
  int32 code = 1;         // 错误码 0:成功; 其他:参考《错误码统计》
  string message = 2;     // 错误消息
}

/*
 ** 版本信息
 */
message Version {
  string software_version = 1;    // 软件版本号
  int64 last_compile_time = 2;    // 上次编译时间
}

message GetVersionRequest {
}

message GetVersionResponse {
  BaseResponse response = 1;
  Version version = 2;
}

3. 下载安装proto编译器及go相关插件

其实官方的go插件安装讲的比较笼统,我就着了这个道,通过官方的文档我们可以知道除了要安装protoc之外,还需要安装protoc-gen-go,实际上如果我们需要使用Go生成rpc服务接口的话还需要安装protoc-gen-go-grpc插件,否则只会生成一个.pb.go,我们在进行rpc服务开发时还需要xx_grpc.pb.go,官方的指南中很容易忽略这一点。

注:官方是将proto和gRPC可能是为了强调proto和gRPC的独立性,所以只说了protoc-gen-go插件,实际上当结合go+gRPC+proto使用时需要安装一个软件,两个插件:
1、protoc
2、protoc-gen-go-grpc
3、protoc-gen-go

因为rpc的框架不止gRPC,proto也可以和其它rpc框架结合使用。

1.安装protoc
根据操作系统具体安装:https://github.com/protocolbuffers/protobuf/tags
2.安装protoc-gen-go
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
3.安装protoc-gen-go-rpc
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

4. 编译proto文件生成go相关文件

需要指定两个插件以及源文件的位置和生成文件的位置,可以指定相对路径(可通过protoc --help获取帮助):

protoc -I=. --go_out=. --go-grpc_out=. ./version.proto

根据proto文件会生成两个文件,这个是官方例子的文件树:

D:\mynotes\go\src\google.golang.org\grpc\examples\helloworld>tree . /F
文件夹 PATH 列表
卷序列号为 0865-F52B
D:\MYNOTES\GO\SRC\GOOGLE.GOLANG.ORG\GRPC\EXAMPLES\HELLOWORLD
├─greeter_client
│      main.go
│
├─greeter_server
│      main.go
│
└─helloworld
        helloworld.pb.go
        helloworld.proto
        helloworld_grpc.pb.go

这个是我们生成的代码的文件:

$ ls
version.pb.go  version.proto  version_grpc.pb.go

5. 使用框架代码时的包依赖坑点

生成.pb.go等插件生成的文件后在使用时需要一些依赖(protobuf相关包及其依赖),但是这些依赖我通过go get的一些全球代理或者七牛的国内代理也没有获取成功,最后还是采用老方法,直接通过GitHub上的仓库clone下载(或者fork到gitee等国内仓后下载,避免某些时候墙的厉害GitHub也无法访问下载),比如:

git clone https://github.com/golang/net.git  src/golang.org/x/net
git clone https://github.com/golang/crypto.git  src/golang.org/x/crypto
git clone https://github.com/golang/sys.git  src/golang.org/x/sys
git clone https://github.com/golang/text.git  src/golang.org/x/text
git clone https://github.com/googleapis/go-genproto.git  src/google.golang.org/genproto

其它有需要的话也是类似的处理方式。

6. 创建服务端

(1). 代码

直接上代码吧,结合官方的helloworld实例,如果前面编译proto文件及依赖安装没有问题的话照猫画虎即可:

package main

import (
    "context"
    "gitee.com/commanderZY/test_grpc/version"
    "google.golang.org/grpc"
    "log"
    "net"
)

type server struct {
     
    test_grpc.UnimplementedTestGRPCServer
}

func (s *server) GetVersion(ctx context.Context, in *test_grpc.GetVersionRequest) (*test_grpc.GetVersionResponse, error) {
     
    resp := test_grpc.BaseResponse{
     
        Code:    0,
        Message: "",
    }
    ver := test_grpc.Version{
     
        SoftwareVersion: "1.0",
        LastCompileTime: 0,
    }
    verResp := test_grpc.GetVersionResponse{
     
        Response: &resp,
        Version:  &ver,
    }
    return &verResp, nil
}

func main() {
     
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
     
        log.Fatalf("failed to listen: %v", err)
    }
    s := grpc.NewServer()
    test_grpc.RegisterTestGRPCServer(s, &server{
     })
    if err := s.Serve(lis); err != nil {
     
        log.Fatalf("failed to serve: %v", err)
    }
}

(2). 可能的错误

无法将 ‘&server{}’ (类型 *server) 用作类型 TestGRPCServer 类型未实现 ‘TestGRPCServer’ ,因为缺少某些方法: mustEmbedUnimplementedTestGRPCServer()

在issue上找到了解决方法:

https://github.com/grpc/grpc-go/issues/3794

gRPC框架学习:5、 go+gRPC+proto详细使用实例_第1张图片

即在定义的结构体中嵌入该空方法即可。

7. 创建客户端

代码:

package main

import (
    "context"
    "fmt"
    testgrpc "gitee.com/commanderZY/test_grpc/version"
    "google.golang.org/grpc"
)

func main() {
     
    conn, err := grpc.Dial(":50051", grpc.WithInsecure())
    if err != nil {
     
        fmt.Println("connect server failed,", err)
    }
    defer conn.Close()

    c := testgrpc.NewTestGRPCClient(conn)
    r1, err := c.GetVersion(context.Background(), &testgrpc.GetVersionRequest{
     })
    if err != nil {
     
        fmt.Println("can't get version,", err)
        return
    }
    fmt.Println(r1.Version)
}

8. 结果

先编译运行服务端程序,然后运行客户端程序,测试结果如下:

gRPC框架学习:5、 go+gRPC+proto详细使用实例_第2张图片

你可能感兴趣的:(gRPC,go,grpc)