• 项目需求
  • 整体思路
  • 网络通信
  • 字符解析
  • 数据存储与查询
    • 1 存储管理
    • 2 数据查询
  • 多线程
  • 待改进未实现的想法
  • GitHub源代码

项目需求

设计一个基于Socket或基于HTTP的server,服务内容是提供一种简单的key/value映射关系的管 理与查询
以下的全部操作都是通过结构体Node来传递的:
struct Node {
char key[KEY_SIZE];
char value[VALUE_SIZE];
};
本场景中须要client和server两个程序
client端仅仅有两种操作:
int AddNode(const struct Node *node); // 将指定的node保存到server上,须要key和value都 完整
int GetNode(struct Node *node); // 输入的node须要有完整的key。server负责将这个key相应 的value填到node中。或返回不存在

server: server端就是接收client的两种请求,然后要么保存node要么查询node并返回值。
server端怎么保存node不作要求,但要达到以下的几点:
1. 假设将全部node都保存在内存中。那么当内存中的数据太多时,须要定期将node保存成磁 盘文件。
2. client端在运行AddCell时,假设相应的key已经存在于server端。那么覆盖原有的值。

1. 整体思路

首先我们要开发一个基于网络的server,那么网络的知识是必须的,我们要开发的server是一种要求端对端的可靠的server。因此将採用基于TCP协议和套接字Socket作为服务端和client之间的通信方式。进,而我们能够观察到我们操作的数据是一个(key,value)形式。而TCP是一个以字节流传输的连接,因此须要一个字符串(char*)和数据结构(key,value)之间的相互转换函数。来将服务端和client之间的传输字节流解析成命令和数据节点(以及将命令和数据节点打包成字符串进行通信)。然后我们必须考虑server上数据的存储和查询问题,这是项目实现的关键。最后,server肯定要支持多client的同一时候连接和通信,因此还必须增加多线程或多进程。
本项目已实现的功能

  • 基于Socket的TCP网络传输
  • 对网络传输字符的解析与打包
  • LRU Page 缓存
  • Hash-map 查找
  • 多线程多clients 同一时候put。get操作

以下将从四个方面进行阐述项目思想:网络通信,字符解析。数据存储与查询,多线程实现。

2. 网络通信

基于本server的功能特性考虑。我们要求的是可靠性,因此选择TCP。
而在TCP的连接前。
- server首先要做的准备是:创建一个连接套接字socket。然后socket绑定(bind)在一个IP和Port上以供client寻找。监听(listen)client的请求,然后accept一直堵塞等待client的连接。

- client须要做的准备:client的套接字附上服务端的ip地址和自己的port号。
当服务端和client做好通信准备后,由client发起,服务端响应。经过三次握手,建立相互之间的连接。
当一个client通信完毕后, client会主动向服务端请求释放连接。

关于三次握手以及当一个client通信完毕,与服务端释放连接时的四次挥手的具体知识,请參考wireshark抓包图解 TCP三次握手/四次挥手具体解释

建立连接后,服务端调用accept()函数。堵塞服务端进程。直至收到clientsend()过来的信息。
关于网络编程中的Socket接口函数的具体解说。參考Linux的SOCKET编程具体解释

3. 字符解析

因为网络通信中传输的是字符,我们必须将字符解析成 命令(put,get,…etc.)和数据(key,value)。
由上述得知,传输字符包括有:命令,key,value。我们可依据习惯。设立切割字符,如空格,分号等。

须要注意的是每次传输的字符串切割后,得到的子字符串的个数可能是不同的,如”put 12:quinn” 分为3段,而”get 12”分为2段,”exit”仅仅有一段。

因此依据client发信给client的命令不同,字符解析函数将它解析成不同的命令。

而服务端首先推断第一段字符的含义(put,get,exit,save等)。来决定自己要实现的动作。

源代码查看:https://github.com/qzxin/key-value-server/blob/master/convert.cpp

4. 数据存储与查询

4.1 存储管理

