9-protobuf进阶

官方地址: https://developers.google.com/protocol-buffers/docs/proto3

一 protobuf 基本类型和默认值

1.1 protobuf类型和语言对应关系

该表格展示了定义于.proto文件中的类型,与go和python对应的类型:

.proto Type Notes Python Type Go Type
double float float64
float float float32
int32 使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代 int int32
uint32 使用变长编码 int uint32
uint64 使用变长编码 int uint64
sint32 使用变长编码,这些编码在负值时比int32高效的多 int int32
sint64 使用变长编码,有符号的整型值。编码时比通常的int64高效。 int int64
fixed32 总是4个字节,如果数值总是比总是比228大的话,这个类型会比uint32高效。 int uint32
fixed64 总是8个字节,如果数值总是比总是比256大的话,这个类型会比uint64高效。 int uint64
sfixed32 总是4个字节 int int32
sfixed64 总是8个字节 int int64
bool bool bool
string 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。 str string
bytes 可能包含任意顺序的字节数据。 str []byte

可以在文章Protocol Buffer 编码中,找到更多"序列化消息时各种类型如何编码"的信息。

  1. 在java中,无符号32位和64位整型被表示成他们的整型对应形似,最高位被储存在标志位中。
  2. 对于所有的情况,设定值会执行类型检查以确保此值是有效。
  3. 64位或者无符号32位整型在解码时被表示成为ilong,但是在设置时可以使用int型值设定,在所有的情况下,值必须符合其设置其类型的要求。
  4. python中string被表示成在解码时表示成unicode。但是一个ASCIIstring可以被表示成str类型。
  5. Integer在64位的机器上使用,string在32位机器上使用

1.2 protobuf默认值

如果protobuf定义了类型,在gRPC使用过程中没有传值,会使用默认值

当一个消息被解析的时候,如果被编码的信息不包含一个特定的元素,被解析的对象锁对应的域被设置位一个默认值,对于不同类型指定如下:

  • 对于strings,默认是一个空string
  • 对于bytes,默认是一个空的bytes
  • 对于bools,默认是false
  • 对于数值类型,默认是0
  • 对于枚举,默认是第一个定义的枚举值,必须为0;
  • 对于消息类型(message),域没有被设置,确切的消息是根据语言确定的,详见generated code guide
    对于可重复域的默认值是空(通常情况下是对应语言中空列表)。
    注:对于标量消息域,一旦消息被解析,就无法判断域释放被设置为默认值(例如,例如boolean值是否被设置为false)还是根本没有被设置。你应该在定义你的消息类型时非常注意。例如,比如你不应该定义boolean的默认值false作为任何行为的触发方式。也应该注意如果一个标量消息域被设置为标志位,这个值不应该被序列化传输。
    查看generated code guide选择你的语言的默认值的工作细节

演示案例

目录结构

proto_default_demo
  -client
  	main.go
  -proto
  	hello.proto
  -server
  	main.go

hello.proto

syntax = "proto3";
option go_package = ".;proto";
// 定义一个服务,gRPC自有的,它需要用grpc插件生成,也就是咱们安装的那个插件
service Hello{
  // 服务内有一个函数叫Hello,接收HelloRequest类型参数,返回HelloResponse类型参数
  rpc Hello(HelloRequest) returns(HelloResponse);
}

// 类似于go的结构体,可以定义属性
message HelloRequest {
  string name = 1; // 1 是编号,不是值
  int32 age = 2;
  repeated string girls = 3;

}
// 定义一个响应的类型
message HelloResponse {
  string reply =1;
}

生成go文件

//protoc --go_out=. --go_opt=paths=source_relative ./hello.proto
//protoc --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false --go-grpc_opt=paths=source_relative ./hello.proto

client/main.go

package main

