首页 > 其他分享 >protobuf进阶

protobuf进阶

时间:2024-03-18 15:57:51浏览次数:24  
标签:protobuf proto grpc request context go main 进阶

目录

protobuf进阶

一、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选择你的语言的默认值的工作细节

3. 案例

(1)目录结构

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

(2)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;
}

(3)生成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

(4)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)
}

(5)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 顺序导致的错误

(1)目录结构

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

(2)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;
}

(3)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;
}

(4)生成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

(5)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)
}

(6)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

(1)目录结构

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

(2)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;
}

(3)oproto/order.proto

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

(4)命令生成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 

(5)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)
}

(5)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

(1)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;
}

(2)oproto/order.proto

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

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

(3)命令生成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

(4)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)
}

(6)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如下

(1)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;
}

(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 

(3)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)
}

(4)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;
}

(1)命令生成go文件

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

(2)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)

}

(3)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类型

(1)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;
}

(2)命令生成go文件

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

(3)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)

}

(4)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类型

(1)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;
}

(2)命令生成go文件

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

(3)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)

}

(4)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)
}

标签:protobuf,proto,grpc,request,context,go,main,进阶
From: https://www.cnblogs.com/Mcoming/p/18080571

相关文章

  • gRPC进阶
    目录一、grpcmetadata机制1.1proto1.2生成go文件1.3服务端1.4客户端二、grpc拦截器interceptor2.1服务端拦截器grpc.UnaryInterceptor(interceptor)2.2客户端拦截器2.3开源拦截器三、通过metadata+拦截器实现认证3.1自定义(1)服务端(2)客户端(3)proto3.2WithPerRPCCredential......
  • C语言自定义类型:枚举(C语言进阶)
    目录前言1、枚举类型定义2、枚举的优点3、枚举的使用结语前言    本篇文章讲解C语言自定义类型:枚举类型。    枚举顾名思义就是一一列举,把可能的值一一列举。像一周的周一到周日可以枚举;每年12个月,可以枚举。1、枚举类型定义enumDay//星期{ Mo......
  • aardio教程二) 进阶语法
    表(table)aardio中除了基础数据类型外,其他的复合对象都是table(例如类和名字空间等)。table可以用来存放aardio的任何对象,包括另一个table。在其他语言中的字典、列表、数组、集合映射等,在aardio中都使用table来实现。创建字典importconsole;vartab={a=123;......
  • C语言指针(适合C语言进阶者):一道题带你深入理解数组与指针的关系
    ......
  • HTML进阶版(表单标签、语义化标签、字符实体)
    雷迪斯andthe乡亲们 欢迎你们来到奇幻的编程世界  上一篇我们学习了(列表标签、表格标签)让我来回顾一下吧!列表标签无序列表最常用,有序列表偶尔用,自定义列表底部导航用表格标签①表格基本标签:table>tr>td②表格标题和表头单元格标签:table> caption......
  • 【Python使用】python高级进阶知识md总结第4篇:静态Web服务器-命令行启动动态绑定端口
    python高级进阶全知识知识笔记总结完整教程(附代码资料)主要内容讲述:操作系统,虚拟机软件,Ubuntu操作系统,Linux内核及发行版,查看目录命令,切换目录命令,绝对路径和相对路径,创建、删除文件及目录命令,复制、移动文件及目录命令,终端命令格式的组成,查看命令帮助。HTTP请求报文,HTTP响应报文......
  • <爬虫部署,进阶Docker>----第二章 安装Docker
    前言:安装docker---本章是只针对windows的Docker! 如果你需要你就往下看,不需要就换频道~正文:1.安装Docker前配置a.开启虚拟化功能(VT)  -如果你电脑有这个(虚拟化已启用)        直接跳过这一步;如果没有,那你就去对照自己电脑开启虚拟化; 相关链......
  • Spring Schedule定时任务进阶篇(调度器)
    SpringSchedule背后支持多种任务调度方案,如JDKTimer、concurrent包下的ScheduledExecutorService以及Quartz等。Spring通过封装这些底层实现,为开发者提供了统一的接口和配置方式来处理定时任务。接下来通过SpringBoot+数据库来实现根据数据库数据来动态管理我们的定时任务,我这......
  • JavaScript 进阶(一)
    一、作用域作用域(scope)规定了变量能够被访问的“范围”,离开了这个“范围”变量便不能被访问。作用域分为:局部作用域、全局作用域。1.1局部作用域局部作用域分为函数作用域和块作用域。1.函数作用域:在函数内部声明的变量只能在函数内部被访问,外部无法......
  • 算法进阶之路:十大经典排序算法详解与实践
    算法进阶之路:十大经典排序算法详解与实践在计算机科学的世界里,排序算法是基础且至关重要的一环。无论是数据库查询、数据分析还是日常的编程任务,高效的排序算法都能显著提升程序的性能。本文将带你深入了解十大经典排序算法,包括它们的原理、优缺点以及代码实现,帮助你在算法......