Три вида передачи опытных gRPC динамических параметров

Go

gRPCэто инфраструктура удаленного вызова, которая используетProtobufВ качестве носителя информации для завершения передачи данных между клиентом и сервером. О том, как определитьProtobufновости, сборкаgRPCслужил раньшесерия статейбыли упомянуты вgRPCиProtobufКак передать динамические параметры в процессе.

Прежде всего, позвольте мне объяснить, что упомянутые здесь динамические параметры относятся к определениюProtobufПоле составного типа конкретного контента не может быть определено при отправке сообщения.Проще говоря, именно это поле в сообщении.Мы хотим передать аналогичныйJSONобъект,Mapсоставные значения, такие как словари, структуры и т. д., ноJSONКакие поля есть, какого типа значение каждого поля илиMapТип ключей словаря в нашем пользовательском сообщении Shihai не может быть определен (можно определить, может ли определить вложенное подсообщение, выходящее за рамки этой статьи), что такоеProtobufПоля сообщения называются динамическими параметрами.

за прохождениеProtobufСтандартного решения требования передачи динамических параметров в официальной документации нет.bytes,Map<string, string>а такжеproto.Structэти троеProtobufТип реализации поля сообщения, каждый метод также имеет свои преимущества и недостатки, если вы знаете лучший план реализации, оставьте сообщение в комментариях для обсуждения.

Давайте посмотрим, как использовать эти три типа полей сообщений для динамической передачи параметров.

Передайте параметр объекта JSON, используя байты

ProtobufвнутреннийbytesПоля типа кодируются какGoСоответствующий код после кодаGoбайтовый срез в[]byteтип. Таким образом, мы можем определить тип поля динамического параметра какbytesтипа, чтобы клиент поставилJSONПосле того, как объект передан на сервер, сервер может напрямуюJSONОбъект выполняет операцию декодирования, устраняя необходимость вstringприбыть[]byteпреобразование типов.

Например, в следующемProtobufв определении сообщенияinfoТип поляbytes

rpc UpdateRecord (UpdateRecordRequest) returns (UpdateRecordReply) {
}
    
message UpdateRecordRequest {
    int64 id = 1;
    bytes info = 2;
}

Затем при использовании этого метода gRPC, когда клиент использует его, он напрямую ставитinfoпередача данныхjson.MarshalПосле кодирования его можно передать на сервер.

info := struct {
  name string
  age  int
} {
   name: "James",
   age: 20,
}
jsonInfo, _ := json.Marshal(info)
_ := AppService.UpdateRecord(&AppService.UpdateRecordRequest{id: 2, info: jsonInfo})

На стороне сервера можно добавить проверку параметра, чтобы убедиться, что он передан правильно.JSONобъект.

func IsJSON(in []byte) bool {
 var js map[string]interface{}
 return json.Unmarshal(in, &js) == nil

}

После проверки вы можете декодировать динамические параметры в соответствии с фактическими требованиями использования.JSONОбъект разрешается в определенную структурную переменную.

type Info struct {
 name string `json:"name"`
 age int `json:"id"`
}

func (s server) UpdateRecord(ctx context.Context, reqeust *AppService.UpdateRecordRequest) (reply *AppService.UpdateRecordReply, err error) {
  if !isJson(req.Info) {
    // 错误处理
    ...
  }
  v := Info{}
  json.Unmarshal(req.Info, $v)
}

Я обычно использую этот метод, он кажется более удобным, единственная проблема в том, что каждое место, которое использует динамические параметры, должно определять и анализировать само по себеJSONТип структуры, соответствующий объекту.

Передайте динамические параметры, используя тип карты

Если вы не хотите проходитьJSONобъект для передачи параметров, другое решение, о котором часто можно подумать, состоит в том, чтобы определить тип поля параметра как словарь, и каждый вызов может быть установлен в соответствии с различными потребностями.Key-Valueправильно.Protobufслучилось иметьMapтип.

