实现一个数据库(7)消息模型

github链接(更新中)https://github.com/pourtheworld/mydb大纲(更新中)(0)mydb的架构设想本期任务通讯协议的构建架构设计协议格式内存对齐自定义消息格式消息头消息主体消息接受消息回复消息处理服务端Agent消息处理测试gdb调试以及BSON格式完善了Agent的处理后进行测试通讯协议的构建协议的构建来源于架构的设计;协议的格式包括头+主体+附加信息;我们设计协议某个部分长度时需要考虑到内存对齐。架构设计插入每次插入一条或者多条记录。(我
摘要由CSDN通过智能技术生成

github链接(更新中)

https://github.com/pourtheworld/mydb


大纲(更新中)
(0)mydb的架构设想


通讯协议的构建

协议的构建来源于架构的设计;
协议的格式包括头+主体+附加信息;
我们设计协议某个部分长度时需要考虑到内存对齐。

架构设计

  1. 插入
    每次插入一条或者多条记录。(我们暂时设定为一次插一条)
  2. 更改
    不支持。
  3. 查询
    仅支持K/V存储,查询提供value作为查询条件。
  4. 删除
    仅支持K/V存储,删除提供value作为删除条件。
  5. 其他
    包含不定信息,具体内容存于信息内。
  6. 返回
    包含返回码、记录数量、数据信息。

协议格式

协议头:包含消息的长度+消息的类型,可能有人会问为什么类型需要4个字节,等会再内容对齐会详述。
在这里插入图片描述
协议主体:
在这里插入图片描述
协议附加信息:
在这里插入图片描述

内存对齐

在消息头的设置上,我们将消息类型置为了4字节,实际上我们能用到的可能1字节都足矣。
但是在CPU读取处理的过程中,往往是以4字节为单位的,如果我们存了1个字节a,系统会一次读取1个字节的a加上3个字节的其他内容b。而b在下一个读取块里还有其他部分,可能会导致需要读两次才能得到完整的b;
甚至这个b由于下一块内容被修改了导致不一致

在以下InefficientParking结构体中,我们可以看到mF2,mB5虽然只有1个字节,但是编译器会自动进行内存对齐
在这里插入图片描述

自定义消息格式

我们首先介绍msg.hpp中消息格式如何定义、消息如何封装和解封;
再介绍command.cpp中命令的处理函数。

消息头

#define OP_REPLY        1
#define OP_INSERT       2
#define OP_DELETE       3
#define OP_QUERY        4
#define OP_COMMAND      5
//由于在网络层的disconnect需要PD_LOG,我们从通信层定义正常的关闭与连接
#define OP_DISCONNECT   6
#define OP_CONNECT      7
#define OP_SNAPSHOT     8

#define RETURN_CODE_STATE_OK 1

//各类消息的头结构是一样 4bytes消息长度+4bytes消息类型(为了内存对齐)
struct MsgHeader
{
   
    int messageLen;
    int opCode;
};

消息主体

回复类消息:

//回复类消息的主体为 返回值  返回的记录数
//data[0]为可变长度数组,本身可以不占内存
//在这里用于记录消息主体开始的位置
struct MsgReply
{
   
    MsgHeader header;
    int returnCode;
    int numReturn;
    char data[0];
};

插入类消息:

//插入类消息的主体为 插入的记录数
struct MsgInsert
{
   
    MsgHeader header;
    int numInsert;
    char data[0];
};

删除类消息:

//删除类消息没啥主体,不过key对应了待删除消息的键
struct MsgDelete
{
   
    MsgHeader header;
    char key[0];
};

查询类消息:

struct MsgQuery
{
   
    MsgHeader header;
    char key[0];
};

命令类消息:

//命令类消息要包含命令参数
struct MsgCommand
{
   
    MsgHeader header;
    int numArgs;;
    char data[0];
};

消息的解封,过程比较类似,我们以回复类消息为准:

//需要返回的当前缓冲区、缓冲区当前长度、给定的返回值、需要返回的数据
int msgBuildReply(char **ppBuffer,int *pBufferSize,int returnCode,BSONObj *objReturn)
{
   
    int rc=EDB_OK;
    int size=sizeof(MsgReply);  //当前包的长度实际为MsgReply的Header
    MsgReply *pReply=NULL;

    //如果有需要返回的内容,长度+数据长度
    if(objReturn)   size+=objReturn->objsize();
    
    //接下来需要重新分配ppBuffer,首先查看当前缓冲区是否够大
    rc=msgCheckBuffer(ppBuffer,pBufferSize,size);
    PD_RC_CHECK ( rc, PDERROR, "Failed to realloc buffer for %d bytes, rc = %d",
                 size, rc ) ;

    //缓冲区分配好了,我们先把它的指针类型强转成MsgReply
    //即-> Header:messengeLen+opCode 、returnCode、numReturn、data[0]
    pReply=(MsgReply*)(*ppBuffer);
    pReply->header.messageLen=size;
    pReply->header.opCode=OP_REPLY;

    pReply->returnCode=returnCode;
    pReply->numReturn=(objReturn)?1:0;

    //从pReply结构体中成员data所占内存的首地址开始,复制data的内容
    if(objReturn)   memcpy(&pReply->data[0],objReturn->objdata(),objReturn->objsize());

done:
    return rc;
error:
    goto done;

}

解封信息也以回复信息为准:

//MsgReply的解封
//获得的缓冲区 待接受的返回值 待接受的记录数 待接受的data内容
int msgExtractReply(char *pBuffer,int &returnCode,int &numReturn,const char **ppObjStart)
{
   
    int rc=EDB_OK;

    //我们用一个pReply的指针去指向获得的缓冲区
    MsgReply *pReply=(MsgReply*
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值