返回

Go - 如何编写 ProtoBuf 插件 (三) ?

后端

前言

上篇文章《Go - 如何编写 ProtoBuf 插件 (二) 》,分享了基于 自定义选项 定义了 interceptor 插件,然后在 helloworld.proto 中使用了插件,最后在 gRpc 服务器和客户端中应用拦截器。

在这篇文章中,我将继续分享如何使用 protoc-gen-go 工具生成 Go 代码,优化代码性能,并对 Go 插件进行调试。同时,我还将讨论如何使用 Go 插件实现代码转换,以及如何使用 gRPC-gateway 插件生成 RESTful API。最后,我还将介绍如何使用 protoc-gen-validate 插件进行数据验证,以及如何使用 protoc-gen-openapiv2 插件生成 OpenAPI 规范。

1. 如何使用 protoc-gen-go 工具生成 Go 代码

protoc-gen-go 工具可以将 ProtoBuf 定义的文件转换为 Go 代码。要使用此工具,您需要安装 protoc-gen-go,并将其添加到您的 PATH 环境变量中。

go install google.golang.org/protobuf/cmd/protoc-gen-go

安装完成后,您可以使用以下命令将 ProtoBuf 定义的文件转换为 Go 代码:

protoc --go_out=plugins=grpc:. helloworld.proto

此命令将生成一个名为 helloworld.pb.go 的 Go 文件,其中包含 ProtoBuf 定义的文件的 Go 代码。

2. 如何优化代码性能

为了优化代码性能,您可以使用以下技巧:

  • 使用 protoc-gen-go 的 --optimize_for=speed 选项。此选项将生成更快的代码,但可能需要更长的编译时间。
  • 使用 protoc-gen-go 的 --go_opt=paths=source_relative 选项。此选项将生成相对路径的 Go 代码,这可以减少编译时间。
  • 使用 protoc-gen-go 的 --go_opt=Mgoogle/protobuf/any.proto=github.com/golang/protobuf/ptypes/any 选项。此选项将生成使用 Google Cloud Platform 编写的 protobuf 消息的 Go 代码。

3. 如何对 Go 插件进行调试

要对 Go 插件进行调试,您可以使用以下步骤:

  1. 在您的 ProtoBuf 定义的文件中添加以下注释:
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: helloworld.proto
  1. 使用 protoc-gen-go 生成 Go 代码。
protoc --go_out=plugins=grpc:. helloworld.proto
  1. 在 Go 代码中添加以下代码:
import (
	"context"

	helloworld "github.com/example/helloworld/helloworld"
)

func main() {
	// Create a gRPC client.
	conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
	if err != nil {
		log.Fatalf("Failed to dial gRPC server: %v", err)
	}
	defer conn.Close()

	// Create a helloworld service client.
	client := helloworld.NewGreeterClient(conn)

	// Call the SayHello method.
	resp, err := client.SayHello(context.Background(), &helloworld.HelloRequest{
		Name: "World",
	})
	if err != nil {
		log.Fatalf("Failed to call SayHello: %v", err)
	}

	// Print the response.
	log.Printf("Greeting: %s", resp.Message)
}
  1. 使用以下命令运行 Go 代码:
go run main.go
  1. 使用以下命令连接到 gRPC 服务器:
grpcurl -plaintext localhost:50051 helloworld.Greeter.SayHello
  1. 使用以下命令发送 SayHello 请求:
{"name": "World"}
  1. 您应该会看到以下响应:
{
  "message": "Hello World!"
}

4. 如何使用 Go 插件实现代码转换

要使用 Go 插件实现代码转换,您可以使用以下步骤:

  1. 创建一个新的 Go 项目。
  2. 在您的项目中添加以下代码:
package main

import (
	"context"
	"fmt"

	helloworld "github.com/example/helloworld/helloworld"
)

func main() {
	// Create a gRPC client.
	conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
	if err != nil {
		log.Fatalf("Failed to dial gRPC server: %v", err)
	}
	defer conn.Close()

	// Create a helloworld service client.
	client := helloworld.NewGreeterClient(conn)

	// Call the SayHello method.
	resp, err := client.SayHello(context.Background(), &helloworld.HelloRequest{
		Name: "World",
	})
	if err != nil {
		log.Fatalf("Failed to call SayHello: %v", err)
	}

	// Print the response.
	fmt.Println(resp.Message)
}
  1. 在您的项目中添加一个名为 plugin.go 的文件,并添加以下代码:
package main

import (
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"

	helloworld "github.com/example/helloworld/helloworld"
	"google.golang.org/protobuf/proto"
)

func main() {
	// Create a gRPC client.
	conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
	if err != nil {
		log.Fatalf("Failed to dial gRPC server: %v", err)
	}
	defer conn.Close()

	// Create a helloworld service client.
	client := helloworld.NewGreeterClient(conn)

	// Call the SayHello method.
	resp, err := client.SayHello(context.Background(), &helloworld.HelloRequest{
		Name: "World",
	})
	if err != nil {
		log.Fatalf("Failed to call SayHello: %v", err)
	}

	// Write the response to a file.
	f, err := os.Create("helloworld.txt")
	if err != nil {
		log.Fatalf("Failed to create file: %v", err)
	}
	defer f.Close()

	if _, err := f.Write([]byte(resp.Message)); err != nil {
		log.Fatalf("Failed to write to file: %v", err)
	}

	// Read the response from the file.
	b, err := ioutil.ReadFile("helloworld.txt")
	if err != nil {
		log.Fatalf("Failed to read file: %v", err)
	}

	// Unmarshal the response.
	var newResp helloworld.HelloReply
	if err := proto.Unmarshal(b, &newResp); err != nil {
		log.Fatalf("Failed to unmarshal response: %v", err)
	}

	// Print the response.
	fmt.Println(newResp.Message)
}
  1. 使用以下命令运行 Go 代码:
go run plugin.go
  1. 您应该会看到以下输出:
Hello World!

5. 如何使用 gRPC-gateway 插件生成 RESTful API

要使用 gRPC-gateway 插件生成 RESTful API,您可以使用以下步骤:

  1. 安装 gRPC-gateway 插件。
go install github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
  1. 在您的 ProtoBuf 定义的文件中添加以下注释:
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: helloworld.proto

option java_multiple_files = true;
option java_package = "com.example.helloworld";
option java_outer_classname = "HelloWorldProto";
option go_package = "github.com/example/helloworld/helloworld";
option objc_class_prefix = "HelloWorld";
  1. 使用 protoc-gen-go 和 gRPC-gateway 插件生成 Go 代码。
protoc --go_out=plugins=grpc:. --grpc-gateway_out=logtostderr=true:. helloworld.proto
  1. 在您的项目中添加以下