在gRPC中使用直接类以避免数据复制

时间:2019-01-03 09:57:47

标签: java c# protocol-buffers grpc protobuf-net

我们要避免gRPC通信中的数据复制。

我在原始文件中有以下(不是真实的)示例:

message Person
{
    string id = 1;
    string Name = 2;
    string Address = 3;
}

message PersonId
{
    required string id = 1;
}

service PersonService
{
    rpc GetPersonById( PersonId ) returns Person;
}

它工作正常。但是当我实现它时,我已经将所有数据复制到了请求中:

class PersonServiceImpl : PersonService.MaterialServiceBase
{
    public override Task<Person> GetPersonById( PersonId request, ServerCallContext context )
    {
        Datamodel.Person person = GetPersonForDatabaseAsync(request.Id).Result;

        Task.FromResult( new Person() {
            Id = person.Id,
            Name = person.Name,
            Address = person.Address
            } );
    }
}

但是,当我们避免数据复制时,它很慢,因为当我们向Person添加新成员时,它很慢而且很危险。完美的代码如下:

    public override Task<Person> GetPersonById( PersonId request, ServerCallContext context )
    {
        return GetPersonForDatabaseAsync(request.Id);
    }

有可能吗?

我们检查性能,然后将25%的时间用于封送和拆组,特别是在大型类型上,该类型具有嵌套类型,例如“大订单”。

我添加了Java标记,因为它也是Java环境中的相关问题。

由于评论而编辑:

namespace Datamodel
{
    class Person
    {
        string Id { get; set; }
        string Name { get; set; }
        string Address { get; set; }
    }
}

从另一个角度来看,问题是:我应该如何更改Datamodel.Person以使其与gRPC响应兼容。

1 个答案:

答案 0 :(得分:0)

不幸的是,由于protobuf代码的生成,这是不可能的。所有成员和功能都经过硬编码。

当gRPC允许使用接口而不是直接成员定义为消息类时,这将是很棒的。该接口将包含所有成员和一个create函数。

因此,生成的代码将与此更改:

[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void MergeFrom( pb::CodedInputStream input )
{
    uint tag;
    while ( ( tag = input.ReadTag() ) != 0 )
    {
        switch ( tag )
        {
            default:
                _unknownFields = pb::UnknownFieldSet.MergeFieldFrom( _unknownFields, input );
                break;
            case 10:
                {
                    Id = input.ReadString();
                    break;
                }
            case 18:
                {
                    Name = input.ReadString();
                    break;
                }
        }
    }
}

对此:

[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void MergeFrom( pb::CodedInputStream input )
{
    uint tag;
    while ( ( tag = input.ReadTag() ) != 0 )
    {
        switch ( tag )
        {
            default:
                Instance.UnknownFields = pb::UnknownFieldSet.MergeFieldFrom( Instance.UnknownFields, input );
                break;
            case 10:
                {
                    Instance.Id = input.ReadString();
                    break;
                }
            case 18:
                {
                    Instance.Name = input.ReadString();
                    break;
                }
        }
    }
}

其中Instance是从原型文件生成的接口的实现:

public interface IPerson
{
    private pb::UnknownFieldSet UnknownFields;
    string Id { get; set; }
    string Name { get; set; }
}

如果我的Datamodel.Person实现了该接口,并且生成的Person消息存储并通过此IPerson接口检索了数据,那么我们可以避免数据复制以及使用gRPC速度会提高25%。

我认为这是gRPC的巨大优势,也许Jon Skeet可以做到。当我们有明确的概念时,我可以进行c,c ++和c#的更改。

顺便说一句,对于gRPC,这将是一个巨大的进步,因为这是人们使用REST而不是REST的原因之一。在REST世界中,可以使用JSON Datamodel将数据直接检索到Serialize结构。他们可以使用Deserialize直接发送结构。

它必须是protobuf文件中的一个新选项,并且所有巫婆支持的界面都可以使用它。