【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插件

三个插件选择其中一个

// 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库文件

go get github.com/gogo/protobuf/proto

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

// go
protoc --go_out=. *.proto

// gogo
protoc --gogo_out=. *.proto

// gofast
protoc --gofast_out=. *.proto

5、遇到问题

配置代理

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

下载指定版本

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

生成命令

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

三、具体案例编写

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

1、定义消息协议

message.proto

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

syntax = "proto3";

package protocol;

option go_package = "./protocol";
option java_package = "com.kone.pbdemo.protocol";

message File {
  string name = 1;
  int32 size = 2;
}

2、生成协议代码

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

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

package main

import "test12/service"

func main() {
	service.StartServer()
}

cmd/client/main.go


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、运行

运行客户端

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

运行服务端

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 @ 2022-10-02 16:15  踏雪无痕SS  阅读(383)  评论(0编辑  收藏  举报