«Если рабочий хочет хорошо выполнять свою работу, он должен сначала заточить свои инструменты» — Конфуций, «Аналитики Конфуция. Лу Лингун»
титульная страница > программирование > RPC Action EPU Использование Protobuf и создание собственного плагина

RPC Action EPU Использование Protobuf и создание собственного плагина

Опубликовано 8 ноября 2024 г.
Просматривать:967

RPC Action EPUsing Protobuf and Creating a Custom Plugin

В предыдущей статье я реализовал простой интерфейс RPC, используя пакет net/rpc, и опробовал кодировку Gob, идущую в комплекте с net/rpc, и кодировку JSON, чтобы изучить некоторые основы Golang. РПК. В этом посте я объединим net/rpc с protobuf и создам свой плагин protobuf, который поможет нам генерировать код, так что давайте начнем.

Эта статья была впервые опубликована в плане Medium MPP. Если вы являетесь пользователем Medium, подписывайтесь на меня на Medium. Большое спасибо.

Должно быть, во время работы мы использовали gRPC и protobuf, но они не связаны. gRPC можно закодировать с использованием JSON, а protobuf можно реализовать на других языках.

Protocol Buffers (Protobuf) – это бесплатный кроссплатформенный формат данных с открытым исходным кодом, используемый для сериализации структурированных данных. Это полезно при разработке программ, которые взаимодействуют друг с другом по сети или для хранения данных. Этот метод включает в себя язык описания интерфейса, который описывает структуру некоторых данных, и программу, которая генерирует исходный код на основе этого описания для создания или анализа потока байтов, представляющего структурированные данные.

Пример использования protobuf

Сначала мы пишем прото-файл hello-service.proto, который определяет сообщение «String»

syntax = "proto3";
package api;
option  go_package="api";

message String {
  string value = 1;
}

Затем используйте утилиту protoc для создания кода Go для строки сообщения

protoc --go_out=. hello-service.proto

Затем мы модифицируем аргументы функции Hello, чтобы использовать строку, сгенерированную файлом protobuf.

type HelloServiceInterface = interface {  
    Hello(request api.String, reply *api.String) error  
}  

Использование ничем не отличается от предыдущего, даже не так удобно, как непосредственное использование строки. Так почему же нам следует использовать protobuf? Как я уже говорил ранее, использование Protobuf для определения языково-независимых интерфейсов и сообщений службы RPC, а затем использование инструмента protoc для генерации кода на разных языках — вот в чем его реальная ценность. Например, используйте официальный плагин protoc-gen-go для генерации кода gRPC.

protoc --go_out=plugins=grpc. hello-service.proto

Система плагинов для протокола

Чтобы генерировать код из файлов protobuf, мы должны установить protoc , но протокол не знает, какой у нас целевой язык, поэтому нам нужны плагины, которые помогут нам генерировать код. как работает система плагинов protoc? В качестве примера возьмите приведенный выше grpc.

Здесь есть параметр --go_out. Поскольку плагин, который мы вызываем, — protoc-gen-go, параметр называется go_out; если бы имя было XXX, параметр назывался бы XXX_out.

Когда протокол protoc запущен, он сначала анализирует файл protobuf и генерирует набор описательных данных, закодированных в протокольных буферах. Сначала он определит, включен ли плагин go в protoc, а затем попытается найти protoc-gen-go в $PATH, и если он не сможет его найти, он сообщит об ошибке, а затем запустит protoc-gen-go. protoc-gen-go и отправляет данные описания команде плагина через стандартный ввод. После того, как плагин сгенерирует содержимое файла, он затем вводит данные, закодированные в буфере протокола, на стандартный вывод, чтобы сообщить protoc о необходимости создания конкретного файла.

плагины=grpc — это плагин, который поставляется с protoc-gen-go для его вызова. Если вы его не используете, он будет генерировать только сообщение в Go, но вы можете использовать этот плагин для генерации кода, связанного с grpc.

