• Go 如何编写 ProtoBuf 插件(二)?


    前言

    上篇文章《Go - 如何编写 ProtoBuf 插件 (一) 》,分享了使用 proto3自定义选项 可以实现插件的编写,说到基于 MethodOptionsServiceOptions 选项去实现 methodservice 自定义设置拦截器。

    接上篇文章,继续分享。

    定义插件

    // plugin/interceptor/options/interceptor.proto
    
    syntax = "proto3";
    
    package interceptor;
    
    option go_package = "./;interceptor/options";
    
    import "google/protobuf/descriptor.proto";
    
    extend google.protobuf.MethodOptions {
      optional MethodHandler method_handler = 63500;
    }
    
    extend google.protobuf.ServiceOptions {
      optional ServiceHandler service_handler = 63501;
    }
    
    message MethodHandler {
      optional string authorization = 1; // login token
      optional string whitelist = 2;     // ip whitelist
      optional bool logger = 3;          // logger      
    }
    
    message ServiceHandler {
      optional string authorization = 1; // login token
      optional string whitelist = 2;     // ip whitelist
      optional bool logger = 3;          // logger
    }
    

    接下来根据 interceptor.proto 生成 interceptor.pb.go

    // 生成 interceptor.pb.go
    // 使用的 protoc --version 为 libprotoc 3.18.1
    // 使用的 protoc-gen-go --version 为 protoc-gen-go v1.27.1
    // 在 plugin/interceptor/options 目录下执行 protoc 命令
    
    protoc --go_out=. interceptor.proto
    

    使用插件

    // helloworld/helloworld.proto
    
    syntax = "proto3";
    
    package helloworld;
    
    option go_package = "./;helloworld";
    
    import "plugin/interceptor/options/interceptor.proto";
    
    service Greeter {
      option (interceptor.service_handler) = {
        authorization : "login_token",
      };
    
      rpc SayHello1 (HelloRequest) returns (HelloReply) {
        option (interceptor.method_handler) = {
          whitelist : "ip_whitelist",
          logger: true,
        };
      }
    
      rpc SayHello2 (HelloRequest) returns (HelloReply) {
        option (interceptor.method_handler) = {
          logger: false,
        };
      }
    }
    
    message HelloRequest {
      string name = 1;
    }
    
    message HelloReply {
      string message = 1;
    }
    

    接下来根据 helloworld.proto 生成 helloworld.pb.go

    // 生成 helloworld.pb.go
    // 使用的 protoc --version 为 libprotoc 3.18.1
    // 使用的 protoc-gen-go --version 为 protoc-gen-go v1.27.1
    // 在根目录下执行 protoc 命令
    
    protoc --go_out=helloworld/gen helloworld/helloworld.proto
    

    获取自定义选项

    // main.go
    // 演示代码
    
    package main
    
    import (
    	"fmt"
    	"strconv"
    
    	_ "github.com/xinliangnote/protobuf/helloworld/gen"
    	"github.com/xinliangnote/protobuf/plugin/interceptor/options"
    
    	"google.golang.org/protobuf/proto"
    	"google.golang.org/protobuf/reflect/protoreflect"
    	"google.golang.org/protobuf/reflect/protoregistry"
    )
    
    func main() {
    	protoregistry.GlobalFiles.RangeFiles(func(fd protoreflect.FileDescriptor) bool {
    		services := fd.Services()
    		for i := 0; i < services.Len(); i++ {
    			service := services.Get(i)
    			if serviceHandler, _ := proto.GetExtension(service.Options(), options.E_ServiceHandler).(*options.ServiceHandler); serviceHandler != nil {
    				fmt.Println()
    				fmt.Println("--- service ---")
    				fmt.Println("service name: " + string(service.FullName()))
    
    				if serviceHandler.Authorization != nil && *serviceHandler.Authorization != "" {
    					fmt.Println("use interceptor authorization: " + *serviceHandler.Authorization)
    				}
    				fmt.Println("--- service ---")
    			}
    
    			methods := service.Methods()
    			for k := 0; k < methods.Len(); k++ {
    				method := methods.Get(k)
    				if methodHandler, _ := proto.GetExtension(method.Options(), options.E_MethodHandler).(*options.MethodHandler); methodHandler != nil {
    					fmt.Println()
    					fmt.Println("--- method ---")
    					fmt.Println("method name: " + string(method.FullName()))
    					if methodHandler.Whitelist != nil && *methodHandler.Whitelist != "" {
    						fmt.Println("use interceptor whitelist: " + *methodHandler.Whitelist)
    					}
    
    					if methodHandler.Logger != nil {
    						fmt.Println("use interceptor logger: " + strconv.FormatBool(*methodHandler.Logger))
    					}
    
    					fmt.Println("--- method ---")
    				}
    			}
    		}
    
    		return true
    	})
    }
    

    输出:

    --- service ---
    service name: helloworld.Greeter
    use interceptor authorization: login_token
    --- service ---
    
    --- method ---
    method name: helloworld.Greeter.SayHello1
    use interceptor whitelist: ip_whitelist
    use interceptor logger: true
    --- method ---
    
    --- method ---
    method name: helloworld.Greeter.SayHello2
    use interceptor logger: false
    --- method ---
    

    小结

    本文主要内容是基于 自定义选项 定义了 interceptor 插件,然后在 helloworld.proto 中使用了插件,最后在 golang 代码中获取到使用的插件信息。

    接下来,要对获取到的插件信息进行使用,主要用在 grpc.ServerOption 中,例如在 grpc.UnaryInterceptorgrpc.StreamInterceptor 中使用。

    推荐阅读

    作者:新亮笔记(关注公众号,可申请添加微信好友)
    出处:https://www.cnblogs.com/xinliangcoder
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    10/28总结
    10/27总结
    10/26总结
    10/25总结
    10/24总结
    毕业设计第二周整理规划
    毕业设计第一周第五天完成情况汇总
    毕业设计第一周第四天完成情况汇总
    毕业设计第一周第三天完成情况汇总
    毕业设计第一周第二天完成情况汇总
  • 原文地址:https://www.cnblogs.com/xinliangcoder/p/15705153.html
Copyright © 2020-2023  润新知