从小工到专家:从序列化到工厂模式

2021-03-04 14:21:52 浏览数 (1)

预计阅读时间: 6分钟

问题:

假如 device 和base是继承关系

base * ptr1 =new device();

device* ptr2 = new base(*ptr1)

请问ptr1 和ptr2是同样2个对象吗? 肯定不是呀

办法:

原型在哪里呀?

代码:这里是使用谷歌序列化框架为例子

tCP是一种流协议(stream protocol)。【补充一个概念

这就意味着数据是以字节流的形式传递给接收者的,没有固有的”报文”或”报文边界”的概念。

https://www.toutiao.com/i6932340828622963208/

tcp为什么需要分包:消息(多个)--字节流--消息(完整)

代码语言:javascript复制
//字节流拆分边界
void ProtobufCodec::onMessage(const TcpConnectionPtr& conn,
                              Buffer* buf,
                              Timestamp receiveTime)
{
  while (buf->readableBytes() >= kMinMessageLen   kHeaderLen)
  {
    const int32_t len = buf->peekInt32();
    if (len > kMaxMessageLen || len < kMinMessageLen)
    {
      errorCallback_(conn, buf, receiveTime, kInvalidLength);
      break;
    } //不是完整数据
    else if (buf->readableBytes() >= implicit_cast<size_t>(len   kHeaderLen))
    {
      ErrorCode errorCode = kNoError;
      //pb不负责数据的边界处理。
      MessagePtr message = parse(buf->peek() kHeaderLen, len, &errorCode);
      if (errorCode == kNoError && message)
      {
        messageCallback_(conn, message, receiveTime);
        buf->retrieve(kHeaderLen len);
      }
      else
      {
        errorCallback_(conn, buf, receiveTime, errorCode);
        break;
      }
    }
    else
    {
      break;
    }
  }
}
//通过工厂映射创建一个类
google::protobuf::Message* ProtobufCodec::createMessage(const std::string& typeName)
{
  google::protobuf::Message* message = NULL;
  const google::protobuf::Descriptor* descriptor =
    google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(typeName);
  if (descriptor)
  {
    const google::protobuf::Message* prototype =
      google::protobuf::MessageFactory::generated_factory()->GetPrototype(descriptor);
    if (prototype)
    {
      message = prototype->New();
    }
  }
  return message;
}
//把字节流变成一个类
MessagePtr ProtobufCodec::parse(const char* buf, int len, ErrorCode* error)
{
  MessagePtr message;

  // check sum
  int32_t expectedCheckSum = asInt32(buf   len - kHeaderLen);
  int32_t checkSum = static_cast<int32_t>(
      ::adler32(1,
                reinterpret_cast<const Bytef*>(buf),
                static_cast<int>(len - kHeaderLen)));
  if (checkSum == expectedCheckSum)
  {
    // get message type name
    int32_t nameLen = asInt32(buf);
    if (nameLen >= 2 && nameLen <= len - 2*kHeaderLen)
    {
      std::string typeName(buf   kHeaderLen, buf   kHeaderLen   nameLen - 1);
      // create message object
      message.reset(createMessage(typeName));
      if (message)
      {
        // parse from buffer
        const char* data = buf   kHeaderLen   nameLen;
        int32_t dataLen = len - nameLen - 2*kHeaderLen;
        if (message->ParseFromArray(data, dataLen))
        {
          *error = kNoError;
        }
        else
        {
          *error = kParseError;
        }
      }
      else
      {
        *error = kUnknownMessageType;
      }
    }
    else
    {
      *error = kInvalidNameLen;
    }
  }
  else
  {
    *error = kCheckSumError;
  }

  return message;
}

补充概念:

  1. pb 不负责 tcp 读取和写入 包括 切分数据这个动作。
  2. pb 客户端和服务共享pro文件。通过名字映射具体的类。
代码语言:javascript复制
这个是谷歌pb代码:
//static 
GeneratedMessageFactory* GeneratedMessageFactory::singleton() {
  static auto instance =
      internal::OnShutdownDelete(new GeneratedMessageFactory);
  return instance;
}

class GeneratedMessageFactory final : public MessageFactory {
 public:
  static GeneratedMessageFactory* singleton();

  void RegisterFile(const google::protobuf::internal::DescriptorTable* table);
  void RegisterType(const Descriptor* descriptor, const Message* prototype);

  // implements MessageFactory ---------------------------------------
  const Message* GetPrototype(const Descriptor* type) override;

 private:
  
  // Initialized lazily, so requires locking.
  std::unordered_map<const Descriptor*, const Message*> type_map_;
  //问题:谁注册的,在哪里注册。
  //这样避免if else 判断,新增类型就注册
};
void GeneratedMessageFactory::RegisterType(const Descriptor* descriptor,
                                           const Message* prototype) {


  // This should only be called as a result of calling a file registration
  // function during GetPrototype(), in which case we already have locked
  // the mutex.
  mutex_.AssertHeld();
  if (!InsertIfNotPresent(&type_map_, descriptor, prototype)) {
    GOOGLE_LOG(DFATAL) << "Type is already registered: " << descriptor->full_name();
  }
}
void MessageFactory::InternalRegisterGeneratedMessage(
    const Descriptor* descriptor, const Message* prototype) {
  GeneratedMessageFactory::singleton()->RegisterType(descriptor, prototype);
}哈哈哈
找到了
// For internal use only:  Registers a message type.  Called only by the
  // functions which are registered with InternalRegisterGeneratedFile(),
  // above.
  static void InternalRegisterGeneratedMessage(const Descriptor* descriptor,
 
  从静态方法到静态类,这说明了什么,在编译期间就可以执行。
  不需要依赖具体对象。
  
  // For internal use only:  Registers a .proto file at static initialization
  // time, to be placed in generated_factory.  The first time GetPrototype()
  // is called with a descriptor from this file, |register_messages| will be
  // called, with the file name as the parameter.  It must call
  // InternalRegisterGeneratedMessage() (below) to register each message type
  // in the file.  This strange mechanism is necessary because descriptors are
  // built lazily, so we can't register types by their descriptor until we
  // know that the descriptor exists.  |filename| must be a permanent string.
  static void InternalRegisterGeneratedFile(
      const google::protobuf::internal::DescriptorTable* table);
  

最重要一个注释:在静态对象初始化的时候,注册类型(编译期间,不需要手工注册)

代码语言:javascript复制
  // For internal use only:  Registers a .proto file at static initialization
  // time, to be placed in generated_factor

0 人点赞