自学内容网 自学内容网

tRPC多个proto文件、optional、import等用法【2 使用进阶】

tRPC多个proto文件、optional、import等用法【2 使用进阶】

多个proto文件场景

  • 项目代码(欢迎star⭐️) :
    Github:https://github.com/ziyifast/ziyifast-code_instruction/tree/main/go-demo/go-trpc/02-complicated

有时我们为了结构清晰,会使用多个proto文件。比如一个classroom里会有多个学生。

  • classroom.proto
  • student.proto

classroom.proto里肯定是需要使用student.proto,因此我们需要使用import关键字导入,然后trpc create分别生成classroom.trpc.go、student.trpc.go文件。

1 编写proto文件

项目结构:
在这里插入图片描述

student.proto

syntax = "proto3";

package trpc.complicated;
option go_package="ziyi.com/go-demo/go-trpc/02-complicated/pb";

message Student {
  string name = 1;
  int32 age = 2;
}

classroom.proto

虽然goland import报红,这是因为我们没有配置goland识别proto文件的路径。但并不影响执行trpc create命令生成trpc.go文件。

在这里插入图片描述
或者我们也可以在goland配置proto文件路径:
在这里插入图片描述

syntax = "proto3";

package trpc.complicated;
option go_package="ziyi.com/go-demo/go-trpc/02-complicated/pb";

import "student.proto"; // 导入外部proto文件(因为两个proto文件都在同一个目录,因此这里可使用相对路径)

//定义教室服务
service ClassroomService {
  rpc GetInfo (Request) returns (Response) {}
}

message Request {
  int32 roomId = 1;
}

message Response {
  Classroom classroom = 1;
}

//定义教室struct
message Classroom {
  int32 id = 1;
  string name = 2;
  string address = 3;
  repeated Student students = 4;
}

2 trpc create生成trpc.go文件

# 生成classroom.pb.go、classroom.trpc.go文件
trpc create -p classroom.proto \
            --rpconly \
            --nogomod \
            --mock=false \
            --protodir . \
            -o .

# 生成student.pb.go文件
trpc create -p student.proto \
            --rpconly \
            --nogomod \
            --mock=false \
            --protodir . \
            -o .

# 解释:
# -p student.proto 指定要生成代码的 .proto 文件
# --rpconly 只生成 stub 代码
# --nogomod 不生成 go.mod 文件
# --mock=false 禁用 mock 代码生成
# --protodir . 指定 .proto 文件的搜索路径
# -o . 输出目录为当前目录

在这里插入图片描述
生成后项目结构:
在这里插入图片描述

如果发现报错: generate files from template inside create rpc stub err: template execute err: template: trpc.go.tpl:91:31: executing “trpc.go.tpl” at <gofulltypex .RequestType $.FileDescriptor>: error calling gofulltypex: invalid type:xxx。

  • 观察是否是.proto文件未加package xxxx;

3 编写server端

trpc_go.yaml

配置服务名称、监听地址、端口

server:
  service:
    #       服务名称
    - name: trpc.classroom
      #      监听地址
      ip: 127.0.0.1
      #      服务监听端口
      port: 8088

server.go

对外暴露服务

package main

import (
"context"
"fmt"
"trpc.group/trpc-go/tnet/log"
"trpc.group/trpc-go/trpc-go"
"ziyi.com/02-complicated/pb"
)

func main() {
trpc.ServerConfigPath = "/Users/ziyi/GolandProjects/ziyifast-code_instruction/go-demo/go-trpc/02-complicated/server/trpc_go.yaml"
server := trpc.NewServer()
pb.RegisterClassroomServiceService(server, &ClassRoomService{})
if err := server.Serve(); err != nil {
panic(err)
}
}

type ClassRoomService struct {
}

func (c *ClassRoomService) GetInfo(ctx context.Context, request *pb.Request) (*pb.Response, error) {
log.Info("【server】receive ", request.RoomId, " info...")
if request.RoomId != 1 {
return nil, fmt.Errorf("the classroom does not exist")
}
rsp := new(pb.Response)
room := &pb.Classroom{
Name:    "grade7_21",
Address: "北京市 朝阳区 大屯路 ",
Students: []*pb.Student{
&pb.Student{
Name: "小明",
Age:  18,
},
},
}
rsp.Classroom = room
return rsp, nil
}

4 编写client端

client.go

请求服务端

package main

import (
"context"
"trpc.group/trpc-go/trpc-go/client"
"ziyi.com/02-complicated/pb"
)

func main() {
cli := pb.NewClassroomServiceClientProxy(client.WithTarget("ip://localhost:8088"))
rsp, err := cli.GetInfo(context.TODO(), &pb.Request{RoomId: 1})
if err != nil {
panic(err)
}
println("【client】 receive ", rsp.Classroom.Name)
}

5 演示

启动服务端、客户端,客户端向服务端发起请求查询id为1的classroom信息。

在这里插入图片描述

