在 gRPC/proto3 中设置 oneof 请求的更有效方法

More efficient way to set a oneof request in gRPC/proto3

在 PHP 客户端和 Python 服务器上拥有此 proto3 模式:

service GetAnimalData{
    rpc GetData (AnimalRequest) returns (AnimalData) {}
}

message AnimalRequest {
    OneOfAnimal TypeAnimal = 1;
}

message AnimalData {
    repeated int32 data = 1;
}

message OneOfAnimal {
    oneof animal_oneof {
        CAT cat = 1;
        DOG dog = 2;
    }
}

message CAT{
    int32 p = 1;
    int32 d = 2;
    int32 q = 3;
}

message DOG{
    int32 p = 1;
    int32 d = 2;
    int32 q = 3;
    int32 p2 = 4;
    int32 d2 = 5;
    int32 q2 = 6;
}

为了设置来自 PHP 客户端的请求,我需要做:

  1. 新建一个CAT
  2. 设置OneOfAnimalCAT
  3. 设置AnimalRequest.TypeAnimalOneOfAnimal

是否有 proto3 的架构,我可以直接将 CATDOG 对象设置为我的 AnimalRequest.TypeAnimal

笼统地说;不,不是真的。

在某些非常具体的情况下,答案是"yes" - 例如,protobuf-net(.NET 中的 "code-first" protobuf 实现)发生以这种方式在 exactly 中实现继承,因此您可以使用:

[ProtoContract]
public class AnimalRequest {
    [ProtoMember(1)] public Animal Animal {get;set;}
}

[ProtoContract]
[ProtoInclude(1, typeof(Cat))]
[ProtoInclude(2, typeof(Dog))]
class Animal {} // acts identically to your OneOfAnimal message type

[ProtoContract]
class Cat {
    [ProtoMember(1)] public int P {get;set;}
    [ProtoMember(2)] public int D {get;set;}
    [ProtoMember(3)] public int Q {get;set;}
}

[ProtoContract]
class Dog {
    [ProtoMember(1)] public int P {get;set;}
    [ProtoMember(2)] public int D {get;set;}
    [ProtoMember(3)] public int Q {get;set;}
    [ProtoMember(4)] public int P2 {get;set;}
    [ProtoMember(5)] public int D2 {get;set;}
    [ProtoMember(6)] public int Q2 {get;set;}
}

然后你使用:

var result = svc.GetData(
    new AnimalRequest { Animal = new Dog { P = 42, ... Q2 = 19 } }
);

它会以完全相同的方式工作 - 将发送相同的字节;本质上,protobuf-net 将继承视为 oneof(在类型层次结构的每个级别)。

但是:因为 .proto 旨在以非常通用的 "least common denominator" 方式工作,.proto 本身 没有继承的概念,所以:您不能从 .proto 或 Google 库中执行此操作。