Go - 如何編寫 ProtoBuf 外掛(二)?

新亮筆記發表於2021-12-18

前言

上篇文章《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 中使用。

推薦閱讀

相關文章