每周一篇又来了。这次主要介绍netio的buffer管理器。 首先buffer管理是每一个网络层不可回避的问题。怎么高效的使用buffer是很关键的问题。这里主要介绍下我们的netio是怎么处理。说实话 这是我见过比较蛋疼buffer管理。  反正我是看了好几天 才看明白的。

     最近看了下Qcon2016的视频.里面很多大牛介绍分布式平台。 感觉特别牛逼~~。 感觉我们的分布式相比他们的这些还是简陋了点。感兴趣的同学可以去看看

     http://daxue.qq.com/content/special/id/20

1.1  我们先看下 一次系统调用recv就能收到完整包的情况

1)首先通过系统调用函数recv  会每次把从TCP读到的数据放到
 
m_achRecvBuf[TPT_RECV_BUF_LEN];
这个buf大小为128*1024
2、判断包头。
先判断是否是0x5a5a
然后解析包头判断 需要发送过来的总长度  如果大于1024*1024就报错。
1024*1024是在初始化的时候申请的大小。 我们的一个最大请求包已经限定为1M
 
如果发现tcp一次就能收到完整的包。
netio并不会使用我们字节的buf管理器
m_pSink->OnRecv而是直接丢给netio的app类去处理。
然后等netio中app类对包做了具体的处理后。
网络层 发现处理完以后就会直接  重新跳到while 循环中等待新事件
 
int CNetHandleMng::_RetrievePkgData(int nHandle,char* pRcvBuf,int nBufLen)
{
......
//当前数据包已经读取完成
m_pSink->OnRecv(nHandle,pRcvBuf+TPT_HEAD_LEN,dwPkgLen);
return (dwPkgLen + TPT_HEAD_LEN);
}
int CNetHandleMng::OnRecv(int nHandle,char* pRcvBuf,int nBufLen)
{
stConn* pConn = _GetConn(nHandle);
if( NULL == pConn )
{
std::stringstream oss;
oss<<"reactor report recv data for connection handle"<<nHandle<<" but we cann't found the connection data"<<std::endl;
m_pSink->ReportTptError(__FILE__,__LINE__,__func__,oss.str().c_str());
return ;
} int nReadLen =;
if( == pConn->m_pRcvBuf->m_nDataLen )
{ nReadLen = _RetrievePkgsData(nHandle,pRcvBuf,nBufLen);
if( nReadLen < )
return nReadLen;//reactor层会自动关闭连接 if( nReadLen >= nBufLen )
return ;//数据已经处理完毕
.....
}

我们发现在一次recv能收完整个数据包的时候。平台没用字节的buf管理器。而是直接就给netio app类来处理了

 

1.2  我们先看下 一次系统调用recv收不完包的情况。

5.2.1  我们先分析下一个具体的例子 然后再慢慢的归纳和总结

a)为什么是256个指针?
初始化的时候  
CNetioApp::CNetioApp():CNetMsgqSvr(4096*5,1024*1024,4*1024)

规定了一个最大请求包是 1024*1024

buffer管理器中 最小的一个buf大小是4*1024
 
(1024*1024+4*1024 -1 ) / (1024*4)  = 256
然后分配256个大小的二维指针。

所以初始化的时候  会设置一个大小为256的二维指针。注意这里只是创建二维指针。当时并没有给每个指针指向的对象分配空间。

 
b) 当客户端首次connet的时候。会继续初始化一些信息
当客户端connet请求来临时候。会去buffer管理器取一块buffer。
默认情况下。都是取p[0]里的buffer。  
当buffer管理器 发现p[0]为空的时候。  会去创建10个buffer 。这里10是写死的。 由于是p[0]是第一行。那么每个buffer的大小是4096.
 
这10个buffer 是一个链表。 index=0是最先创建的。index=9是最后创建的。
 
如上图。 index=9被拿出去了 。但其实这个时候并没有数据过来。
这个不管。被connet信息结构体指向的buffer。我们都认为是在使用中。
这个时候p[0] 就会跳到index=8.
注意 二维指针  永远是指向未被使用的buffer。这很重要。如果没有空间。会继续创建buffer
 
 
c) 第一次recv   16384数据
这里发现收到的16384个字节 大于4096个字节。
则buffer管理器。会在p[3] 这里申请10个buffer块。
每块  1024*4*4=16384  刚好 放下recv的16384数据
 
因为不用p[0]的buffer块。 则先回退p[0]的指向。从index=8  到index=9
然后让connet信息块 重新指向p[3]的index=9
前面说了 二维指针一定要指向未被使用的buffer。  所有p[3] 指向index=8
 