Настройка плагина протокола

Если мы добавим синхронизацию интерфейса Hello в protobuf, сможем ли мы настроить плагин protoc для прямой генерации кода?

syntax = "proto3";  
package api;  
option  go_package="./api";  
service HelloService {  
  rpc Hello (String) returns (String) {}  
}  
message String {  
  string value = 1;
}

Цель

В этой статье моей целью было создать плагин, который затем можно было бы использовать для генерации кода на стороне сервера и клиента RPC, который выглядел бы примерно так.

// HelloService_rpc.pb.go
type HelloServiceInterface interface {  
    Hello(String, *String) error  
}  

func RegisterHelloService(  
    srv *rpc.Server, x HelloServiceInterface,  
) error {  
    if err := srv.RegisterName("HelloService", x); err != nil {  
       return err  
    }  
    return nil  
}  

type HelloServiceClient struct {  
    *rpc.Client  
}  

var _ HelloServiceInterface = (*HelloServiceClient)(nil)  

func DialHelloService(network, address string) (  
    *HelloServiceClient, error,  
) {  
    c, err := rpc.Dial(network, address)  
    if err != nil {  
       return nil, err  
    }  
    return &HelloServiceClient{Client: c}, nil  
}  

func (p *HelloServiceClient) Hello(  
    in String, out *String,  
) error {  
    return p.Client.Call("HelloService.Hello", in, out)  
}

Это изменит наш бизнес-код и будет выглядеть следующим образом

// service
func main() {  
    listener, err := net.Listen("tcp", ":1234")  
    if err != nil {  
       log.Fatal("ListenTCP error:", err)  
    }  
    _ = api.RegisterHelloService(rpc.DefaultServer, new(HelloService))  
    for {  
       conn, err := listener.Accept()  
       if err != nil {  
          log.Fatal("Accept error:", err)  
       }  
       go rpc.ServeConn(conn)  
    }  
}  

type HelloService struct{}  

func (p *HelloService) Hello(request api.String, reply *api.String) error {  
    log.Println("HelloService.proto Hello")  
    *reply = api.String{Value: "Hello:"   request.Value}  
    return nil  
}
// client.go
func main() {  
    client, err := api.DialHelloService("tcp", "localhost:1234")  
    if err != nil {  
       log.Fatal("net.Dial:", err)  
    }  
    reply := &api.String{}  
    err = client.Hello(api.String{Value: "Hello"}, reply)  
    if err != nil {  
       log.Fatal(err)  
    }  
    log.Println(reply)  
}

Исходя из сгенерированного кода, наша рабочая нагрузка уже намного меньше и вероятность ошибки уже очень мала. Хорошее начало.

На основе приведенного выше API-кода мы можем получить файл шаблона:

const tmplService = `  
import (  
    "net/rpc")  
type {{.ServiceName}}Interface interface {  
func Register{{.ServiceName}}(  
    if err := srv.RegisterName("{{.ServiceName}}", x); err != nil {        return err    }    return nil}  
    *rpc.Client}  
func Dial{{.ServiceName}}(network, address string) (  
{{range $_, $m := .MethodList}}  
    return p.Client.Call("{{$root.ServiceName}}.{{$m.MethodName}}", in, out)}  
`

Весь шаблон понятен, и в нем есть несколько заполнителей, таких как имя метода, имя службы и т. д., о которых мы поговорим позже.

Как разработать плагин?

Google выпустил API 1 языка Go, в котором представлен новый пакет google.golang.org/protobuf/compile R/protogen, который значительно снижает сложность разработки плагинов:

  1. Прежде всего, мы создаем проект языка go, например protoc-gen-go-spprpc
  2. Затем нам нужно определить protogen.Options, затем вызвать его метод Run и передать обратный вызов func(*protogen.Plugin). Это конец основного кода процесса.
  3. Мы также можем установить параметр ParamFunc для protogen.Options, чтобы protogen автоматически анализировал для нас параметры, передаваемые из командной строки. Такие операции, как чтение и декодирование информации protobuf из стандартного ввода, кодирование входной информации в protobuf и запись стандартного вывода, выполняются protogen. Что нам нужно сделать, так это взаимодействовать с protogen.Plugin для реализации логики генерации кода.

