写这个数据库也已经有段时间了,在我心中此项目的第一个版本算是完成了,所以做一下小结,更多的功能以后再继续迭代即可!
1. 引言
1.1 编写目的
学习搭建rpc框架,自己阅读了部分redis实现源码,想实现一个加深印象,将学习的网络方面的知识学以致用。等等…
1.2 项目概述
客户端和服务器协议:Google Protocol buffer
RPC部分实现的功能:序列化和反序列化,发送远程命令
项目服务器使用Reactor模式设计的框架,为每一个连接设置了相应的读回调,写回调,每个连接套接字设置了EPOLLIN+非阻塞模式。实现了string和hash两种操作,并实现了save和bgsave两种持久化操作,bgsave操作通过创建新进程实现。项目中添加了最大堆定时器,为每个客户端设置了保活时间。当客户端产生一次活动时,会自动为该客户端延长时间,对于一些一直不活动的客户端,服务器会定时检查连接队列,并剔除闲置客户端。定时器通过统一事件源触发。
客户端实现比较简单,使用readline进行命令行输入,并设置了检测命令的合法模块,检测客户端是否与服务器建立了连接,要是没建立连接,会尝试和服务器建立5次连接。直到建立连接成功。
1.3 术语定义
术语名称 | 术语含义 |
---|---|
GPB | Google Protocol Buffer |
Server | 服务器 |
Client | 客户端 |
1.4 引用文档
名称 | 作者 |
---|---|
Redis设计与实现 | 黄建宏 |
Redis深度探险 | 钱文品 |
Google Protocol Buffer | Google Protocol Buffer |
高性能服务器 | 游双 |
2. 设计决策
2.1 设计目标
2.1.1 运行环境
LInux Ubuntu18.10
2.1.2 开发环境及工具
vim+vimplus+gcc 编译器
2.1.3 技术限制
书读的有点少
2.2 设计原则
严格遵守需求规格说明书设计,使用一些面向对象语言中的一些设计模式例如工厂模式,减少代码的复用性。
3. 逻辑架构设计
3.1 设计决策
Server设计领域模型如下:
client设计:
3.2 软件单元
- server端
直接看.cpp文件吧,类中的成员比较多,不好陈述,所以选择每个.cpp文件中的相关方法,将整个项目的功能实现加以描述。
aeEventloop.cpp
第一个就不说了,见名知意!
addServerEvent主要功能就是初始化时创建监听套接字,并设置好相应的回调函数。
addTimerEvent该方法创建一个eventfd并设置好相应的回调函数。
aeEventloop构造方法,创建了时间堆,创建了epoll检测引擎
下面是析构函数
aeProcessEvent该方法处理不同的事件,当aeEventloop将事件收集到vecotor中,该方法就会将其中的各种方法进行分类处理。
initDataInfo初始化时间堆中的相关数据
kickClient处理不活跃的客户端
notifyToSave 通知保存
setCallBack 设置回调函数
start 开始启动服务例程
aeEpoll.cpp
很简单的操作,可以看懂!
rpc.cpp
反序列化,发送响应请求。
aeEvent.cpp
读回调,和写回调函数。
timerHeap.cpp
MyTimer 时间堆上的节点,TimerManager是时间堆管理器。
detect_timers检测到期时间,若有超时客户端,就执行回调函数处理。
其它函数太鸡肋,不解释了。
cmdProcess.cpp
findCmd在进行发序列化之后,判查找命令是否存在。
initCmdCb初始化命令回调函数,每个命令对应一个回调函数。
initRedis 初始化数据库(就是从文件中将已经持久化的数据读出来)
processMsg处理不同的命令请求
sendMsg发送消息
cmdSet.cpp
addObjectToDb添加对象到数据库。
append增加数据库
countRedis获取当前数据库的数量
expend扩大容纳数据库容器的容量
findCmd 查找命令
getDB获取指定数据库对象
initCmdCb初始化命令集合
initRedis初始化数据库
redisCommandProc 数据库各种命令处理函数,根据命令的不同回调在命令集中找相应的回调函数
cb 数据库各种命令对象对应的回调函数
redisDb.cpp和redisDb.h
因为是键值数据库,所以对于大部分对象而言操作比较相似,所以利用C++多态机制实现了工厂模式,父类为抽象类dbObject,设置了许多虚方法,对于strings和hashSet都是继承了dbObject,redisDb通过hashmap管理dbObject,如图。键值为请求的key对象,key对象,成员是要访问对象的键值,类型,以及数据库编号三元组唯一确定对象在数据库中的值。
- redisDb.cpp
getValue
getValues hash获取值,将以vector对象的形式返回。
该数据库管理对象的模型如下:
- redisDb.h
目前实现了两种类型的操作。
抽象基类。
hash操作,setValue实现多参数设置。
strings对象,内部数据。
equalFunc和hashFunc设置hash函数。
factory工厂根据不同提示,返回不同对象。
rdb.cpp
持久化功能
下面是两大对象在文件中的存储格式:
- hash
- string
ctp存的是类型编号,数字编号之类,在初始化数据库的时候,都会通过正则表达式辨别。然后其他解析之类的函数就不作解释了!可以看源代码。
recoverDb.cpp
恢复数据库,因为我将每个数据库单独存在一个文件中,0号数据存在.db_0文件,后面以此类推,其中或有一个redis_fileName文件,存放的是数据库文件名,在恢复的时候,先读取.redis_fileName,将所有数据库文件名存在vector中,然后遍历vector再通过mmap映射将文件内容映射到string中,然后解析string就行。
客户端设计比较简单,读者可自行研究!
3.3 处理流程
4 运行演示
源代码 感谢star或者fork!本代码仅供阅读参考学习,在个人平台上运行的话可能得安装配置google protocol buffer!并且要考虑系统兼容性!