同时在m_nDatalen里面记录 已经保存的字节数
 
这个时候还没有收完 需要继续收数据
 

d) 第二次recv   16384数据
注意 第二次也收到了16384.
那么第二次的16384  +  上次的16384 = 32768 
这个时候p[3] 这一列的buffer放不下。  需要重新创建buffer
 
这个时候在p[7] 这一列上创建   4*1024*8 =32768  刚好放下所有数据
这个时候在p[7] 创建10个buffer 。 每个buffer为4*1024*8
 
接着还是要归还p[3]  buffer的使用权。这个时候吧p[3]的指针指向index = 9  同时把 index=9里面的m_nDatalen设置为0.
这样就表示p[3]的index=9被 释放了。  但是其实index=9还是有内容的并没有清除。
 
接着我们把累加的数据 放到p[7]的index=9里面
 
e)
 后面都是类似的逻辑。 归还空间。然后申请新空间
我看总共131158个字节的内容  recv  6次。
buffer管理器 替换了包括最开始初始化的的buffer总共花了7次 才找到合适的buffer来存放内容
p[32] 的buffer大小 为4*1024*33=135168

1.2.2 .  正常情况下的buffer总大小

 
        在netio包了一段时间后。假如各种包的大小都存在。那么最后会怎么样~~。
        这256个指针  都会被创建buffer。 没一列的buffer大小是 4*1024*行数。比如第一行就是4*1024*1.
最后一行就是4*1024*256.
        而且被创建的buffer不会被释放。我们来计算下这个总的buffer会多大。
        4*1024*(1+2+3...+256)=134742016 134742016 1048576
       134742016 / (1024 * 1024) = 128 M  
        大概128兆。但是 这只是并发请求不高的情况下。我们来看下并发请求高的情况下会怎么样

1.2.3 .  高并发场景下 buffer的总大小

我们假设并发来了20个请求。为了使分析简单。我们就认为。每个请求数据都在4*1024以内。
如下图  用户p[0]的10个buffer以后。
p[0] 这个时候是指向了NULL的。
但是这个时候还有请求该怎么帮。
继续分配
这个时候再分配 10个buffer
如下图  又重新分配了10个buffer。 跟在0的后面。 在来的请求就是在后面的10个buffer中分配。
代码中是 每次网络层向buffer管理器申请buffer的时候。
会去查看 二维指针是否为空。不为空则把空间给出来存数据。
如果为空。则会申请10个内存
 
所有看到这。当并发请求很大的时候。这个buffer会突增大到一个很恐怖的数字。
而且由于 创建后的空间不会被删除。会一直维持一个很高的内存占用
 
 
1.2.4  buffer的释放。
我们以下图为例子。
请求 7、4、8 先后是否空间。
那么p[0] 先是指向 index=3  然后指向index=6  最后指向index=2
 
那么p[0] 指向的其实是  没有被使用的空间的  链表头。  
p[0]  ->index2->index6->index3
 
下次又有新请求来的时候。 则把index2分配给新请求使用
 

总结下:
1)netio的buffer初看还是很麻烦的。看了2、3天才看明白。主要是实现的思想还是有点复杂。但是个人感觉看下来并没有什么特别惊艳的地方。实现上感觉有点像google的tcmall。
2)申请不释放的好处就是不会产生大量内存碎片。
3)但是高并发场景下回内存爆增。且不会下去。
4)还有针对一个大包。需要多次recv。那么buffer管理器会不停的替换buffer来存数据。而不是解析包头。确定包的大小。然后指定一个刚好符合的buffer。然后每次recv数据都放在这个buffer里。而不用不停的替换buffer.
 