Самое важное для каждого сервиса — это имя сервиса, а затем у каждого сервиса есть набор методов. Для метода, определенного службой, наиболее важным является имя метода, а также имя входного параметра и тип выходного параметра. Давайте сначала определим ServiceData для описания метаинформации сервиса:

// ServiceData 
type ServiceData struct {  
    PackageName string  
    ServiceName string  
    MethodList  []Method  
}
// Method 
type Method struct {  
    MethodName     string  
    InputTypeName  string  
    OutputTypeName string  
}

Затем следует основная логика, логика генерации кода и, наконец, вызов tmpl для генерации кода.

func main() {  
    protogen.Options{}.Run(func(gen *protogen.Plugin) error {  
       for _, file := range gen.Files {  
          if !file.Generate {  
             continue  
          }  
          generateFile(gen, file)  
       }  
       return nil  
    })  
}  

// generateFile function definition
func generateFile(gen *protogen.Plugin, file *protogen.File) {  
    filename := file.GeneratedFilenamePrefix   "_rpc.pb.go"  
    g := gen.NewGeneratedFile(filename, file.GoImportPath)  
    tmpl, err := template.New("service").Parse(tmplService)  
    if err != nil {  
       log.Fatalf("Error parsing template: %v", err)  
    }  
    packageName := string(file.GoPackageName)  
// Iterate over each service to generate code
    for _, service := range file.Services {  
       serviceData := ServiceData{  
          ServiceName: service.GoName,  
          PackageName: packageName,  
       }  
       for _, method := range service.Methods {  
          inputType := method.Input.GoIdent.GoName  
          outputType := method.Output.GoIdent.GoName  

          serviceData.MethodList = append(serviceData.MethodList, Method{  
             MethodName:     method.GoName,  
             InputTypeName:  inputType,  
             OutputTypeName: outputType,  
          })  
       }  
// Perform template rendering
       err = tmpl.Execute(g, serviceData)  
       if err != nil {  
          log.Fatalf("Error executing template: %v", err)  
       }  
    }  
}

Плагин отладки

Наконец, мы помещаем скомпилированный двоичный исполняемый файл protoc-gen-go-spprpc в $PATH, а затем запускаем protoc для генерации нужного кода.

protoc --go_out=.. --go-spprpc_out=.. HelloService.proto

Поскольку запуск protoc-gen-go-spprpc должен зависеть от protoc, его немного сложно отладить. Мы можем использовать

fmt.Fprintf(os.Stderr, "Fprintln: %v\n", err)

Чтобы распечатать журнал ошибок для отладки.

Краткое содержание

Это все, что есть в этой статье. Сначала мы реализовали вызов RPC с использованием protobuf, а затем создали плагин protobuf, который поможет нам сгенерировать код. Это открывает нам возможность изучить protobuf RPC и является нашим путем к глубокому пониманию gRPC. Я надеюсь, что каждый сможет освоить эту технологию.

Ссылка

  1. https://taoshu.in/go/create-protoc-plugin.html
  2. https://chai2010.cn/advanced-go-programming-book/ch4-rpc/ch4-02-pb-intro.html
Заявление о выпуске Эта статья воспроизведена по адресу: https://dev.to/huizhou92/rpc-action-ep2-using-protobuf-and-creating-a-custom-plugin-2j9j?1. В случае нарушения прав обращайтесь по адресу Study_golang@163. .com, чтобы удалить его
Последний учебник Более>

Изучайте китайский

Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.

Copyright© 2022 湘ICP备2022001581号-3