Java 运行时生成的协议缓冲区对象

Java 运行时生成的协议缓冲区对象,java,protocol-buffers,Java,Protocol Buffers,我的一位同事提出了在运行时生成协议缓冲区类的想法。意思是: 有C++服务器应用程序和java客户机应用程序通过协议缓冲区消息在TCP/IP上通信。 LI>不同版本的C++应用程序可能有不同的模式,这不一定是向后兼容的 有一个Java应用程序与此服务器通信,它应该支持所有可能的服务器版本 其思想是,服务器发送协议缓冲区的定义作为初始握手的一部分,java应用程序在运行时生成类,并使用它与服务器通信 我想知道这是否是一个非常重要的想法,对于这样的用例是否可能有一些实用性 谢谢,你所描述的实际上

我的一位同事提出了在运行时生成协议缓冲区类的想法。意思是:

  • 有C++服务器应用程序和java客户机应用程序通过协议缓冲区消息在TCP/IP上通信。 <> LI>不同版本的C++应用程序可能有不同的模式,这不一定是向后兼容的
  • 有一个Java应用程序与此服务器通信,它应该支持所有可能的服务器版本
其思想是,服务器发送协议缓冲区的定义作为初始握手的一部分,java应用程序在运行时生成类,并使用它与服务器通信

我想知道这是否是一个非常重要的想法,对于这样的用例是否可能有一些实用性


谢谢

,你所描述的实际上已经被C++和java中的协议缓冲区实现所支持。您只需发送一个
文件描述符集
(如中所定义),其中包含表示每个相关
.proto
文件的
文件描述符proto
,然后使用
动态消息
解释接收端的消息

在C++中获得<代码>文件描述符PROTOT/<代码>,给定该文件中定义的消息类型<代码> FoO ,请执行:

google::protobuf::FileDescriptorProto file;
Foo::descriptor().file()->CopyTo(&file);
将所有定义所需类型的
FileDescriptorProto
s以及它们导入的所有文件放入
FileDescriptorSet
proto。请注意,您可以使用(
Foo::descriptor().file()
)来迭代依赖项,而不是显式地命名每个依赖项

现在,将
文件描述符集
发送到客户端

在客户机上,使用将每个
文件描述符协议转换为live。您必须确保在生成依赖项之前先生成依赖项,因为在生成依赖项时必须向提供已生成的依赖项

从那里,您可以使用查找您关心的特定消息类型的

最后,您可以调用为所讨论的类型构造一个新的生成器实例。实现接口,该接口具有和等字段,用于动态操作消息的字段(通过指定相应的s)

类似地,您可以调用解析从服务器接收的消息

请注意,这种方法的一个缺点是速度相对较慢。本质上,它就像一种解释语言。生成的代码更快,因为编译器可以针对特定类型进行优化,而必须能够处理任何类型

然而,真的没有办法解决这个问题。即使在运行时运行代码生成器并编译类,实际使用新类的代码仍然是您之前编写的代码,您还不知道要使用什么类型。因此,它仍然必须使用反射或类似反射的接口来访问消息,这将比为特定类型手工编写代码要慢

但这是个好主意吗? 这要看情况了。客户机实际将如何处理它从服务器接收到的这个模式?通过有线传输模式并不能神奇地使客户机与该版本的协议兼容——客户机仍然必须理解协议的含义。如果协议已以向后不兼容的方式更改,这几乎肯定意味着协议的含义已更改,并且必须更新客户端代码,模式传输与否。您可以期望客户端在不进行更新的情况下继续工作的唯一时间是当客户端只执行一个只依赖于消息内容而不依赖于消息含义的通用操作时——例如,客户端可以将消息转换为JSON,而不必知道它的含义。但这是相对不寻常的,尤其是在应用程序的客户端。这正是Protobufs在默认情况下不发送任何类型信息的原因——因为它通常是无用的,因为如果接收者不知道其含义,则模式是不相关的

如果问题是服务器正在向客户机发送根本不需要解释的消息,而只是在以后发送回服务器,那么客户机根本不需要该模式。只需将消息作为
字节进行传输
,不必费心解析它。请注意,包含类型为
Foo
的编码消息的
bytes
字段在线路上看起来与类型实际声明为
Foo
的字段完全相同。实际上,您可以根据稍微不同的
.proto
文件版本编译客户端和服务器,其中客户端将特定字段视为
字节,而服务器将其视为子消息,以避免客户端需要知道该子消息的定义。

``

对于Java,您可能会发现以下包装器API(“protobuf dynamic”)比原始protobuf API更易于使用:

例如:

// Create dynamic schema
DynamicSchema.Builder schemaBuilder = DynamicSchema.newBuilder();
schemaBuilder.setName("PersonSchemaDynamic.proto");

MessageDefinition msgDef = MessageDefinition.newBuilder("Person") // message Person
    .addField("required", "int32", "id", 1)     // required int32 id = 1
    .addField("required", "string", "name", 2)  // required string name = 2
    .addField("optional", "string", "email", 3) // optional string email = 3
    .build();

schemaBuilder.addMessageDefinition(msgDef);
DynamicSchema schema = schemaBuilder.build();

// Create dynamic message from schema
DynamicMessage.Builder msgBuilder = schema.newMessageBuilder("Person");
Descriptor msgDesc = msgBuilder.getDescriptorForType();
DynamicMessage msg = msgBuilder
    .setField(msgDesc.findFieldByName("id"), 1)
    .setField(msgDesc.findFieldByName("name"), "Alan Turing")
    .setField(msgDesc.findFieldByName("email"), "at@sis.gov.uk")
    .build();

动态模式在某些应用程序中非常有用,可以在不重新编译代码的情况下分发更改(例如在更动态类型的系统中)。对于不需要语义理解的“愚蠢”应用程序(比如数据浏览器工具),它们也非常有用。

这不是为什么吗?虽然我不知道你说的是不是个好主意。我需要看看。你能举个例子吗?不幸的是,javadoc没有说太多。不幸的是,我没有一个例子。被接受的答案完全解决了这个问题,除了我用另一个问题要求的扩展:这实际上是非常有用的答案。我需要一段时间才能完全尝试,但我正在慢慢前进:)一旦完成就接受。请阅读此文,你能快速浏览一下吗,好像你是前任