大型分布式C++框架《四:netio之buffer管理器 下》的更多相关文章

  1. Storm分布式实时流计算框架相关技术总结

    Storm分布式实时流计算框架相关技术总结 Storm作为一个开源的分布式实时流计算框架,其内部实现使用了一些常用的技术,这里是对这些技术及其在Storm中作用的概括介绍.以此为基础,后续再深入了解S ...

  2. 开源分享 Unity3d客户端与C#分布式服务端游戏框架

    很久之前,在博客园写了一篇文章,<分布式网游server的一些想法语言和平台的选择>,当时就有了用C#做网游服务端的想法.写了个Unity3d客户端分布式服务端框架,最近发布了1.0版本, ...

  3. Django准备知识-web应用、http协议、web框架、Django简介

    一.web应用 Web应用程序是一种可以通过web访问的应用程序(web应用本质是基于socket实现的应用程序),程序的最大好处是用户很容易访问应用程序,用户只需要有浏览器即可,不需要再安装其他软件 ...

  4. Unity 游戏框架搭建 2018 (一) 架构、框架与 QFramework 简介

    约定 还记得上版本的第二十四篇的约定嘛?现在出来履行啦~ 为什么要重制? 之前写的专栏都是按照心情写的,在最初的时候笔者什么都不懂,而且文章的发布是按照很随性的一个顺序.结果就是说,大家都看完了,都还 ...

  5. Python分布式爬虫必学框架Scrapy打造搜索引擎

    Python分布式爬虫必学框架Scrapy打造搜索引擎 部分课程截图: 点击链接或搜索QQ号直接加群获取其它资料: 链接:https://pan.baidu.com/s/1-wHr4dTAxfd51M ...

  6. Python分布式爬虫必学框架Scrapy打造搜索引擎 ✌✌

    Python分布式爬虫必学框架Scrapy打造搜索引擎  ✌✌ (一个人学习或许会很枯燥,但是寻找更多志同道合的朋友一起,学习将会变得更加有意义✌✌) 第1章 课程介绍 介绍课程目标.通过课程能学习到 ...

  7. Django框架-目录文件简介

    Rhel6.5 Django1.10 Python3.5 Django框架-目录文件简介 1.介绍Django Django:一个可以使Web开发工作愉快并且高效的Web开发框架. 使用Django, ...

  8. Net框架下-ORM框架LLBLGen的简介

    >对于应用程序行业领域来说,涉及到Net框架的,在众多支持大型项目的商用ORM框架中,使用最多的目前了解的主要有三款: 1.NHibernate(从Java版移植来的Net版). 2.微软的EF ...

  9. Unity3d&C#分布式游戏服务器ET框架介绍-组件式设计

    前几天写了<开源分享 Unity3d客户端与C#分布式服务端游戏框架>,受到很多人关注,QQ群几天就加了80多个人.开源这个框架的主要目的也是分享自己设计ET的一些想法,所以我准备写一系列 ...

  10. asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程

    最近在学习张善友老师的NanoFabric 框架的时了解到Exceptionless : https://exceptionless.com/ !因此学习了一下这个开源框架!下面对Exceptionl ...

随机推荐

  1. Task的运行过程分析

    Task的运行过程分析 Task的运行通过Worker启动时生成的Executor实例进行, caseRegisteredExecutor(sparkProperties)=> logInfo( ...

  2. [转] vim 正则表达式 很强大

    毋庸多言,在vim中正则表达式得到了十分广泛的应用. 最常用的 / 和 :s 命令中,正则表达式都是不可或缺的. 下面对vim中的正则表达式的一些难点进行说明. 关于magic vim中有个magic ...

  3. TP框架多表联查

    join方法import("@.ORG.Page");        $Form   =   M('gly');        $where='';        if ($_PO ...

  4. oracle、db2、sybase大型数据库面试总结

    1. oracle数据库单例.多例模式. 数据库创建之后会有一系列为该数据库提供服务的内存空间和后台进程,称为该数据库的实例. 每一个数据库至少会有一个实例为其服务. 2. mysql获取字段的长度用 ...

  5. c - 水仙花数.

    #include <stdio.h> #include <math.h> /* *打印出所有的“水仙花数” ,所谓“水仙花数”是指一个三位数,其各位数字立方和等于该数本身. * ...

  6. iptables里filter表前面几个数字的意思

    一般的linux系统iptables配置文件filter表前面都带下面三行,但是具体是什么意思呢! *filter:INPUT ACCEPT [0:0]:FORWARD ACCEPT [0:0]:OU ...

  7. gc内存回收机制

    判断哪些对象可回收 GC是通过对象是否存活来决定是否进行回收,判断对象是否存活主要有两种算法:引用计数算法.可达性分析算法 引用计数算法 引用计数的算法原理是给对象添加一个引用计数器,每被引用一次计数 ...

  8. 给控制器添加工具栏(Swift语言)

    //懒加载工具条 private lazy var toolBar: UIToolbar = UIToolbar() //设置底部的工具条 private func setToolBar() { // ...

  9. Mysql 5.7.9 cmake boost.cmake 处理

    环境Centos 6.7 x64 mininal 今天突然编译Mysql 5.7.9 按之前的cmake .的方式 发现报错了..提示 需要boost -- BOOST_INCLUDE_DIR /us ...

  10. 一个使用CSocket类的网络通信实例

    http://www.cppblog.com/changshoumeng/archive/2010/05/14/115413.html 3.8 一个使用CSocket类的网络通信实例 本例采用CSoc ...