map<key_type, value_type> map_field = N;

Но есть одно но, в определенииMapтип, тип значения должен быть фиксированным и не поддерживает такие вещи, какmap[string]interface{}такой тип значения.Таким образом, этот метод обычно используется, когда можно определить тип значения параметра словаря., в противном случае, если он определен какmap<string, string>Если вы хотите передать целочисленное поле, клиенту также необходимо сначала преобразовать данные из целочисленного типа в строковый.

Используйте proto.Struct для передачи динамических параметров структуры

В некоторых источниках упоминается использованиеProtobufОн поставляется с составным типомproto.StructПередавая параметры динамического типа, преимущество его использования в том, что он выглядит какProtobufВстроенная поддержка динамически типизированных данных, которую вы можете использоватьProtobufсобственный пакетjsonpbзавершить отJSONприбытьproto.Structпреобразование между.

использоватьproto.Structтип должен быть вprotoСначала файл вводит определение своего типа, как показано ниже.

syntax = "proto3";
package messages;
import "google/protobuf/struct.proto";

service UserService {
    rpc SendJson (SendJsonRequest) returns (SendJsonResponse) {}
}

message SendJsonRequest {
    string UserID = 1;
    google.protobuf.Struct Details = 2;
}

message SendJsonResponse {
    string Response = 1;
}

пройти черезproto.StructБазовые определения исходного кода могут видеть, что на самом деле это человек по имениStruct, который содержит толькоfiledsизMapполе типа, черезProtobufизOneofособенность указываетMapДиапазон типов значения аппроксимируется для завершения поддержки динамического типа.

message Struct {
  // Unordered map of dynamically typed values.
  map<string, Value> fields = 1;
}

message Value {
  // The kind of value.
  oneof kind {
    // Represents a null value.
    NullValue null_value = 1;
    // Represents a double value.
    double number_value = 2;
    // Represents a string value.
    string string_value = 3;
    // Represents a boolean value.
    bool bool_value = 4;
    // Represents a structured value.
    Struct struct_value = 5;
    // Represents a repeated `Value`.
    ListValue list_value = 6;
  }
}

Так действуйте при использованииproto.StructЧто-то вроде манипулирования словарями, вот пример его использования.

func sendJson(userClient pb.UserServiceClient, ctx context.Context) {
    var item = &structpb.Struct{
        Fields: map[string]*structpb.Value{
            "name": &structpb.Value{
                Kind: &structpb.Value_StringValue{
                    StringValue: "James",
                },
            },
            "age": &structpb.Value{
                Kind: &structpb.Value_NumberValue{
                    NumberValue: 20,
                },
            },
        },
    }

    userGetRequest := &pb.SendJsonRequest{
        UserID: "A123",
        Details: item,
    }

    res, err := userClient.SendJson(ctx, userGetRequest)
}

Суммировать

Суммируя три метода, я все же считаю, что первый более удобен в использовании, второй может ограничивать тип значения только единицей, в противном случае нужно делать преобразование типов на клиенте и сервере, третий тоже может найти в сети.proto.StructИспользование меньшего количества информации, более сложное для начала и не такое гибкое, как первое. Кроме тогоProtobufесть еще одинAnyТип, давайте не нужно определить сообщение, когда мы его используем, но нам нужно нести данные о описанииurlПользоваться им не удобно. Эту часть можно обсуждать, если можно обсудить связанный с ней опыт читателя.

Смотрите здесь, если вам понравилась моя статья, вы можете поставить мне лайк, я буду делиться своими знаниями и практическим опытом из первых рук в технических статьях каждую неделю, спасибо за вашу поддержку. Поиск в WeChat и подписка на общедоступную учетную запись «Управление сетью Наоби Нао» каждую неделю обучают вас углубленным знаниям, а также есть вводные руководства по Kubernetes, специально написанные для инженеров-разработчиков.