【Golang】关于Go语言中使用gRPC的使用

一、gRPC介绍

gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特性。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。

gRPC有四种服务方法:

  • Unary RPCs,一元RPC。客户端发送一个请求到服务端,服务端响应一个请求。
    • rpc getUser (User) returns (User) {}
  • Server streaming RPCs,服务端流RPC。客户端发送一个请求到服务端,获取到一个流去连续读取返回的消息,直到消息全部获取。gRPC保证单个请求的消息顺序。
    • rpc getUsers (User) returns (stream User) {}
  • Client streaming RPCs,客户端流RPC。客户端给服务器通过流写入连续的消息,一旦客户端完成了消息写入,就等待服务端读取完成然后返回一个响应。同时gRPC也会保证单个请求的消息顺序。
    • rpc saveUsers (stream User) returns (User) {}
  • Bidirectional streaming RPCs,双向流。客户端和服务端都可以通过 read-write流发送一个连续的消息。两个流之间的操作是相互独立的。所以,客户端和服务端可以同时进行流的读写。
    • rpc saveUsers (stream User) returns (stream User) {}

二、插件及其生成代码

1、进入protobuf仓库,下载和电脑相对应的版本

https://github.com/protocolbuffers/protobuf/releases

2、安装gogoprotobuf插件

三个插件选择其中一个

1
2
3
4
5
6
7
8
// go官方
go get github.com/golang/protobuf/protoc-gen-go
 
// 完全兼容google protobuf,它生成的代码质量和编解码性能均比goprotobuf高一些
go get github.com/gogo/protobuf/protoc-gen-gogo
 
// 最快
go get github.com/gogo/protobuf/protoc-gen-gofast

3、安装gogoprotobuf库文件

1
go get github.com/gogo/protobuf/proto

4、生成文件和下载的插件选择的对应命令

1
2
3
4
5
6
7
8
// go
protoc --go_out=. *.proto
 
// gogo
protoc --gogo_out=. *.proto
 
// gofast
protoc --gofast_out=. *.proto

5、遇到问题

配置代理

1
2
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct // 使用七牛云的

下载指定版本

1
2
go install  github.com/golang/protobuf/protoc-gen-go@v1.5.2
go install  github.com/micro/micro/v3/cmd/protoc-gen-micro@master

生成命令

