-
pb开发-proto文件 以及 PB在 go项目中的开发过程
- 编写Proto文件
复制代码
syntax = "proto3";
message ComputeRequest {
int32 a = 1;
int32 b = 2;
}
message ComputeResponse {
int32 result = 1;
}
service Calculator {
rpc Add(ComputeRequest) returns (ComputeResponse) {}
}
该文件定义了两个消息类型 ComputeRequest
和 ComputeResponse
,以及一个名为 Calculator
的服务接口,该接口包含一个 Add
方法,用于将两个整数相加并返回结果。
- 生成Go代码
protoc-gen-go
插件将Proto文件编译为Go代码:
复制代码
protoc --go_out=. calculator.proto
这将在当前目录中生成一个名为 calculator.pb.go
的文件,其中包含所有通过Proto文件定义的消息类型和服务接口。
- 实现服务端
go复制代码
package main
import (
"context""log""net""google.golang.org/grpc"
pb "path/to/calculator.pb"
)
type server struct{}
func (s *server) Add(ctx context.Context, req *pb.ComputeRequest) (*pb.ComputeResponse, error) {
result := req.A + req.B
return &pb.ComputeResponse{Result: result}, nil
}
func main() {
lis, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatalf("failed to start server: %v", err)
}
srv := grpc.NewServer()
pb.RegisterCalculatorServer(srv, &server{})
log.Printf("server listening on %s", lis.Addr().String())
if err := srv.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
在上面的示例中,我们定义了一个名为 Server
的结构体,并实现了 Add
方法,该方法接收一个 ComputeRequest
请求对象并返回一个 ComputeResponse
响应对象,该响应对象包含两个数字的总和。然后,我们创建一个 grpc.Server
对象,并向其注册 Calculator
服务。最后,我们启动服务器并开始监听传入的连接。
- 实现客户端
go复制代码
package main
import (
"context""log""time""google.golang.org/grpc"
pb "path/to/calculator.pb"
)
func main() {
conn, err := grpc.Dial(":8080", grpc.WithInsecure())
if err != nil {
log.Fatalf("failed to connect: %v", err)
}
defer conn.Close()
c := pb.NewCalculatorClient(conn)
req := &pb.ComputeRequest{A: 10, B: 20}
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
res, err := c.Add(ctx, req)
if err != nil {
log.Fatalf("failed to add: %v", err)
}
log.Printf("result: %d", res.Result)
}
在上面的示例中,我们使用 grpc.Dial()
连接到服务器,并使用通过Proto文件生成的 NewCalculatorClient()
函数创建一个客户端对象。然后,我们创建一个 ComputeRequest
请求对象,并使用 context.WithTimeout()
创建一个有超时限制的上下文。最后,我们调用客户端的 Add
方法,并打印响应。
- 测试和调试
-
GRPC 结合实例 说明 其使用及原理
protobuf复制代码
syntax = "proto3";
message SortRequest {
repeated int32 nums = 1;
}
message SortResponse {
repeated int32 sorted_nums = 1;
}
service Sorter {
rpc Sort(SortRequest) returns (SortResponse) {}
}
上述Proto文件定义了两个消息类型 SortRequest
和 SortResponse
,以及一个名为 Sorter
的服务接口,该接口包含一个 Sort
方法,用于对传入的整数数组进行排序并返回已排序的数组。
根据这个Proto文件,我们可以使用protoc
编译器生成Go语言的代码,如下所示:
bash复制代码
protoc --go_out=. sorter.proto
执行以上命令会在当前目录下生成一个名为 sorter.pb.go
的文件,其中包含所有通过Proto文件定义的消息类型和服务接口。
接下来,我们需要编写一个服务器端程序,用于实现定义的 Sorter
服务接口。以下是示例代码:
go复制代码
package main
import (
"context""log""net""google.golang.org/grpc"
pb "path/to/sorter.pb"
)
type server struct{}
func (s *server) Sort(ctx context.Context, req *pb.SortRequest) (*pb.SortResponse, error) {
nums := req.GetNums()
// 使用快排算法对整数数组进行排序
quickSort(nums, 0, len(nums)-1)
return &pb.SortResponse{SortedNums: nums}, nil
}
func quickSort(nums []int32, left, right int) {
if left >= right {
return
}
pivot := partition(nums, left, right)
quickSort(nums, left, pivot-1)
quickSort(nums, pivot+1, right)
}
func partition(nums []int32, left, right int) int {
pivot := nums[right]
i := left
for j := left; j < right; j++ {
if nums[j] < pivot {
nums[i], nums[j] = nums[j], nums[i]
i++
}
}
nums[i], nums[right] = nums[right], nums[i]
return i
}
func main() {
lis, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatalf("failed to start server: %v", err)
}
srv := grpc.NewServer()
pb.RegisterSorterServer(srv, &server{})
log.Printf("server listening on %s", lis.Addr().String())
if err := srv.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
在以上示例中,我们首先定义了一个名为 Server
的结构体,并实现了 Sort
方法,该方法接收一个 SortRequest
请求对象,并对数组进行排序。然后,我们创建一个 grpc.Server
对象,并向其注册 Sorter
服务。最后,我们启动服务器并开始监听传入的连接。
现在,我们需要编写一个客户端程序,用于向服务器发送请求并获取响应。以下是示例代码:
go复制代码
package main
import (
"context""log""google.golang.org/grpc"
pb "path/to/sorter.pb"
)
func main() {
conn, err := grpc.Dial(":8080", grpc.WithInsecure())
if err != nil {
log.Fatalf("failed to connect: %v", err)
}
defer conn.Close()
c := pb.NewSorterClient(conn)
req := &pb.SortRequest{Nums: []int32{9, 3, 5, 2, 6, 4, 8, 7, 1}}
res, err := c.Sort(context.Background(), req)
if err != nil {
log.Fatalf("failed to sort: %v", err)
}
log.Printf("sorted nums: %v", res.SortedNums)
}
以上代码是一个gRPC客户端程序的示例,其主要流程如下:
- 使用
grpc.Dial
函数连接服务器,并返回一个连接对象conn
。 - 创建一个
SorterClient
对象c
,用于调用Sort
方法。这个对象由pb.NewSorterClient(conn)
创建,其中pb
是通过protoc
编译器生成的Go语言代码包。 - 创建一个
SortRequest
对象req
,将要排序的整数数组作为参数传递给Sort
方法。 - 调用
Sort
方法,并传递请求对象和上下文信息作为参数,获取响应结果和错误信息。 - 在获取到响应结果后,打印已排序的整数数组。
Sort
方法时,gRPC会自动处理序列化和反序列化,以及TCP连接的管理等底层细节,这样我们就可以专注于定义服务接口和业务逻辑,并使用gRPC轻松地构建分布式应用程序。
-
总结 关于grpc 客户端调用步骤有哪些 都做了什么事
- 创建
grpc.ClientConn
对象连接服务器。在本例中,使用了grpc.Dial
函数,指定了服务器地址和连接选项(WithInsecure()
表示不使用TLS安全连接)。 - 使用
pb.NewSorterClient(conn)
函数创建一个SorterClient
对象,其中pb
是通过 Protobuf 编译器生成的包含服务接口和消息的Go语言代码包。该客户端对象表示与服务器交互的实体。 - 创建一个
SortRequest
对象req
作为请求消息,将要排序的整数数组作为参数传递给Sort
方法。 - 调用
c.Sort(context.Background(), req)
方法,向服务器发送请求消息并获取响应信息。在这里,我们传递了上下文信息context.Background()
和请求消息req
作为参数,并从SorterClient
对象c
中调用了Sort
方法,该方法与定义的服务接口相匹配。 - gRPC处理序列化和反序列化,以及TCP连接的管理等底层细节。它将请求消息从结构化数据转换为二进制格式,并将其发送到服务器。然后,它接收来自服务器的响应消息,并将其从二进制格式转换为结构化数据。在此过程中,我们无需关心底层通信协议。
- 处理RPC调用的上下文信息,如超时时间、取消请求等。在这个例子中,使用了默认上下文信息
context.Background()
。 - 处理元数据(metadata),如身份验证令牌等。在这个例子中,没有使用自定义元数据。
- 处理错误信息,并在需要时重试请求。如果出现错误,gRPC将返回错误信息,并可以根据错误类型和错误码进行处理。在这个例子中,如果有错误,则会在控制台上打印错误信息并停止程序。
- 打印已排序的整数数组。如果一切顺利,我们将获得一个包含已排序的整数数组的响应结果。
- 创建
grpc.ClientConn
对象连接服务器。在本例中,使用了grpc.Dial
函数,指定了服务器地址和连接选项(WithInsecure()
表示不使用TLS安全连接)。 - 使用
pb.NewSorterClient(conn)
函数创建一个SorterClient
对象,其中pb
是通过 Protobuf 编译器生成的包含服务接口和消息的Go语言代码包。该客户端对象表示与服务器交互的实体。 - 创建一个
SortRequest
对象req
作为请求消息,将要排序的整数数组作为参数传递给Sort
方法。 - 调用
c.Sort(context.Background(), req)
方法,向服务器发送请求消息并获取响应信息。在这里,我们传递了上下文信息context.Background()
和请求消息req
作为参数,并从SorterClient
对象c
中调用了Sort
方法,该方法与定义的服务接口相匹配。 - gRPC处理序列化和反序列化,以及TCP连接的管理等底层细节。它将请求消息从结构化数据转换为二进制格式,并将其发送到服务器。然后,它接收来自服务器的响应消息,并将其从二进制格式转换为结构化数据。在此过程中,我们无需关心底层通信协议。
- 处理RPC调用的上下文信息,如超时时间、取消请求等。在这个例子中,使用了默认上下文信息
context.Background()
。 - 处理元数据(metadata),如身份验证令牌等。在这个例子中,没有使用自定义元数据。
- 处理错误信息,并在需要时重试请求。如果出现错误,gRPC将返回错误信息,并可以根据错误类型和错误码进行处理。在这个例子中,如果有错误,则会在控制台上打印错误信息并停止程序。
- 打印已排序的整数数组。如果一切顺利,我们将获得一个包含已排序的整数数组的响应结果。
- 创建
grpc.ClientConn
对象连接服务器。在本例中,使用了grpc.Dial
函数,指定了服务器地址和连接选项(WithInsecure()
表示不使用TLS安全连接)。 - 使用
pb.NewSorterClient(conn)
函数创建一个SorterClient
对象,其中pb
是通过 Protobuf 编译器生成的包含服务接口和消息的Go语言代码包。该客户端对象表示与服务器交互的实体。 - 创建一个
SortRequest
对象req
作为请求消息,将要排序的整数数组作为参数传递给Sort
方法。 - 调用
c.Sort(context.Background(), req)
方法,向服务器发送请求消息并获取响应信息。在这里,我们传递了上下文信息context.Background()
和请求消息req
作为参数,并从SorterClient
对象c
中调用了Sort
方法,该方法与定义的服务接口相匹配。 - gRPC处理序列化和反序列化,以及TCP连接的管理等底层细节。它将请求消息从结构化数据转换为二进制格式,并将其发送到服务器。然后,它接收来自服务器的响应消息,并将其从二进制格式转换为结构化数据。在此过程中,我们无需关心底层通信协议。
- 处理RPC调用的上下文信息,如超时时间、取消请求等。在这个例子中,使用了默认上下文信息
context.Background()
。 - 处理元数据(metadata),如身份验证令牌等。在这个例子中,没有使用自定义元数据。
- 处理错误信息,并在需要时重试请求。如果出现错误,gRPC将返回错误信息,并可以根据错误类型和错误码进行处理。在这个例子中,如果有错误,则会在控制台上打印错误信息并停止程序。
- 打印已排序的整数数组。如果一切顺利,我们将获得一个包含已排序的整数数组的响应结果。
-
Proto文件 基本的语法
- 使用
syntax
关键字指定Proto文件所使用的语法版本。目前最新的版本是proto3,可以使用以下语句指定:
protobuf复制代码
syntax = "proto3";
- 使用
package
关键字指定当前Proto文件所属的包名。包名应该采用反向DNS格式,并以分号结尾。例如:
protobuf复制代码
package com.example.grpc;
- 使用
message
关键字定义一个消息类型。一个消息类型可以包含多个字段,每个字段都有一个名称、类型和唯一的数字标识符。例如:
protobuf复制代码
message Person {
string name = 1;
int32 age = 2;
bool is_student = 3;
}
- 使用
repeated
关键字定义一个重复字段。重复字段可以出现零次或多次,并且会按照添加的顺序进行编码和解码。例如:
protobuf复制代码
message Book {
string title = 1;
repeated string author = 2;
}
- 使用
enum
关键字定义一个枚举类型。枚举类型包含多个命名的常量,每个常量都有一个名称和唯一的数字值。例如:
protobuf复制代码
enum Gender {
UNKNOWN = 0;
MALE = 1;
FEMALE = 2;
}
- 使用 使用上述是Proto文件中最基本的语法,通过这些语法我们可以定义自己的服务接口和消息结构,从而实现分布式系统间的通信。在实际开发中,可能还会涉及到嵌套类型、子消息、扩展等高级语法。
-
proto文件中不指定 syntax
syntax
关键字,gRPC会假设使用的是proto2语法,并默认采用一些proto2的规则和特性。这可能会导致与proto3语法不兼容的问题,因此建议始终在Proto文件中明确指定所使用的语法版本。
在Proto3语法中,一些proto2特性已被取消或更改,而新的特性也已被引入。例如:
- 在proto3语法中,所有字段都必须显式地分配一个数字标识符。
- proto3不再支持required关键字,因为它对于可选字段和必需字段之间的区别没有太大意义。
- proto3不再支持默认值,因为它可以导致歧义和错误。
- 在proto3语法中,枚举类型的第一个常量必须具有值0,以避免混淆和错误。
syntax
关键字,则可能会导致解析和编码出现问题。因此,在编写Proto文件时,应该始终指定正在使用的语法版本,以便在不同的平台和语言中实现正确的通信。
-
proto文件中 的repeated 在GO语言中是代表 集合吗
repeated
关键字定义的字段,会被转换为相应类型的切片(slice),例如:
protobuf复制代码
message Person {
string name = 1;
int32 age = 2;
repeated string email = 3;
}
在这个例子中,Person
消息类型有三个字段:name
、age
和 email
。其中 email
字段使用 repeated
关键字定义,表示一个人可以有零个或多个邮件地址。当使用 Protobuf 编译器生成 Go 代码时,将会自动生成具有以下签名的方法:
go复制代码
func (m *Person) GetEmail() []string
该方法返回一个字符串类型的切片,其中包含了所有的 email
字段值。
因此,repeated
关键字在Go语言中确实代表了一种集合/列表/数组类型,并且可以用切片来表示和访问。这种方式可以方便我们处理消息中包含多个值的情况,并且在不同的平台和语言之间实现兼容性。
-
proto文件中 optional 关键字
optional
关键字已被废弃,不再被支持。在之前的Proto2语法中,
例如,在Proto2语法中,可以使用 optional
关键字来定义一个可选字段:
protobuf复制代码
message Person {
optional string email = 1;
// ...
}
然而,这种方式虽然在语言层面上提供了一些便利,但在实际使用中可能会引起歧义和错误。因此,在Proto3语法中,optional
关键字已被取消,所有字段都默认为可选的,即可以包含零个或一个值。
-
proto文件中service 中的 option关键字
option
关键字用于为服务(service)或方法(method)指定附加选项。
这些选项可以影响代码生成、运行时行为和通信协议等方面。
在服务(service)级别上,可以使用 option
关键字来指定以下选项:
rpc.greeter
:用于指定RPC方法的名称前缀。例如,如果将此选项设置为“Hello”,则所有RPC方法的名称前缀都会自动添加“Hello”。cc_enable_arenas
:控制是否启用内存分配器(Memory Arena Allocator)。默认情况下,gRPC使用内存分配器来管理消息大小和生命周期,以提高性能。但是,对于某些平台和应用程序,可能需要禁用内存分配器。通过设置此选项,可以灵活地控制内存分配器的使用。
option
关键字定义服务级别选项的例子:
protobuf复制代码
service MyService {
option cc_enable_arenas = false;
rpc MyRpcMethod(MyRequest) returns(MyResponse) {}
}
在这个例子中,
我们使用 option
关键字在 MyService
服务中指定了一个选项:禁用内存分配器。
这将使gRPC在处理消息时不再使用内存分配器,从而提高代码兼容性和灵活性。
当然,在实际开发中,还可以根据需要在方法(method)级别上指定选项,
例如,可以使用 option
关键字指定每个方法的超时时间、重试次数、最大请求数等等。
这些选项可以提高系统的稳定性和可靠性,同时也为开发人员提供了更多的灵活性和自由度。
请解读一下 以下代码中的option 做了什么 service EmailConfigSvr { option (srpc.service_option_id) = 0x8628; //添加邮件配置 rpc AddEmailConfig(AddEmailConfigReq) returns (AddEmailConfigRsp) { option (srpc.method_option_id) = 0x01; } //更新邮件配置 rpc UpdateEmailConfig(UpdateEmailConfigReq) returns (UpdateEmailConfigRsp) { option (srpc.method_option_id) = 0x02; } //删除邮件配置 rpc DelEmailConfig(DelEmailConfigReq) returns (DelEmailConfigRsp) { option (srpc.method_option_id) = 0x03; } //批量查询邮箱配置 rpc BatchQueryEmailConfig (QueryEmailConfigReq) returns (QueryEmailConfigRsp) { option (srpc.method_option_id) = 0x04; } }
在这段代码中,option
关键字用于定义了 srpc.service_option_id
和 srpc.method_option_id
两个选项,它们会影响生成的 gRPC 代码的行为。具体来说:
srpc.service_option_id
:该选项定义了服务(service)级别的选项 ID,值为0x8628
。这个选项可以用来标识当前服务的版本、权限等信息,在运行时被解析和使用。例如,在微服务架构中,可能需要使用此选项指示服务的负载均衡策略、调用频率限制等信息。srpc.method_option_id
:该选项定义了方法(method)级别的选项 ID,分别对应四个 RPC 方法的不同取值:0x01
、0x02
、0x03
、0x04
。这些选项可以用来控制每个方法的行为、参数验证规则、超时时间等等。例如,AddEmailConfig
方法的选项是0x01
,表示该方法的行为是添加邮件配置,其他方法的选项与之类似。
option
关键字定义这些选项,我们可以在Proto文件中指定更多的属性和元数据,以便在生成gRPC代码时进行处理和使用。一些选项可以用于控制编码/解码过程、调试/追踪数据流、优化网络传输等方面,从而提高系统性能和可维护性。
option 可以做哪些事
option
关键字可以用于为Proto文件中的服务(service)或方法(method)定义元数据,从而影响代码生成、运行时行为和通信协议等方面。虽然选项并不直接影响消息的结构或语义,但它们提供了一种灵活和可扩展的方式来增加附加信息和属性。
具体来说,option
可以用于以下场景:
- 控制代码生成:使用
option
关键字可以控制编译器生成代码的方式和输出形式。例如,可以使用gogoproto.jsontag
选项来定义JSON标签,以控制序列化输出的格式;或者使用go_package
选项指定生成Go代码时的包名和路径。 - 定义元数据:使用
option
关键字可以为服务或方法定义元数据,例如版本号、权限、调用频率限制等信息。这些元数据可以在运行时被解析和使用,以便为系统提供更多的控制和保障。 - 指示运行时行为:使用
option
关键字可以指示运行时行为和规则,例如超时时间、请求重试次数、TLS设置等。这些选项可以用于优化网络传输、提高系统稳定性和可靠性等方面,从而满足不同业务需求。 - 扩展Protobuf语法:使用
option
关键字可以扩展Protobuf语法,添加新的类型、操作符和语法规则。这些扩展可以通过自定义插件或编写DSL脚本来实现,并与其他工具集成,从而提高代码质量和效率。
option
关键字是一个十分强大和灵活的特性,在Proto文件的设计和实现过程中都有着广泛的应用和价值。开发人员可以根据需要自由地定义和使用选项,以适应不同的场景和需求。
标签:option,err,gRPC,代码,pb,开发,使用,Go
From: https://www.cnblogs.com/shoshana-kong/p/17728230.html