potobuf 进阶操作

potobuf 进阶

protobuf 基本类型和默认值

protobuf类型和语言对应关系

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

.proto TypeNotesPython TypeGo Type
doublefloatfloat64
floatfloatfloat32
int32使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代intint32
uint32使用变长编码intuint32
uint64使用变长编码intuint64
sint32使用变长编码,这些编码在负值时比int32高效的多intint32
sint64使用变长编码,有符号的整型值。编码时比通常的int64高效。intint64
fixed32总是4个字节,如果数值总是比总是比228大的话,这个类型会比uint32高效。intuint32
fixed64总是8个字节,如果数值总是比总是比256大的话,这个类型会比uint64高效。intuint64
sfixed32总是4个字节intint32
sfixed64总是8个字节intint64
boolboolbool
string一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。strstring
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位机器上使用

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";   // mac 写法
option go_package = "./;proto";  // win上生成到当前路径,需要带./
// 定义一个服务,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语言的定义

// 基本使用
// win 和mac的小区别
option go_package = "./";  // win上生成到当前路径,需要带./
option go_package = "."; // mac上生成到当前路径,需要带.
// 这样会把go文件生成到当前路径,并且go文件的包名为proto
// 指定生成的go文件放到某个路径下
option go_package = "common/hello/proto/v1";
// 这样会把go文件生成到当前路径下的common/hello/proto/文件夹中,包名为v1
// 生成到公共位置---》多个服务使用同一个proto,生成到公共的位置
option go_package = "../../common/hello/proto/v1";

// 使用命令生成
protoc --go_out=. ./hello.proto
protoc --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false  ./hello.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

服务端客户端同步问题

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

顺序导致的错误

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: "lxx",
		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)


}

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

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

//client/proto/hello.proto
message HelloRequest {
  string name = 1;
  string gender = 2;
}
//server/proto/hello.proto
message HelloRequest {
  string name = 1;
}
// 程序正常运行, 但是只能解析 name 字段

import另一个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)

}

引入内置的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)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

go&Python

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值