zoukankan      html  css  js  c++  java
  • 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
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    python 包管理工具 pip 的配置
    Python 变量作用域 LEGB (下)—— Enclosing function locals
    Python 变量作用域 LEGB (上)—— Local,Global,Builtin
    2020 Java 面试题 小结 (答案慢慢补上,有错误请指出)
    mysql 根据日期(date)做年,月,日分组统计查询
    jvm指令
    正则表达式 分割地址 获取省市区详细地址
    .Net 异常记录
    WCF设计服务协议(一)
    plsql ORA-01789:查询块具有不正确的结果列数
  • 原文地址:https://www.cnblogs.com/xinliangcoder/p/15705153.html
Copyright © 2011-2022 走看看