go使用grpc通信示例

一. protobuf环境配置

  1.下载protobuf编译器protoc, 下载地址: https://github.com/protocolbuffers/protobuf/releases

   注意根据电脑的版本下载,这里使用的是 protoc-3.19.4-win64.zip,下载完成后解压,进入bin目录中发现有一个protoc.exe二进制文件;

      将这个bin地址添加到环境变量中.

   验证安装:在cmd中输入protoc --version查看

     

  2.安装protobuf插件

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

    执行完成后在GOPATH/bin下会生成protoc-gen-go.exe和protoc-gen-go-grpc.exe文件,安装完成.

    之前版本可以通过go get命令安装.

二.编写proto文件test.proto

  protobuf文件规范参考官网.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
syntax = "proto3";
 
option go_package = "./;pb";
package pb;
 
message Student {
  string name = 1;
  int32 age = 2;
}
 
message Class {
  string className = 1;
}
 
service FindClass {
  rpc MyClass(Student) returns (Class){}
}

三.通过插件生成go代码

  在test.proto中定义了一个grpc服务,在test.proto所在的目录中执行:

1
protoc --go_out=./ --go-grpc_out=./ test.proto

  发现在本目录下会生成两个.go文件:test.pb.go和test_grpc.pb.go,结构如下:

    

四.go文件解读

  1.test.pb.go: 源码

    

     在test.pb.go文件中可以看到定义了两个结构体Student和Class,以及一些它们各自对应的方法,对比test.proto文件可以发现,正是我们用message关键字定义的两个消息体,而且结构体中也包含着消息体中对应的字段,其它内容感兴趣的可以自己研究.

  2.test_grpc.pb.go:源码

    

     FindClass是test.proto中定义的服务名称,MyClass是服务中使用的方法.

    client部分定义了一个包含MyClass方法的接口.NewFindClassClient函数用于初始化grpc的客户端,可以看到其返回正是上边定义的接口类型,这就使初始化后的客户端可以直接调用findClassClient结构体的MyClass方法.

    server部分同样也定义了一个包含MyClass方法的接口,接口中还包含了一个内部方法,此处不做分析.RegisterFindClassServer函数用于服务注册,其参数分别为一个grpc服务和一个FindClassServer接口类型.

五.grpc服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package main
 
import (
    "context"
    "errors"
    "fmt"
    "google.golang.org/grpc"
    "net"
    "protobuf/pb"
)
 
type Teacher struct {
    // 由于在test_grpc.pb.go文件中FindClassServer接口不仅包含MyClass方法,还包含mustEmbedUnimplementedFindClassServer内部方法
    // 所以这里直接继承 pb.FindClassServer,否则在调用注册服务时会失败
    pb.FindClassServer
}
 
// MyClass方法接收一个上下文和一个Student对象,返回Class对象
 
func (t *Teacher) MyClass(ctx context.Context, stu *pb.Student) (*pb.Class, error) {
    // 初始化Class对象
    cla := pb.Class{}
    if stu.Name == "root" && stu.Age == 3 {
        cla.ClassName = "Chinese"
        return &cla, nil
    } else {
        return &cla, errors.New("no this student")
    }
}
 
func main() {
    // 初始化一个grpc对象
    service := grpc.NewServer()
    // 注册服务
    pb.RegisterFindClassServer(service, new(Teacher))
    // 设置监听
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        fmt.Println(err)
        return
    }
    defer listener.Close()
    //启动服务
    err = service.Serve(listener)
    if err != nil {
        fmt.Println(err)
        return
    }
} 

六.grpc客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package main
 
import (
    "context"
    "fmt"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
    "protobuf/pb"
)
 
func main() {
    // 连接grpc服务, 添加证书
    client, err := grpc.Dial(":8080", grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil {
        fmt.Println(err)
        return
    }
    defer client.Close()
    // 初始化grpc客户端
    findClassClient := pb.NewFindClassClient(client)
    // findClassClient是一个包含了MyClass方法的接口类型,所以可以直接调用MyClass方法
    class, err := findClassClient.MyClass(context.Background(), &pb.Student{
        Name: "root",
        Age:  3,
    })
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(class.ClassName) // Chinese
} 

七.注意事项

  1. 由于版本问题,在通过proto文件生成.go文件时,之前可以通过: protoc --go_out=plugins=grpc:./ test.proto 来生成test_grpc.pb.go文件,最新版本此方法以及废弃,需要通过protoc --go-grpc_out=./ test.proto来实现;

  2.在编写服务端时,之前的版本可以直接定义一个空结构体去注册服务,但在新版本中,如果直接new一个空结构体会编译失败:

    错误如下:

      Cannot use 'new(Teacher)' (type *Teacher) as the type FindClassServerType does not implement 'FindClassServer' as some methods are missing:mustEmbedUnimplementedFindClassServer()

    可以看到是缺少mustEmbedUnimplementedFindClassServer()方法,在test_grpc.pb.go中也可以看到FindClassServer接口中确实包含了两个方法:

      

  3.如果你使用的编辑器是goland2021.3版本,会发现在test.pb.go文件中有两处标红的地方(但并不影响编译):

      

      

     错误信息如下:

      Cannot use 'ms' (type *messageState) as the type protoreflect.MessageType does not implement 'protoreflect.Message'need the method: ProtoMethods() *methodshave the method: ProtoMethods() *protoiface.Methods

    查了部分资料还是没得到明确的处理方法,有人说是goland2021.3的问题,不知道其他同学有没有遇到同样的问题,参考: https://github.com/grpc/grpc-go/issues/4980.

  4.在编写客户端连接grpc服务时,grpc.Dial()方法中后一个参数是证书设置,之前的版本中可以使用grpc.WithInsecure(),但在新版本中会提示'WithInsecure' is deprecated ,此方法已废除,但并不影响程序运行,因为此例子只是简单的grpc示例,所以为了以防万一,使用了官方提供的方法:

    提示信息: 

      WithInsecure returns a DialOption which disables transport security for this ClientConn. Under the hood, it uses insecure.NewCredentials().Note that using this DialOption with per-RPC credentials (through WithCredentialsBundle or WithPerRPCCredentials) which require transport security is incompatible and will cause grpc.Dial() to fail.Deprecated: use WithTransportCredentials and insecure.NewCredentials() instead. Will be supported throughout 1.x.

1
client, err := grpc.Dial(":8080", grpc.WithTransportCredentials(insecure.NewCredentials()))

 

  

 

 

 

 

posted @   屁桃  阅读(1563)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示