因为内存空间有限。当数据量到达上限时,必须把数据转存到磁盘文件里。
在server实现过程中,服务端须要接受client的get和put两种操作,
- put(key, value): 在接收一定数量的数据后须要将数据保存到磁盘上,而且须要检查是否存在同样的key。
- get(key): 向服务端查询是否存在该key;

因为项目需求同样的key仅仅能有一个值,所以无论是put和get都必须遍历内存和磁盘文件里的数据,查找是否含有该key。假设每一次操作都须要訪问磁盘,那么效率将是极低的,因为訪问数据的时间局部性,近期訪问过的数据在近期内有更大的可能再次被訪问,因此想到了引入内存缓存系统,即将近期訪问过的节点保留在内存中。又由訪问数据的空间局部性,近期訪问过的数据周围的数据有更大的可能被訪问,想到了引入分页机制。

  • 缓存系统:当查找节点时,首先在缓存中查找。查找成功则对该节点操作。

    缓存查找失败。即缺页中断。因为内存空间限制。缓存的大小是一定的,当发生缺页中断时,要从磁盘中载入新数据,即须要不断的用近期訪问成功的新数据替换缓存中的旧数据。

  • 分页机制:当待查找数据不在缓存,即缺页中断时,假设每次都从磁盘中载入一个数据,那效率是不可接受的。因此,将页(包括N个数据节点)作为缓存和磁盘数据之间操作的基本单位。

如上提到的缺页中断是操作系统中内存管理中的概念。

在请求分页存储管理系统中。因为使用了虚拟存储管理技术,使得全部的进程页面不是一次性地全部调入内存,而是部分页面装入。
这就有可能出现以下的情况:要訪问的页面不在内存。这时系统产生缺页中断。操作系统在处理缺页中断时,要把所需页面从外存调入到内存中。假设这时内存中有空暇块,就能够直接调入该页面。假设这时内存中没有空暇块,就必须先淘汰一个已经在内存中的页面,腾出空间。再把所需的页面装入。即进行页面置换。
当缺页中断时,须要进行页面置换。而常见的页面置换算法有:FIFO。LRU和时钟算法。
(1)FIFO是淘汰内存中存在时间最长的页,而最长的页可能是最常被訪问的。因此性能差。
(2)LRU是淘汰内存中最久没有被訪问的页。
(3)时钟算法是,将页连成一个环形链表,当缺页中断时。指针指向最老的页,当该页的訪问位为0。则删除该页。若该页訪问位为1,则将訪问位置0。遍历它的下一页,直至遇到一个訪问位为0的页。用新数据替换它,并把指针指向它的下一页。

注意,本文中假设”数据缓存“存在于内存中,即内存缓存,而”磁盘中的数据文件“模拟现代OS中的虚拟内存。即本文将缓存放在内存,将磁盘文件当做缓存页。

本文选用easy实现且性能尚可的LRU页面置换。具体实现过程已在还有一篇博文基于文件页的 LRU Cache:磁盘缓存实现中具体描写叙述,本文不再赘述。

本文思想是,为了更便利的对页数据进行置换。将磁盘文件的大小设置为页的大小,形成映射。