其他字段使用场景

具体代码地址:

  • https://github.com/ziyifast/ziyifast-code_instruction/tree/main/go-demo/go-trpc/03-keyword

message:定义结构体struct

// message: 定义结构体,类比go中的type
message Request {
  // optional: 可选字段
  optional string reqCreateTime = 1;
  string name = 2;
  //tips:字段后的1、2、1001等id标识,不用连续,只要全局唯一即可。通常为了后续有字段扩展我们字段的id都会跳着定义。
  map<string, string> extInfo = 1001;
  
}

service:定义服务

// service : 定义服务
service KeywordService {
  rpc GetKeyword(Request) returns (Response);
  //...
}

optional:可选字段

如果字段为optional,且未设置值,在序列化时候则不会包含该字段,且不会给字段默认值

// message: 定义结构体,类比go中的type
message Request {
  // optional: 可选字段
  optional string reqCreateTime = 1;
  map<string, string> reqInfo = 2;
}

repeated:可重复字段=>列表(切片)

message Classroom{
  string name = 1;
  //repeated 列表(切片)
  repeated int32 studentIds = 2;
}

enum:定义枚举类

enum ResponseCode {
  OK = 0;
  FAIL = 1;
  INVALID_PARAM = 2;
}

validate:规则校验

定义校验validate.proto:

使用:

// 案例一:
// 账号信息
message Account {
  AccountType type = 1; 
  string id = 2 [(validate.rules).string.kstr = true]; // 账号id
}

//案例二:
message Req {
  Account account          = 1; 
  map<string, string> ext                 = 2; 
  repeated string variable_ids            = 3[(validate.rules).repeated = {min_items: 0, max_items: 100, items: {string: {kstr: true, min_len: 1, max_len: 100}}}]; 
}

extend:扩展字段

例如下面代码实现了 对google.protobuf.MessageOptions 的扩展,用于在消息级别应用验证规则。具体来说:

  • disabled:如果设置为 true,则会禁用此消息的所有验证规则,包括其支持验证的所有字段的验证规则。
  • ignored:如果设置为 true,则会跳过为此消息生成验证方法。

validate.proto:

// Validation rules applied at the message level
extend google.protobuf.MessageOptions {
  // Disabled nullifies any validation rules for this message, including any
  // message fields associated with it that do support validation.
  optional bool disabled = 1071;
  // Ignore skips generation of validation methods for this message.
  optional bool ignored = 1072;
}

oneof type:单选

oneof 关键字表示在 FieldRules 消息中只能选择其中一个字段。这意味着每个 FieldRules 实例只能定义一种类型的验证规则。例如,如果选择了 FloatRules,则其他字段(如 DoubleRules 或 StringRules)将不会生效。

// FieldRules encapsulates the rules for each type of field. Depending on the
// field, the correct set should be used to ensure proper validations.
message FieldRules {
  optional MessageRules message = 17;
  oneof type {
    // Scalar Field Types
    FloatRules    float    = 1;
    DoubleRules   double   = 2;
    Int32Rules    int32    = 3;
    Int64Rules    int64    = 4;
  }
}

Validate使用场景:校验字段

tRPC 验证器(Validator)是一种用于在 tRPC 通信过程中进行数据验证的工具,通过在 .proto 文件中定义验证规则(例如长度限制、格式检查等),确保客户端和服务器之间传递的数据符合预期的格式和约束条件。

安装protoc-gen-validate工具

# fetches this repo into $GOPATH
go get github.com/envoyproxy/protoc-gen-validate

下载之后如果发现依然无法执行protoc-gen-validate命令,解决办法:下载源码,手动编译。

  1. 下载源码:go install github.com/envoyproxy/protoc-gen-validate@latest
  2. 进入源码所在目录(如果配置了GOPATH,会直接下载到GOPATH/pkg/mod/github.com/…目录下):
  • 我的GOPATH:/Users/ziyi/go
  • 我下载的源码路径:/Users/ziyi/go/pkg/mod/github.com/envoyproxy/protoc-gen-validate@v1.1.0
  1. cd 进入源码目录,手动编译: sudo go build -o protoc-gen-validate main.go
  2. 编译后的二进制添加可执行权限:sudo chmod +x protoc-gen-validate
  3. 将二进制拷贝到/usr/bin下(或其他目录,只要该目录配置了环境变量,能在任何地方执行即可)

protoc-gen-validate可支持校验类型

官网地址:https://gitcode.com/gh_mirrors/pr/protoc-gen-validate/overview?utm_source=csdn_github_accelerator&isLogin=1

在这里插入图片描述

实战一:使用Validate默认规则

  • 官方文档: trpc validate官方文档
  • 全部代码:
    Github:https://github.com/ziyifast/ziyifast-code_instruction/tree/main/go-demo/go-trpc/04-validate

项目结构:

