前言

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

接上篇文章,继续分享。

定义插件

  1. // plugin/interceptor/options/interceptor.proto
  2. syntax = "proto3";
  3. package interceptor;
  4. option go_package = "./;interceptor/options";
  5. import "google/protobuf/descriptor.proto";
  6. extend google.protobuf.MethodOptions {
  7. optional MethodHandler method_handler = 63500;
  8. }
  9. extend google.protobuf.ServiceOptions {
  10. optional ServiceHandler service_handler = 63501;
  11. }
  12. message MethodHandler {
  13. optional string authorization = 1; // login token
  14. optional string whitelist = 2; // ip whitelist
  15. optional bool logger = 3; // logger
  16. }
  17. message ServiceHandler {
  18. optional string authorization = 1; // login token
  19. optional string whitelist = 2; // ip whitelist
  20. optional bool logger = 3; // logger
  21. }

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

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

使用插件

  1. // helloworld/helloworld.proto
  2. syntax = "proto3";
  3. package helloworld;
  4. option go_package = "./;helloworld";
  5. import "plugin/interceptor/options/interceptor.proto";
  6. service Greeter {
  7. option (interceptor.service_handler) = {
  8. authorization : "login_token",
  9. };
  10. rpc SayHello1 (HelloRequest) returns (HelloReply) {
  11. option (interceptor.method_handler) = {
  12. whitelist : "ip_whitelist",
  13. logger: true,
  14. };
  15. }
  16. rpc SayHello2 (HelloRequest) returns (HelloReply) {
  17. option (interceptor.method_handler) = {
  18. logger: false,
  19. };
  20. }
  21. }
  22. message HelloRequest {
  23. string name = 1;
  24. }
  25. message HelloReply {
  26. string message = 1;
  27. }

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

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

获取自定义选项

  1. // main.go
  2. // 演示代码
  3. package main
  4. import (
  5. "fmt"
  6. "strconv"
  7. _ "github.com/xinliangnote/protobuf/helloworld/gen"
  8. "github.com/xinliangnote/protobuf/plugin/interceptor/options"
  9. "google.golang.org/protobuf/proto"
  10. "google.golang.org/protobuf/reflect/protoreflect"
  11. "google.golang.org/protobuf/reflect/protoregistry"
  12. )
  13. func main() {
  14. protoregistry.GlobalFiles.RangeFiles(func(fd protoreflect.FileDescriptor) bool {
  15. services := fd.Services()
  16. for i := 0; i < services.Len(); i++ {
  17. service := services.Get(i)
  18. if serviceHandler, _ := proto.GetExtension(service.Options(), options.E_ServiceHandler).(*options.ServiceHandler); serviceHandler != nil {
  19. fmt.Println()
  20. fmt.Println("--- service ---")
  21. fmt.Println("service name: " + string(service.FullName()))
  22. if serviceHandler.Authorization != nil && *serviceHandler.Authorization != "" {
  23. fmt.Println("use interceptor authorization: " + *serviceHandler.Authorization)
  24. }
  25. fmt.Println("--- service ---")
  26. }
  27. methods := service.Methods()
  28. for k := 0; k < methods.Len(); k++ {
  29. method := methods.Get(k)
  30. if methodHandler, _ := proto.GetExtension(method.Options(), options.E_MethodHandler).(*options.MethodHandler); methodHandler != nil {
  31. fmt.Println()
  32. fmt.Println("--- method ---")
  33. fmt.Println("method name: " + string(method.FullName()))
  34. if methodHandler.Whitelist != nil && *methodHandler.Whitelist != "" {
  35. fmt.Println("use interceptor whitelist: " + *methodHandler.Whitelist)
  36. }
  37. if methodHandler.Logger != nil {
  38. fmt.Println("use interceptor logger: " + strconv.FormatBool(*methodHandler.Logger))
  39. }
  40. fmt.Println("--- method ---")
  41. }
  42. }
  43. }
  44. return true
  45. })
  46. }

输出:

  1. --- service ---
  2. service name: helloworld.Greeter
  3. use interceptor authorization: login_token
  4. --- service ---
  5. --- method ---
  6. method name: helloworld.Greeter.SayHello1
  7. use interceptor whitelist: ip_whitelist
  8. use interceptor logger: true
  9. --- method ---
  10. --- method ---
  11. method name: helloworld.Greeter.SayHello2
  12. use interceptor logger: false
  13. --- method ---

小结

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

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

推荐阅读