一种自动反射消息类型的 Google Protobuf 网络传输方案

1、Protobuf 自动反射消息

参考
这篇文章要解决的问题是:在接收到 protobuf 数据之后,如何自动创建具体的 Protobuf Message 对象,再做的反序列化。

  • “自动”的意思是:当程序中新增一个 protobuf Message 类型时,这部分代码不需要修改,不需要自己去注册消息类型。

  • 其实,Google Protobuf 本身具有很强的反射(reflection)功能,可以根据 type name创建具体类型的Message 对象,我们直接利用即可。

2、网络编程中使用 protobuf 的两个问题

Google Protocol Buffers (Protobuf) 是一款非常优秀的库,它定义了一种紧凑的可扩展二进制消息格式,特别适合网络数据传输。它为多种语言提供 binding,大大方便了分布式程序的开发,让系统不再局限于用某一种语言来编写。

在网络编程中使用 protobuf 需要解决两个问题:

  • 长度,protobuf 打包的数据没有自带长度信息终结符,需要由应用程序自己在发生和接收的时候做正确的切分;

  • 类型,protobuf打包的数据没有自带类型信息,需要由发送方把类型信息传给给接收方,接收方创建具体的 Protobuf Message 对象,再做的反序列化。

3、根据 type name 反射自动创建 Message 对象

Google Protobuf 本身具有很强的反射(reflection)功能,可以根据 type name 创建具体类型的 Message 对象。
在这里插入图片描述
我估计大家通常关心和使用的是图的左半部分:MessageLite、Message、Generated Message Types (Person, AddressBook) 等,而较少注意到图的右半部分:Descriptor, DescriptorPool, MessageFactory。

上图中,其关键作用的是 Descriptor class,每个具体 Message Type 对应一个 Descriptor 对象。尽管我们没有直接调用它的函数,但是Descriptor在“根据 type name 创建具体类型的 Message 对象”中扮演了重要的角色,起了桥梁作用。上图的红色箭头描述了根据 type name 创建具体 Message 对象的过程,后文会详细介绍。

扫描二维码关注公众号,回复: 11470546 查看本文章

4、原理简述

Protobuf Message class 采用了 prototype pattern,Message class 定义了 New() 虚函数,用以返回本对象的一份新实例,类型与本对象的真实类型相同。也就是说,拿到 Message* 指针不用知道它的具体类型,就能创建和它类型一样的具体 Message Type 的对象

每个具体 Message Type 都有一个 default instance,可以通过 ConcreteMessage::default_instance() 获得,也可以通过 MessageFactory::GetPrototype(const Descriptor*) 来获得。所以,现在问题转变为

  • 如何拿到 MessageFactory
  • 如何拿到 Descriptor*

当然,ConcreteMessage::descriptor() 返回了我们想要的 Descriptor*,但是,在不知道 ConcreteMessage 的时候,如何调用它的静态成员函数呢?这似乎是个鸡与蛋的问题。

我们的英雄是 DescriptorPool,它可以根据 type name 查到 Descriptor*,只要找到合适的 DescriptorPool,再调用 DescriptorPool::FindMessageTypeByName(const string& type_name) 即可。眼前一亮?

5、根据 type name 自动创建 Message 的关键代码

  • DescriptorPool::generated_pool() 找到一个DescriptorPool对象,它包含了程序编译的时候所链接的全部 protobuf Message types
  • DescriptorPool::FindMessageTypeByName() 根据 type name 查找 Descriptor
  • 再用 MessageFactory::generated_factory() 找到 MessageFactory对象,它能创建程序编译的时候所链接的全部 protobuf Message types
  • 然后,用 MessageFactory::GetPrototype() 找到具体 Message Typedefault instance
  • 最后,用 prototype->New() 创建对象。
inline google::protobuf::Message* createMessage(const std::string& type_name)
{
  google::protobuf::Message* message = NULL;
  const google::protobuf::Descriptor* descriptor =
    google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(type_name);
  if (descriptor)
  {
    const google::protobuf::Message* prototype =
      google::protobuf::MessageFactory::generated_factory()->GetPrototype(descriptor);
    if (prototype)
    {
      message = prototype->New();
    }
  }
  return message;
}

参考

1、https://www.cnblogs.com/Solstice/archive/2011/04/03/2004458.html
2、http://www.cppblog.com/sunicdavy/archive/2011/12/14/162115.html

猜你喜欢

转载自blog.csdn.net/JMW1407/article/details/107636570