.
├── client
│   └── client.go
├── proto
│   ├── user_trpc.pb.go
│   ├── user.pb.go
│   ├── user.pb.validate.go
│   ├── user.proto
│   └── validate.proto
└── server
    └── server.go

在这里插入图片描述

1 编写user.proto

syntax = "proto3";

import "validate.proto";

option go_package = ".;proto";

service UserService {
  rpc GetUser(User) returns (User);
}

message User {
  // uid 是用户的唯一标识符,它必须大于999
  uint64 uid = 1 [(validate.rules).uint64.gt = 999];

  // email 是用户的电子邮件地址,它必须是一个有效的电子邮件地址
  string email = 2 [(validate.rules).string.email = true];

  // phone 是用户的电话号码,它必须符合中国大陆的手机号码格式
  string phone = 3 [(validate.rules).string = {pattern: "^1[3456789]\\d{9}$"}];
}

此时import语句会爆红,显示Cannot resolve import ‘validate.proto’,所以我们需要建立validate.proto文件。【注意:如果使用trpc 命令+ --validate参数,可忽略此步骤,trpc会拉取自己所依赖的validate.proto,原理:trpc-cmdline 工具内置了一份该文件。】
解决办法:

  1. 在本地GO Modules内找到 github.com/envoyproxy/protoc-gen-validate@v.xx.xx这个库中的validate/validate.proto文件,利用cv大法复制到proto/validate.proto文件中。
    在这里插入图片描述
  2. 从远程GitHub找到此项目该文件的代码:validate/validate.proto,利用cv大法复制到proto/validate.proto文件中。

2 执行命令生成对应go文件(x.validate.go)

trpc create -p user.proto \
            --rpconly \
            --nogomod \
            --mock=false \
            --protodir . \
            -o . \
            -- validate

3 server/server.go

package main

import (
"context"
"log"
"trpc.group/trpc-go/trpc-go"
"ziyi.com/04-validate/pb"
)

func main() {
//设置服务配置文件路径
trpc.ServerConfigPath = "/Users/ziyi/GolandProjects/ziyifast-code_instruction/go-demo/go-trpc/04-validate/server/trpc_go.yaml"
server := trpc.NewServer()
pb.RegisterUserServiceService(server, &UserService{})
if err := server.Serve(); err != nil {
panic(err)
}
}

type UserService struct {
}

func (s *UserService) GetUser(ctx context.Context, req *pb.User) (*pb.User, error) {
if err := req.ValidateAll(); err != nil {
return nil, err
}
log.Printf("pass.....")
return req, nil
}

trpc_go.yaml:

server:
  service:
    #       服务名称
    - name: trpc.validate
      #      监听地址
      ip: 127.0.0.1
      #      服务监听端口
      port: 8088

4 client/client.go

package main

import (
"context"
"trpc.group/trpc-go/trpc-go/client"
"ziyi.com/04-validate/pb"
)

func main() {
cli := pb.NewUserServiceClientProxy(client.WithTarget("ip://localhost:8088"))
req := new(pb.User)
req.Email = "123@qq.com"
req.Phone = "aaaa" //校验不通过
//req.Phone = "18173827109" //校验通过
req.Uid = 10001
_, err := cli.GetUser(context.Background(), req)
if err != nil {
panic(err)
}
}

5 验证

  • client中填写不满足格式的phone,校验不通过:
    在这里插入图片描述

实战二:自定义Validate规则

官方文档:https://go-kratos.dev/docs/component/middleware/validate/
代码地址:https://github.com/ziyifast/ziyifast-code_instruction/tree/main/go-demo/go-trpc/05-validate-self

步骤:

  1. 拷贝一份validate.proto到本地
  2. 修改validate.proto,添加或者修改规则
  3. 通过命令生成xxx.pb.validate.go之后,根据自己需求修改ValidateAll函数

案例:我现在需要定义一个新pstr选项,如果某个字段为String且为pstr,那么它的值只能在[“curry”, “tom”, "xujie]中三选一。
具体操作:
①本地添加规则在这里插入图片描述
②user.proto中设置pstr为true
在这里插入图片描述
③执行命令生成trpc代码以及对应校验文件
在这里插入图片描述
④修改user.pb.validate.go中ValidateAll方法
在这里插入图片描述
⑤编写client、server端代码,验证效果

  • 校验通过
    在这里插入图片描述
  • 校验失败
    在这里插入图片描述

参考文章:https://blog.csdn.net/MPY_3/article/details/140420543https://github.com/bufbuild/protoc-gen-validatehttps://pkg.go.dev/trpc.group/trpc/trpc-protocol/pb/go/trpc/validatehttps://go-kratos.dev/docs/component/middleware/validate/https://github.com/trpc-group/trpc-cmdline/blob/main/docs/examples/example-2/README.zh_CN.md#%E7%94%9F%E6%88%90-validatepbgo-%E6%96%87%E4%BB%B6


原文地址:https://blog.csdn.net/weixin_45565886/article/details/142282383

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!