文章目录
零、文章背景
有些后台同学将自己称为 SQL Boy,因为负责的业务主要是对数据库进行增删改查。经常和 Proto 打交道的同学,是不是也会叫自己 PB Boy? 因为大部分工作也是对 Proto 进行 SET 和 GET。面对大量重复且丑陋的代码,除了宏是否有更好的解决方法?本文结合 PB 反射给出了我在运营系统开发工作中的一些代码优化实践。
一、反射的背景知识(反射消息来修改消息的某些字段)
-
反射定义
反射的一般定义如下:计算机程序在运行时可以访问、检测和修改它本身状态或行为。 -
类图
-
类图解析
1)从上图我们可以看出,Message 类继承于 MessageLite 类,业务一般自定义的 Person 类继承于 Message 类。
2)Descriptor 类和 Reflection 类都聚合于 Message,是弱依赖的关系。 -
过程(粗略过程,一般使用 PB 反射的步骤)
1. 通过Message获取单个字段的FieldDescriptor
2. 通过Message获取其Reflection
3. 通过Reflection来操作FieldDescriptor,从而动态获取或修改单个字段
- 获取 Descript、Reflection 的函数:
const google::protobuf::Reflection* pReflection = pMessage->GetReflection();
const google::protobuf::Descriptor* pDescriptor = pMessage->GetDescriptor();
- 获取 FieldDescriptor 的函数:
const google::protobuf::FieldDescriptor * pFieldDesc = pDescriptor->FindFieldByName(id);
二、反射消息来传输数据需要注意的点
- 在网络编程中使用Protobuf需要解决以下两个问题:
1)长度
Protobuf打包的数据没有自带长度信息或终结符,需要由应用程序自己在发生和接收的时候做正确的切分。
2)类型
Protobuf打包的数据没有自带类型信息,需要由发送方把类型信息传给给接收方,接收方创建具体的Protobuf Message对象,再做反序列化
三、过程(反射消息来传输数据的详细过程)
1)根据type name自动创建Message的关键代码
(1)1.用DescriptorPool: : generated_pool() 找到一个DescriptorPool对象,它包含了程序编译的时候所链接的全部Protobuf Message types。
(2)根据type name用DescriptorPool: :FindMessageTypeByName()查找Descriptor。
(3)再用MessageFactory: : generated_factory() 找到MessageFactory对象,它能创建程序编译的时候所链接的全部Protobuf Message types。
(4)然后,用MessageF actory: :GetPrototype()找到具体Message type 的default instance。
(5)最后,用prototype->New()创建对象。
2)代码示例
Message* createMessage(const std::string& typeName)
{
Message* message = NULL;
//1)generated_pool() 找到一个DescriptorPool对象
//2)type name用DescriptorPool: :FindMessageTypeByName()查找Descriptor。
const Descriptor* descriptor =
DescriptorPool::generated_pool()->
FindMessageTypeByName(typeName) ;
if (descriptor)
{
//3)再用MessageFactory: : generated_factory() 找到
//MessageFactory对象
//4)用MessageF actory: :GetPrototype()找到具体Message type 的default instance
const Message* prototype
= MessageFactory::generated_factory()->GetPrototype(descriptor);
if (prototype)
{
//5)用prototype->New()创建对象
message = prototype->New(); .
}
}
return message ;
}
3)createMessage()返回的是动态创建的对象的指针,调用方有责任释放它,不然就会使内存泄漏。
-
备注
拿到Message*之后怎么办呢? 怎么调用这个具体消息类型的处理函数? 这就需要消息分发器( dispatcher)出马了 -
做分发器的思路
传送门