1
2
3
protoc -I . --go_out=plugins=grpc:. ./*.proto
protoc --micro_out=./ --go_out=./ productService.proto
protoc-go-inject-tag -input=../productService.pb.go

三、具体案例编写

1
protoc -I . --go_out=plugins=grpc:. protocol/*.proto
  • protoc是生成代码插件
  • gogo_out是go语言代码插件
  • plugins=grpc:./. go_out后面使用grpc插件将代码生成的放到和proto文件同目录
  • protocol/*.proto。protobuf协议文件地址

1、定义消息协议

message.proto

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
syntax = "proto3";
package protocol;
 
import "protocol/file.proto";
 
option go_package = "./protocol";
option java_multiple_files = true;
option java_package = "com.kone.pbdemo.protocol";
 
message User {
  reserved 6 to 7;
  reserved "userId2";
  int32 userId = 1;
  string username = 2;
  oneof msg {
    string error = 3;
    int32 code = 4;
  }
  string name = 8;
 
  UserType userType = 9;
  repeated int32 roles = 10;
 
  protocol.File file = 11;
  map<string, string> hobbys = 12;
}
 
enum UserType {
  UNKNOW = 0;
  ADMIN = 1;
  BUSINESS_USER = 2;
};
 
service UserService {
  rpc getUser (User) returns (User) {}
  rpc getUsers (User) returns (stream User) {}
  rpc saveUsers (stream User) returns (User) {}
}
 
service FileService {
  rpc getFile(User) returns(File) {}
}

file.proto

1
2
3
4
5
6
7
8
9
10
11
syntax = "proto3";
 
package protocol;
 
option go_package = "./protocol";
option java_package = "com.kone.pbdemo.protocol";
 
message File {
  string name = 1;
  int32 size = 2;
}

2、生成协议代码

1
protoc -I . --go_out=plugins=grpc:. protocol/*.proto
 

3、服务端方法实现

在协议中,RPC定义了3个方法:getUser, getUsers, saveUsers。

这三个方法分别对应gRPC四种服务的:

  • getUser: 一元RPC。客户端一次请求返回单个用户信息的响应。
  • getUsers: 服务端流。服务端返回连续的用户。
  • saveUsers: 客户端流。客户端将批量保存的用户信息连续传输给服务端。

在user_service.go中分别实现这3个方法。

service/user_service.go

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package service
 
import (
    "context"
    "fmt"
    "io"
    "net"
    "test12/protocol"
    "google.golang.org/grpc"
)
 
type UserSever struct {
}
 
func (u *UserSever) GetUser(ctx context.Context, user *protocol.User) (*protocol.User, error) {
    fmt.Println("get user from server ", user)
 
    return &protocol.User{
        Username: user.Username,
        Name:     "the new name",
    }, nil
}
 
func (u *UserSever) GetUsers(user *protocol.User, stream protocol.UserService_GetUsersServer) error {
    user1 := &protocol.User{
        Username: user.Username,
        Name:     "stream new name",
    }
    if err := stream.Send(user1); nil != err {
        fmt.Println("send user1 error", err.Error())
        return err
    }
 
    user2 := &protocol.User{
        Username: user.Username,
        Name:     "stream new name2",
    }
    if err := stream.Send(user2); nil != err {
        fmt.Println("send user2 error", err.Error())
        return err
    }
    return nil
}
 
func (u *UserSever) SaveUsers(stream protocol.UserService_SaveUsersServer) error {
    for {
        user, err := stream.Recv()
        if err == io.EOF {
            return stream.SendAndClose(&protocol.User{
                Username: "back username",
                Name:     "name",
            })
        }
 
        if nil != err {
            fmt.Println("SaveUsers error ", err.Error())
            return err
        }
 
        fmt.Println("SaveUsers get user ", user)
    }
}
 
func NewServer() *UserSever {
    return &UserSever{}
}
 
func StartServer() {
    fmt.Println("start...")
    lis, err := net.Listen("tcp", "127.0.0.1:9091")
    if nil != err {
        fmt.Println(err.Error())
        return
    }
 
    var opts []grpc.ServerOption
 
    grpcServer := grpc.NewServer(opts...)
    protocol.RegisterUserServiceServer(grpcServer, NewServer())
 
    grpcServer.Serve(lis)
}

cmd/server/main.go

1
2
3
4
5
6
7
package main
 
import "test12/service"
 
func main() {
    service.StartServer()
}

cmd/client/main.go


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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package main
 
import (
    "context"
    "fmt"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
    "io"
    "test12/protocol"
)
 
func main() {
    conn, err := grpc.Dial("127.0.0.1:9091", grpc.WithTransportCredentials(insecure.NewCredentials()))
    if nil != err {
        fmt.Println(err.Error())
    }
    defer conn.Close()
 
    c := protocol.NewUserServiceClient(conn)
 
    user := &protocol.User{
        Username: "user1",
        UserId:   10,
    }
 
    response, err2 := c.GetUser(context.Background(), user)
    if err2 != nil {
        fmt.Printf("get user error: %s \n", err2)
        return
    }
 
    fmt.Printf("response msg: %s \n", response)
 
    resStream, err3 := c.GetUsers(context.Background(), user)
    if nil != err3 {
        fmt.Println(err3.Error())
        return
    }
    for {
        res, err4 := resStream.Recv()
        if err4 == io.EOF {
            break
        }
 
        if nil != err4 {
            fmt.Println(err4.Error())
            break
        }
 
        fmt.Println("get stream response ", res)
    }
 
    resStream2, err4 := c.SaveUsers(context.Background())
    if nil != err4 {
        fmt.Println(err4.Error())
        return
    }
 
    resStream2.Send(user)
    resStream2.Send(user)
    reply, err5 := resStream2.CloseAndRecv()
    if nil != err5 {
        fmt.Println(err5.Error())
        return
    }
 
    fmt.Println("SaveUsers replay ", reply)
}

4、运行

运行客户端

1
2
PS D:\cpz\go-demo\test11\server> go run main.go
Listen on 127.0.0.1:9988

运行服务端

1
2
3
4
5
6
7
8
PS D:\cpz\go-demo\test12\cmd\server> go run main.go
start...
get user from server  userId:10 username:"user1"
SaveUsers get user  userId:10 username:"user1"
SaveUsers get user  userId:10 username:"user1"
get user from server  userId:10 username:"user1"
SaveUsers get user  userId:10 username:"user1"
SaveUsers get user  userId:10 username:"user1"

  

posted @   踏雪无痕SS  阅读(408)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
历史上的今天:
2021-10-02 【Golang】Go 通过结构(struct) 实现接口(interface)
点击右上角即可分享
微信分享提示