import (
	"context"
	"fmt"
	"go_test_learn/proto_default_demo/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

func main() {

	conn, err := grpc.Dial("127.0.0.1:50052", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		panic("连接服务异常")
	}
	defer conn.Close()
	client := proto.NewHelloClient(conn)
	request := proto.HelloRequest{}  // 不传值,看服务端打印
	res, err := client.Hello(context.Background(), &request)
	if err != nil {
		panic("调用方法异常")
	}
	//打印出返回的数据
	fmt.Println(res.Reply)
}

server/main.go

package main

import (
	"context"
	"fmt"
	"go_test_learn/proto"
	"google.golang.org/grpc"
	"net"
)

type HelloServer struct {
}


func (s *HelloServer) Hello(context context.Context, request *proto.HelloRequest) (*proto.HelloResponse, error) {
	fmt.Println(request.Name) //此处打印出默认值
	return &proto.HelloResponse{Reply:"收到客户端的消息为:"+request.Name}, nil
}
func main() {
	g := grpc.NewServer()
	s := HelloServer{}
	proto.RegisterHelloServer(g,&s)
	lis,error:=net.Listen("tcp","0.0.0.0:50052")
	if error!=nil{
		panic("启动服务异常")
	}
	g.Serve(lis)

}

二 option go_package的作用

可以为.proto文件新增一个可选的package声明符,用来防止不同的消息类型有命名冲突,区分语言,go_package为go语言的定义

// 基本使用
option go_package = ".;proto";
// 这样会把go文件生成到当前路径,并且go文件的包名为proto
// 指定生成的go文件放到某个路径下
option go_package = "common/hello/proto/v1";
// 这样会把go文件生成到当前路径下的common/hello/proto/文件夹中,包名为v1

// 使用命令生成
protoc --go_out=. ./hello.proto
protoc --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false  ./hello.proto
image-20220514183256258
// 指定生成的go文件放到某个路径下
option go_package = "../../common/hello/proto/v1";
// 这样会把go文件生成到当前路径下的上两级目录的common/hello/proto/文件夹中,包名为v1
// 这样便于以后公共的生成到一起,多个微服务共用同样的文件

// 使用命令生成
protoc --go_out=. ./hello.proto
protoc --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false  ./hello.proto

三 服务端客户端同步问题

因为客户端和服务端要使用同一个proto文件,可能是两个人写的,如果两个proto文件内容不一致,会导致错误

3.1 顺序导致的错误

dif_proto
	-server
		-main.go
		-proto
			-hello.proto  // 该文件应该和client下的文件完全一致
	-client
    -main.go
    -proto
    	-hello.proto // 该文件应该和server下的文件完全一致

server/proto/hello.proto

syntax = "proto3";
option go_package = ".;proto";

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

message HelloRequest {
  string name = 1;  // 服务端编号为 name是1,gender 是2
  string gender = 2;

}

message HelloResponse {
  string reply =1;
}

client/proto/hello.proto

syntax = "proto3";
option go_package = ".;proto";

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

message HelloRequest {
  // 客户端编号与服务端编号顺序不一致 name是2,gender 是1
  string gender = 1;
  string name = 2;

}
// 定义一个响应的类型
message HelloResponse {
  string reply =1;
}

生成go文件

cd dif_proto/client/proto
//protoc --go_out=. ./hello.proto
//protoc --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false  ./hello.proto
cd dif_proto/server/proto
//protoc --go_out=. ./hello.proto
//protoc --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false  ./hello.proto

client/main.go

package main

import (
	"context"
	"fmt"
	"go_test_learn/dif_proto/client/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

func main() {

	conn, err := grpc.Dial("127.0.0.1:50052", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		panic("连接服务异常")
	}

	defer conn.Close()
	client := proto.NewHelloClient(conn)
	request := proto.HelloRequest{
		Name: "lqz",
		Gender: "男",
	}
	res, err := client.Hello(context.Background(), &request)
	if err != nil {
		panic("调用方法异常")
	}
	//打印出返回的数据
	fmt.Println(res.Reply)
}

server/main.go

package main

import (
   "context"
   "fmt"
   "go_test_learn/dif_proto/server/proto"
   "google.golang.org/grpc"
   "net"
)

type HelloServer struct {
}



func (s *HelloServer) Hello(context context.Context, request *proto.HelloRequest) (*proto.HelloResponse, error) {
   fmt.Println("name:",request.Name)
   fmt.Println("gender:",request.Gender)
   return &proto.HelloResponse{Reply:"ok"}, nil
}
func main() {
   g := grpc.NewServer()
   s := HelloServer{}
   proto.RegisterHelloServer(g,&s)
   lis,error:=net.Listen("tcp","0.0.0.0:50052")
   if error!=nil{
      panic("启动服务异常")
   }
   g.Serve(lis)


}

结果

image-20220514184803702

3.2 服务端数据多,客户端数据少

这样不客户端和服务端都能运行,只是数据会少

//client/proto/hello.proto
message HelloRequest {
  string name = 1;
  string gender = 2;
}
//server/proto/hello.proto
message HelloRequest {
  string name = 1;
}

// 重新命令生成go文件,重新运行客户端和服务端

// 程序正常运行

四 import另一个proto

4.1 引入自定义的proto

// 目录结构
import_proto
  -client
  	-main.go
  -server
  	-main.go
  -proto
  	-order.proto
  	-base.proto

proto/order.proto

syntax = "proto3";
import "base.proto";  // 导入另一个proto,就可以使用里面的Empty和Pong了
option go_package = ".;proto";

service Hello{
  rpc Hello(HelloRequest) returns(HelloResponse);
  // 因为 rpc的服务必须要传一个参数,但是有时候我们不需要传参数,所以定义一个Empty的message
  rpc Ping(Empty) returns(Pong);
}

message HelloRequest {
  string name = 1;
}
message HelloResponse {
  string reply =1;
}

oproto/order.proto

syntax = "proto3";
option go_package = ".;proto"; // 此处不要忘了加入包的声明
message Empty {
}
// 定义一个Pong
message Pong {
  int32 code =1;
}

命令生成go文件

// 切换到相应路径下
protoc --go_out=. ./order.proto
protoc --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false  ./order.proto 
// base.proto也要生成
protoc --go_out=. ./base.proto 

client/main.go

package main

import (
	"context"
	"fmt"
	"go_test_learn/import_proto/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

func main() {

	conn, err := grpc.Dial("127.0.0.1:50052", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		panic("连接服务异常")
	}
	defer conn.Close()
	client := proto.NewHelloClient(conn)
	request := proto.Empty{}
	res, err := client.Ping(context.Background(), &request)
	if err != nil {
		panic("调用方法异常")
	}
	//打印出返回的数据
	fmt.Println(res.Code)
}

server/main.go

package main

import (
	"context"
	"fmt"
	"go_test_learn/import_proto/proto"
	"google.golang.org/grpc"
	"net"
)

type HelloServer struct {
}

func (s *HelloServer) Hello(context context.Context, request *proto.HelloRequest) (*proto.HelloResponse, error) {
	fmt.Println(request.Name) //此处打印出默认值
	return &proto.HelloResponse{Reply: "收到客户端的消息为:" + request.Name}, nil
}
func (s *HelloServer) Ping(context context.Context, request *proto.Empty) (*proto.Pong, error) {
	fmt.Println(request) //request是Empty的对象,没有值
	return &proto.Pong{Code: 100}, nil
}
func main() {
	g := grpc.NewServer()
	s := HelloServer{}
	proto.RegisterHelloServer(g, &s)
	lis, error := net.Listen("tcp", "0.0.0.0:50052")
	if error != nil {
		panic("启动服务异常")
	}
	g.Serve(lis)

}

4.2 引入内置的proto

proto/order.proto

syntax = "proto3";
import "base.proto";  // 导入另一个proto,就可以使用里面的Empty和Pong了
// 引入谷歌提供的,必须要使用protoc/include/google/protobuf内带empty.proto文件,否则报错
import "google/protobuf/empty.proto"; // 谷歌内置了empty给咱们用,按住control可以看源码,内有go_package是go包导入路径
option go_package = ".;proto";

service Hello{
  rpc Hello(HelloRequest) returns(HelloResponse);
  // 因为 rpc的服务必须要传一个参数,但是有时候我们不需要传参数,所以定义一个Empty的message
  //  rpc Ping(Empty) returns(Pong);
  // 此处使用必须用google.protobuf.Empty
  rpc Ping(google.protobuf.Empty) returns(Pong);
}

message HelloRequest {
  string name = 1;
}
message HelloResponse {
  string reply = 1;
}

oproto/order.proto

syntax = "proto3";
option go_package = ".;proto"; // 此处不要忘了加入包的声明

// 定义一个Pong
message Pong {
  int32 code =1;
}

命令生成go文件

// 切换到相应路径下
protoc --go_out=. ./order.proto
protoc --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false  ./order.proto 
// base.proto也要生成
protoc --go_out=. ./base.proto 

// 删除已下载的模块缓存
go clean --modcache

client/main.go

package main

import (
	"context"
	"fmt"
	"github.com/golang/protobuf/ptypes/empty" // empty使用这个包下的
	"go_test_learn/import_proto/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

func main() {

	conn, err := grpc.Dial("127.0.0.1:50052", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		panic("连接服务异常")
	}
	defer conn.Close()
	client := proto.NewHelloClient(conn)
	request := empty.Empty{}
	res, err := client.Ping(context.Background(), &request)
	if err != nil {
		panic("调用方法异常")
	}
	//打印出返回的数据
	fmt.Println(res.Code)
}

server/main.go

package main

import (
	"context"
	"fmt"
	"github.com/golang/protobuf/ptypes/empty" // empty使用这个包下的
	"go_test_learn/import_proto/proto"
	"google.golang.org/grpc"
	"net"
)

type HelloServer struct {
}

func (s *HelloServer) Hello(context context.Context, request *proto.HelloRequest) (*proto.HelloResponse, error) {
	fmt.Println(request.Name) //此处打印出默认值
	return &proto.HelloResponse{Reply: "收到客户端的消息为:" + request.Name}, nil
}
func (s *HelloServer) Ping(context context.Context, request *empty.Empty) (*proto.Pong, error) {
	fmt.Println(request) //request是Empty的对象,没有值
	return &proto.Pong{Code: 100}, nil
}
func main() {
	g := grpc.NewServer()
	s := HelloServer{}
	proto.RegisterHelloServer(g, &s)
	lis, error := net.Listen("tcp", "0.0.0.0:50052")
	if error != nil {
		panic("启动服务异常")
	}
	g.Serve(lis)

}

五 嵌套message对象

proto文件的message可以嵌套其他的message,修改order.proto如下

order.proto

syntax = "proto3";
import "base.proto";  // 导入另一个proto,就可以使用里面的Empty和Pong了
import "google/protobuf/empty.proto"; // 谷歌内置了empty给咱们用,按住control可以看源码,内有go_package是go包导入路径
option go_package = ".;proto";


service Hello{
  rpc Hello(HelloRequest) returns(HelloResponse);
  // 因为 rpc的服务必须要传一个参数,但是有时候我们不需要传参数,所以定义一个Empty的message
  //  rpc Ping(Empty) returns(Pong);
  // 此处使用必须用google.protobuf.Empty
  rpc Ping(google.protobuf.Empty) returns(Pong);
}

message HelloRequest {
  string name = 1;
}

message HelloResponse {
  message Result { // 嵌套Result,也可以放在message HelloResponse外面
    string code = 1;
    string msg = 2;
  }
  string reply = 1;
  Result data = 2;
}

命令生成go文件

// 切换到相应路径下
protoc --go_out=. ./order.proto
protoc --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false  ./order.proto 
// base.proto也要生成
protoc --go_out=. ./base.proto 

client/main.go

package main

import (
	"context"
	"fmt"
	"go_test_learn/import_proto/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

func main() {

	conn, err := grpc.Dial("127.0.0.1:50052", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		panic("连接服务异常")
	}
	defer conn.Close()
	client := proto.NewHelloClient(conn)
	request := proto.HelloRequest{Name: "lqz"}
	res, err := client.Hello(context.Background(), &request)
	if err != nil {
		panic("调用方法异常")
	}

	//message嵌套,可以嵌套获取
	fmt.Println(res.Data.Code)
	fmt.Println(res.Data.Msg)
}

server/main.go

package main

import (
	"context"
	"fmt"
	"github.com/golang/protobuf/ptypes/empty" // empty使用这个包下的
	"go_test_learn/import_proto/proto"
	"google.golang.org/grpc"
	"net"
)

type HelloServer struct {
}

func (s *HelloServer) Hello(context context.Context, request *proto.HelloRequest) (*proto.HelloResponse, error) {
	fmt.Println(request.Name) //此处打印出默认值
	return &proto.HelloResponse{
		Reply: "收到客户端的消息为:" + request.Name,
		Data: &proto.HelloResponse_Result{Code:"100",Msg: "成功"}, // 注意此处,嵌套的Result变成了HelloResponse_Result
	}, nil
}
func (s *HelloServer) Ping(context context.Context, request *empty.Empty) (*proto.Pong, error) {
	fmt.Println(request) //request是Empty的对象,没有值
	return &proto.Pong{Code: 100}, nil
}
func main() {
	g := grpc.NewServer()
	s := HelloServer{}
	proto.RegisterHelloServer(g, &s)
	lis, error := net.Listen("tcp", "0.0.0.0:50052")
	if error != nil {
		panic("启动服务异常")
	}
	g.Serve(lis)

}

六 enum枚举类型

syntax = "proto3";
option go_package = ".;proto";

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

message HelloRequest {
  string name = 1;
  Gender gen =2; // 使用枚举类型
}

message HelloResponse {
  string reply = 1;
}

// 定义枚举类型
enum Gender{
  Male = 0;
  Female = 1;
}

命令生成go文件

// 切换到相应路径下
protoc --go_out=. ./order.proto
protoc --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false  ./order.proto 

server/main.go

package main

import (
	"context"
	"fmt"
	"go_test_learn/import_proto/proto"
	"google.golang.org/grpc"
	"net"
)

type HelloServer struct {
}

func (s *HelloServer) Hello(context context.Context, request *proto.HelloRequest) (*proto.HelloResponse, error) {
	fmt.Println(request.Name) //此处打印出默认值
	return &proto.HelloResponse{
		Reply: "收到客户端的性别为:" + request.Gen.String(),
	}, nil
}

func main() {
	g := grpc.NewServer()
	s := HelloServer{}
	proto.RegisterHelloServer(g, &s)
	lis, error := net.Listen("tcp", "0.0.0.0:50052")
	if error != nil {
		panic("启动服务异常")
	}
	g.Serve(lis)

}

client/main.go

package main

import (
   "context"
   "fmt"
   "go_test_learn/import_proto/proto"
   "google.golang.org/grpc"
   "google.golang.org/grpc/credentials/insecure"
)

func main() {

   conn, err := grpc.Dial("127.0.0.1:50052", grpc.WithTransportCredentials(insecure.NewCredentials()))
   if err != nil {
      panic("连接服务异常")
   }
   defer conn.Close()
   client := proto.NewHelloClient(conn)
   // 使用枚举类型Gen: proto.Gender_Male,本质是int32
   request := proto.HelloRequest{Name: "lqz",Gen: proto.Gender_Male}
   res, err := client.Hello(context.Background(), &request)
   if err != nil {
      panic("调用方法异常")
   }

   fmt.Println(res.Reply)
}

七 map类型

order.proto

syntax = "proto3";
option go_package = ".;proto";


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

message HelloRequest {
  string name = 1;
  Gender gen =2; // 使用枚举类型
  map<string,string> info =3; // map类型,要指定key和value的类型
}

message HelloResponse {
  string reply = 1;
}

// 定义枚举类型
enum Gender{
  Male = 0;
  Female = 1;
}

命令生成go文件

// 切换到相应路径下
protoc --go_out=. ./order.proto
protoc --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false  ./order.proto 

server/main.go

package main

import (
   "context"
   "fmt"
   "go_test_learn/import_proto/proto"
   "google.golang.org/grpc"
   "net"
)

type HelloServer struct {
}

func (s *HelloServer) Hello(context context.Context, request *proto.HelloRequest) (*proto.HelloResponse, error) {
   fmt.Println(request.Name) //此处打印出默认值
   // 取出map类型
   return &proto.HelloResponse{
      Reply: "收到客户端的map:" + request.Info["name"],
   }, nil
}

func main() {
   g := grpc.NewServer()
   s := HelloServer{}
   proto.RegisterHelloServer(g, &s)
   lis, error := net.Listen("tcp", "0.0.0.0:50052")
   if error != nil {
      panic("启动服务异常")
   }
   g.Serve(lis)

}

client/main.go

package main

import (
   "context"
   "fmt"
   "go_test_learn/import_proto/proto"
   "google.golang.org/grpc"
   "google.golang.org/grpc/credentials/insecure"
)

func main() {

   conn, err := grpc.Dial("127.0.0.1:50052", grpc.WithTransportCredentials(insecure.NewCredentials()))
   if err != nil {
      panic("连接服务异常")
   }
   defer conn.Close()
   client := proto.NewHelloClient(conn)
   // 使用map类型,实质给映射成map[string]string
   request := proto.HelloRequest{Name: "lqz",
      Gen: proto.Gender_Male,
      Info:map[string]string{"name":"lqz","age":"19"},
   }
   res, err := client.Hello(context.Background(), &request)
   if err != nil {
      panic("调用方法异常")
   }

   fmt.Println(res.Reply)
}

八 内置的timestamp类型

order.proto

syntax = "proto3";
import "google/protobuf/timestamp.proto";  // 先引入
option go_package = ".;proto";


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

message HelloRequest {
  string name = 1;
  Gender gen =2; // 使用枚举类型
  map<string,string> info =3; // map类型,要指定key和value的类型
  google.protobuf.Timestamp now =4; // 使用Timestamp时间类型
}

message HelloResponse {
  string reply = 1;
}

// 定义枚举类型
enum Gender{
  Male = 0;
  Female = 1;
}

命令生成go文件

// 切换到相应路径下
protoc --go_out=. ./order.proto
protoc --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false  ./order.proto 

server/main.go

package main

import (
	"context"
	"fmt"
	"go_test_learn/import_proto/proto"
	"google.golang.org/grpc"
	"net"
)

type HelloServer struct {
}

func (s *HelloServer) Hello(context context.Context, request *proto.HelloRequest) (*proto.HelloResponse, error) {
	fmt.Println(request.Name) //此处打印出默认值
	// 取出map类型
	return &proto.HelloResponse{
		Reply: "收到客户端的时间:" + request.Now.AsTime().String(),
	}, nil
}

func main() {
	g := grpc.NewServer()
	s := HelloServer{}
	proto.RegisterHelloServer(g, &s)
	lis, error := net.Listen("tcp", "0.0.0.0:50052")
	if error != nil {
		panic("启动服务异常")
	}
	g.Serve(lis)

}

client/main.go

package main

import (
   "context"
   "fmt"
   "go_test_learn/import_proto/proto"
   "google.golang.org/grpc"
   "google.golang.org/grpc/credentials/insecure"
   "time"

   // "github.com/golang/protobuf/ptypes/timestamp" // 不导入这个路径
   timestamppb "google.golang.org/protobuf/types/known/timestamppb" //上指向这个,直接导入这个路径

)

func main() {

   conn, err := grpc.Dial("127.0.0.1:50052", grpc.WithTransportCredentials(insecure.NewCredentials()))
   if err != nil {
      panic("连接服务异常")
   }
   defer conn.Close()
   client := proto.NewHelloClient(conn)
   request := proto.HelloRequest{Name: "lqz",
      Gen: proto.Gender_Male,
      Info:map[string]string{"name":"lqz","age":"19"},
      Now: timestamppb.New(time.Now()), // timestamppb有New方法,传入Time对象即可
   }
   res, err := client.Hello(context.Background(), &request)
   if err != nil {
      panic("调用方法异常")
   }

   fmt.Println(res.Reply)
}
posted @ 2022-05-14 23:22  刘清政  阅读(503)  评论(0编辑  收藏  举报