在 gRPC 中使用直接 类 以避免数据复制
Use direct classes in gRPC to avoid data-copy
我们希望避免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% 的时间用于编组和解组,尤其是大型类型,它有嵌套类型,例如 Huge Orders。
我添加了 java 标签,因为它也是 Java 环境中的相关问题。
因评论而编辑:
namespace Datamodel
{
class Person
{
string Id { get; set; }
string Name { get; set; }
string Address { get; set; }
}
}
所以问题是,从另一个角度来看:我应该如何更改 Datamodel.Person
以使其与 gRPC
响应兼容。
不幸的是,这是不可能的,因为 protobuf 代码生成。所有成员和函数都是硬编码的。
如果 gRPC 提供了使用接口而不是直接成员定义消息的可能性,那就太好了 class。该接口将包含所有成员和一个创建函数。
所以生成的代码是这样改的:
[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 世界中,可以使用 JSON Serialize
将数据直接检索到 Datamodel
结构。使用 Deserialize
他们可以直接发送结构。
必须是protobuf文件中的新选项,所有语言都可以支持接口
我们希望避免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% 的时间用于编组和解组,尤其是大型类型,它有嵌套类型,例如 Huge Orders。
我添加了 java 标签,因为它也是 Java 环境中的相关问题。
因评论而编辑:
namespace Datamodel
{
class Person
{
string Id { get; set; }
string Name { get; set; }
string Address { get; set; }
}
}
所以问题是,从另一个角度来看:我应该如何更改 Datamodel.Person
以使其与 gRPC
响应兼容。
不幸的是,这是不可能的,因为 protobuf 代码生成。所有成员和函数都是硬编码的。
如果 gRPC 提供了使用接口而不是直接成员定义消息的可能性,那就太好了 class。该接口将包含所有成员和一个创建函数。
所以生成的代码是这样改的:
[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 世界中,可以使用 JSON Serialize
将数据直接检索到 Datamodel
结构。使用 Deserialize
他们可以直接发送结构。
必须是protobuf文件中的新选项,所有语言都可以支持接口