当缺页中断时,调入新的一页时,即读一个新文件到内存中。而怎样定位文件,下文分析;而被替换掉的页。假设页的dirty位为1。则又一次写入到它所属的文件,为了实现这一点,在页的数据结构中应该包括该页所属文件的编号。(这是OS中虚拟缓存的思想

怎样定位key所在的文件?(2015/07/19更新)
建立(key, file)映射的hash表。put操作时,将每个新key和该key将要存入的文件序号压入一个hash-map;get操作或put操作。search(key)时。假设该key不在缓存中,那么查key-file映射表,假设存在该key相应的文件,则将该文件载入进缓存中,否则返回不存在该key。注意,在server启动时,应该载入(key,file)的映射表(它们存储在一个文件里)。

class HashCache::Page {
public:int file_num_; // 页所相应的文件序号bool lock_;bool dirty_;  // 标记page是否被改动class Node data_[PAGE_SIZE];  // 页包括的节点数据class Page* next;class Page* prev;Page() {lock_ = false;dirty_ = false;}
};

4.2 数据查询

4.1存储管理 攻克了数据的存储和载入问题,那么怎样能高速的索引到一个数据呢?两种办法,平衡二叉树O(lgn)和hash表O(1);我们知道hash表的缺点是不能有效解决冲突,而本项目中的key,value唯一。因此採用更快的hash-map实现数据的索引,当然採用时间复杂度为O(lgn)的map实现也是能够的。

在实际操作中,当每次缺页中断,载入一页时,将新页的数据都插入到hash-map中,同一时候将被替换页的数据从hash-map中释放。而为了保证这点,在构建数据结构时,每个数据节点必须包括它所属的页号

class HashCache::Node {
public:std::string key_;std::string value_;class Page* page_;  // 该数据节点所属页
};

总结:由操作系统中的内存页面置换和虚拟缓存中的理论,迁移得到本项目server数据的存储和查询的实现思想。
本节思想的具体实现步骤,已再还有一篇博文中描写叙述。点击此处查看。

5. 多线程

2015/09/30 更新
对于多线程实现,因为线程的创建和销毁耗费时间和资源,因此对于大量的短的传输任务能够用线程池的方式实现。

一个server肯定是要支持多client通信的。那么应该使用多进程还是多线程呢?
由上文可知,全部的数据都是先存储到内存中。然后再转存到文件里,那么为了内存数据(缓存)的共享。选用多线程实现。
每当有一个client和server连接成功后,新建一个线程,将连接套接字传入线程处理函数。然后分离(detach)该线程。由该线程处理该client的全部通信。

因为是通过”共享内存“的方式实现线程之间的通信,可能存在多个client同一时候针对一个key的value做改动。同一时候有client在读取该key的value,造成数据的不同步?那应该怎样解决线程同步问题呢?

线程同步的方式:临界区。相互排斥锁。信号量,事件

本项目,採用相互排斥锁解决数据之间的同步问题,引入2个锁:写入锁和读取锁。当有一个正在put时,全部的put和get操作等待;当有get操作时,能够再有get操作,put操作等待。(读者写者问题的经典思想)
C++多线程编程,详情请參阅:C++11 编写 Linux 多线程程序
C++线程信号量和锁,详情请參阅:C++11 并发指南三(std::mutex 具体解释)

2015/09/30 更新
如上述所述,每一次操作缓存都要锁定整个缓存部分,能够做出例如以下改进:使用两个相互排斥量进行加锁,当读取或者写入一个页的数据时,对该页进行加锁。其它页能够正常訪问。可是,当将刚刚操作的页放到双向链表的头部时,须要对整个链表(整个缓存)进行加锁。这样粒度更小。效率更高。

6. 待改进。未实现的想法

  • 怎样增加断电缓存重建机制?
  • 怎样增加查询超时推断?
  • 需不须要线程调度?
  • 是否能把全部的key全放到一个set里,当cache中不存在该key时,去set里查找。假设存在然后才去遍历文件。不存在则直接返回。
  • 是不是还能够。将key。page_num对存入一个hash-map,依据key直接索引到其所属的页(相应文件号)。
  • 其它參考信息:淘宝自主开发的一个分布式key/value存储系统Tair,开发本项目时没有发现~~~

7. GitHub源代码

本项目开发环境Linux GCC4.8.4 ,C++ 11
源代码:https://github.com/qzxin/key-value-server
原文:key-value 多线程server的Linux C++实现:http://blog.csdn.net/quzhongxin/article/details/46927785

转载于:https://www.cnblogs.com/blfshiye/p/5394835.html

key-value 多线程server的Linux C++实现相关推荐

  1. 当SQL Server爱上Linux:配置 SQL Server 2017 上的可用性组初体验

    作者 | 张乐奕:Oracle ACE 总监,ACOUG (中国 Oracle 用户组)联合发起人.Oracle 数据库高可用解决方案与  Exadata 一体机专家.长于数据库故障诊断,性能调优.作 ...

  2. 配置 sql server linux,配置SQL Server on Linux(2)

    1. 前言 前一篇配置SQL Server on Linux(1),地址:http://www.cnblogs.com/fishparadise/p/8125203.html ,是关于更改数据库排序规 ...

  3. sqlserver 2017 ctp 2 linux,[SQL Server]On Linux奇幻旅程(三) SQLCLR

    因為一些原因,系統內使用了少量的SQLCLR,來讓T-SQL撰寫的Script可以透過SQL函數呼叫C# Method(SqlFunction)來取得特殊環境的運算結果,來試試SQL Server S ...

  4. linux sql server硬件要求,SQL Server On Linux(20)—— SQL Server On Linux性能(6)——针对性能的配置(Linux层面)...

    前面两篇大部分属于Windows和 Linux 平台公用,但是这一篇主要集中在Linux内核层面.因为本系列是Linux上的SQL Server(以2017.2019为主体)的介绍. 完整的列表可以查 ...

  5. sql2016是否支持linux,微软 SQL Server 支持 Linux 了,2017年 中将正式推出

    微软今日宣布推出 SQL Server2016 for Linux 开放私测,并打算在明年年 中正式发布这款关系数据库产品.这是微软该旗舰产品首次登陆 Linux.就像此前 Office 产品登陆了越 ...

  6. 个性化配置你的SQL Server on Linux

    问题引入 这天老鸟满面春风找到菜鸟:"鸟儿啊,看你最近研究SQL Server On Linux如鱼得水,干得不错啊.不过呢,这是一个张扬个性的年代,要创新,要与众不同,那你怎么在Linux ...

  7. SQL Server on Linux的文件和目录结构

    问题引入 "鸟儿啊,我记得你写过一篇<SQLServer On Linux Package List on CentOS>的文章,从这篇文章,我们很清楚的知道了SQL Serve ...

  8. [干货来袭]MSSQL Server on Linux预览版安装教程(先帮大家踩坑)

    前言 昨天晚上微软爸爸开了全国开发者大会,会上的内容,我就不多说了,园子里面很多.. 我们唐总裁在今年曾今透漏过SQL Server love Linux,果不其然,这次开发者大会上就推出了MSSQL ...

  9. linux下装sqlserver安装包,【sqlServer】CentOS7.x 上Microsoft SQL Server for Linux安装和配置...

    SQL Server Documentation https://docs.microsoft.com/en-us/sql/sql-server/sql-server-technical-docume ...

最新文章

  1. Ex 5_33 实现一个关于公式长度(其中所有文字总的出现次数)为线性时间的Horn公式可满足性问题_第十次作业...
  2. as3文本框的动态拖拽和编辑
  3. viewpager的优化
  4. bci测试如何整改_基于fNIRS技术的脑机接口(BCI)
  5. Git 更安全的强制推送,--force-with-lease
  6. AWT_Swing_图标按钮(Java)
  7. 一道组合数学题-马拦过河卒,很精彩
  8. swt中关于Text.setSelection()的记录
  9. 轻松掌握namedtuple
  10. mysql找出最大的天数_mysql 计算连续登录最大天数
  11. 将你的掘金小册制作成一整本PDF
  12. DCE/MS RPC旁窥
  13. “配置系统未能初始化” 异常解决
  14. imx6ull的boot, 之我的理解
  15. LabWindows操作Access
  16. 2010年翡翠岛露营
  17. 计算机配置内存在哪看,从哪里看电脑配置
  18. html 验证码功能 不区分大小写进行验证。
  19. allegro 倒圆角
  20. 台式计算机除尘方法,一种计算机主机箱除尘装置及其工作方法与流程

热门文章

  1. 基于IBM Cognos的高级报表制作技巧
  2. Flutter 一个优美的用户体验的登录页面 抖动提示 文本提示
  3. Flutter中文本输入框TexeFieldr键盘样式TextInputType总结TexeField设置不可编辑
  4. Vue填坑(v-model和:model)
  5. Mr.J--贪吃蛇demo
  6. Html和CSS在浏览器中常见的兼容性问题处理
  7. 逃跑吧少年辅助快乐羊儿吧
  8. Oracle 中启用 scott 用户 的方法
  9. django学习随笔:ManagementUtility
  10. extend_gcd求解不定方程/膜线性方程/乘法(模)逆元