thrift之TTransport层的内存缓存传输类TMemoryBuffer
内存缓存是简单的在内存进行读写操作的一种传输,任何时候想在上面写入数据都是放入缓存中,任何时候读操作数据也是来至于缓存。内存缓存的分配使用c语言的malloc类函数,分配的长度是需要长度的两倍,需要考虑这些内存缓存的使用范围。
同样这个类继承至缓存基类TBufferBase,默认的缓存大小是1024(static const uint32_t defaultSize = 1024;),所有的构造函数都调用函数initCommon,这个函数实现如下:
voidinitCommon(uint8_t* buf, uint32_t size, bool owner, uint32_t wPos) {
if (buf== NULL && size != 0) {//如果传递过来的buf为null并且size不为0
assert(owner);//断言这个内存缓存是不是自己所有,不是就报错不继续执行后面的代码了
buf =(uint8_t*)std::malloc(size);//是自己拥有的内存缓存就自己分配size大小的内存作为缓存
if(buf == NULL) {
throw std::bad_alloc();//分配失败抛出异常
}
}
buffer_= buf;//初始化缓存成员变量
bufferSize_ = size;//大小
rBase_= buffer_;基地址
rBound_= buffer_ + wPos;//界限
wBase_= buffer_ + wPos;//写缓存基地址
wBound_= buffer_ + bufferSize_;//写界限
owner_= owner;//是否自己拥有这段内存缓存
}
上面代码需要说明是:在初始化这个类的时候对于内存缓存是不是属于类自己拥有有不同的处理方式,后面介绍各个方针;还有一点就是读的边界其实就是写的起始地址,因为写入开始的地方就是没有有效数据的地方了,就是一个生存者和消费者的问题,这个后面介绍的一个函数computeRead会更新相关内容。下面我们就看看内存缓存采取的几种可配置的方针(对待内存缓存的方式),是定义一个枚举标识每一个方针的,定义如下:
enumMemoryPolicy
{ OBSERVE= 1
, COPY =2
,TAKE_OWNERSHIP = 3
};
下面分配分析这三种方针,第一种称为观察方针,意思是内存缓存传输类只是简单的存储一个指针存放到内存,它是调用者来保证内存缓存在剩余的时间指向一个有效的内存,并且在适当的时候清理它(注意:这种内存缓存是不允许写入数据的);第二种是内存缓存类内部的一种拷贝,调用者无任何责任;最后一种就是内存缓存类自己拥有的内存缓存,这个必须自己释放,并且在使用以前需要使用malloc分配。明白了三种内存方针了,就知道什么时候需要的是哪一种。内存使用方针除了被构造函数使用以外,我们在重置内存缓存区时也会使用到,而重置缓存函数是resetBuffer,有两个函数重载。在实现重载函数的时候使用了一点点优化手段,就是交换内存的时候使用std::swap,这个函数的好处就是如果是相同的内存分配器分配的就直接交换指针头就可以了。
同其它缓存类一样,内存缓存类通常采用快读和快写去操作数据,这些操作基类的实现已经满足要求了,只是在缓存中数据不足时就会调用慢读和慢写函数从更底层的传输去拿数据,所以慢写和慢读函数实现是不相同的。我们现在就看看内存缓存类的慢读和慢写函数。慢读函数readSlow函数现实如下:
uint32_t TMemoryBuffer::readSlow(uint8_t* buf,uint32_t len) {
uint8_t*start;//缓存读取开始地址
uint32_tgive;//缓存可以读取的长度
computeRead(len, &start, &give);//计算缓存中可以读取的开始地址和长度
// 把缓存中的数据拷贝到提供的buf中
memcpy(buf, start, give);
returngive;//返回实际读取到的数据,可能小于len
}
在读取数据以前先要计算可以读取的开始地址和长度(因为写函数会更新缓存中的数据),这个功能是函数computeRead实现,代码如下:
voidTMemoryBuffer::computeRead(uint32_t len, uint8_t** out_start, uint32_t*out_give) {
rBound_ = wBase_;//更新读取的边界,写的起始地址就是读取的最大限制地址
//取小者,因为数据可能不够,可利用的读长度就是写的起始地址减去读的起始地址
uint32_t give = std::min(len,available_read());
*out_start = rBase_;//返回的读起始地址
*out_give = give;//返回读取的可用长度
rBase_ += give;//更新读取的起始地址
}
下面继续分析慢写函数的实现,代码如下:
voidTMemoryBuffer::writeSlow(const uint8_t* buf, uint32_t len) {
ensureCanWrite(len);//确保能够写入
// 拷贝数据到写缓存的基地址
memcpy(wBase_, buf, len);
wBase_ += len;//更新写缓存的基地址
}
在写入之前需要保证是可以写入的,实现这个保证的函数是ensureCanWrite,代码如下:
void TMemoryBuffer::ensureCanWrite(uint32_tlen) {
uint32_t avail = available_write();//检查可以利用的缓存空间
if (len <= avail) {//如果空间足够就直接返回了
return;
}
if (!owner_) {//检查内存是否自己拥有
throwTTransportException("Insufficient space in external MemoryBuffer");//抛出不是自己拥有不能写入
}
// 根据需要增加缓存空间.
uint32_t new_size = bufferSize_;
while (len > avail) {//循环增加,每次都是上次的二倍大小,知道满足要求为止
new_size = new_size > 0 ? new_size * 2 :1;
avail = available_write() + (new_size -bufferSize_);//计算新的可利用空间:新-老+以前可利用
}
void* new_buffer = std::realloc(buffer_,new_size);//重新按照新大小分配缓存空间
if (new_buffer == NULL) {
throw std::bad_alloc();
}
bufferSize_ = new_size;//重置缓存大小
ptrdiff_t offset = (uint8_t*)new_buffer -buffer_;//计算偏移量,下面依次更新缓存值
buffer_ += offset;
rBase_ += offset;
rBound_+= offset;
wBase_ += offset;
wBound_ = buffer_ + bufferSize_;
}
内存缓存类还增加了一些额外的功能:例如按照字符串来读写,方便了对字符串的操作。
thrift之TTransport层的内存缓存传输类TMemoryBuffer的更多相关文章
- thrift之TTransport层的分帧传输类TFramedTransport
帧传输类就是按照一帧的固定大小来传输数据,所有的写操作首先都是在内存中完成的直到调用了flush操作,然后传输节点在flush操作之后将所有数据根据数据的有效载荷写入数据的长度的二进制块发送出去,允许 ...
- thrift之TTransport层的缓存传输类TBufferedTransport和缓冲基类TBufferBase
本节主要介绍缓冲相关的传输类,缓存的作用就是为了提高读写的效率.Thrift在实现缓存传输的时候首先建立一个缓存的基类,然后需要实现缓存功能的类都可以直接从这个基类继承.下面就详细分析这个基类以及一个 ...
- thrift之TTransport层的堵塞的套接字I/O传输类TSocket
本节将介绍第一个实现具体传输功能的类TSocket,这个类是基于TCP socket实现TTransport的接口.下面具体介绍这个类的相关函数功能实现. 1.构造函数 分析一个类的功能首先看它的定义 ...
- 3.NetDh框架之缓存操作类和二次开发模式简单设计(附源码和示例代码)
前言 NetDh框架适用于C/S.B/S的服务端框架,可用于项目开发和学习.目前包含以下四个模块 1.数据库操作层封装Dapper,支持多种数据库类型.多库实例,简单强大: 此部分具体说明可参考博客: ...
- thrift之TTransport类体系原理及源码详细解析1-类结构和抽象基类
本章主要介绍Thrift的传输层功能的实现,传输的方式多种多样,可以采用压缩.分帧等,而这些功能的实现都是相互独立,和上一章介绍的协议类实现方式比较雷同,还是先看看这部分的类关系图,如下: 由上面的类 ...
- thrift之默认传输类TTransportDefaults和虚拟传输类TVirtualTransport
默认传输类TTransportDefaults提供了抽象类TTransport的默认实现,实现了非虚拟的方法(*_virt) read(), readAll(), write(),borrow() a ...
- 基于.net的通用内存缓存模型组件
谈到缓存,我们自然而然就会想到缓存的好处,比如: 降低高并发数据读取的系统压力:静态数据访问.动态数据访问 存储预处理数据,提升系统响应速度和TPS 降低高并发数据写入的系统压力 提升系统可用性,后台 ...
- Memcached内存缓存技术
Memcached是什么,有什么作用? Memcached是一个开源的.高性能的内存缓存软件,从名称上看Mem就是内存的意思,而Cache就是缓存的意思. Memcached通过在事先规划好的内存空间 ...
- 基于.net的分布式系统限流组件 C# DataGridView绑定List对象时,利用BindingList来实现增删查改 .net中ThreadPool与Task的认识总结 C# 排序技术研究与对比 基于.net的通用内存缓存模型组件 Scala学习笔记:重要语法特性
基于.net的分布式系统限流组件 在互联网应用中,流量洪峰是常有的事情.在应对流量洪峰时,通用的处理模式一般有排队.限流,这样可以非常直接有效的保护系统,防止系统被打爆.另外,通过限流技术手段,可 ...
随机推荐
- VS扩展开发框架
VsSharp:一个VS扩展开发框架(上) 上篇:设计 一.引子 自2008年起开发SSMS插件SqlSharp(er)的过程中,有一天发现多数代码都大同小异,就像这样. Commands2 comm ...
- cocos2d-x3.x 设计与实现弹出对话框
要定义一个类PopupLayer 代码PopupLayer.h #ifndef __crossDT_PopupLayer__ #define __crossDT_PopupLayer__ #inclu ...
- Poj 3517 And Then There Was One Joseph核心问题
基本上纯Joseph核心问题,只是第一步多一件.m. 然后你就可以用获得的递推公式: Win(n) 代表n当个人的中奖号码, 然后,Win(n)必须相等Win(n-1).当一个人将在下一次删除队列. ...
- SQL Prompt5 破解版+使用说明 [转]
SQL脚本越写越多,总是觉得编写效率太过于低下,这和打字速度无关.在我个人编写SQL脚本时,至少会把SQL的格式排列成易于阅读的,因为其他人会阅读到你的SQL,无论是在程序中或是脚本文件中,良好的排版 ...
- 从头开始学JavaScript (十)——垃圾收集
原文:从头开始学JavaScript (十)--垃圾收集 一.垃圾收集 1.1javascript垃圾收集机制: 自动垃圾收集,执行环境会负责管理代码执行过程中的使用的内存.而在C和C++之类的语言中 ...
- R语言数据分析系列六
R语言数据分析系列六 -- by comaple.zhang 上一节讲了R语言作图,本节来讲讲当你拿到一个数据集的时候怎样下手分析,数据分析的第一步.探索性数据分析. 统计量,即统计学里面关注的数据集 ...
- Spring框架和MVC原理
Spring框架和MVC原理 目录 Spring框架 SpringMVC工作原理 参考资料 回到顶部 Spring框架 Spring当前框架有20个jar包,大致可以分为6大模块: Core Cont ...
- UVA 10139 Factovisors(数论)
Factovisors The factorial function, n! is defined thus for n a non-negative integer: 0! = 1 n! = n * ...
- android ndk通过遍历和删除文件
在做移动开发过程,难免有些本地文件管理操作.例如,很常见app随着微博.微信要清除缓存功能,此功能是走app文件夹.然后删除所有缓存文件.使用java的File类能够实现本地文件遍历及删 ...
- Tair LDB基于Prefixkey找到如何提取一系列性能优化项目key的prefix_size
眼下项目已快截止,编码任务也基本完毕.如今主要是性能測试. 项目是依照"Tair LDB基于Prefixkey的范围查找性能优化项目提议方案"的步骤一步步完毕的,首先先介绍第一个关 ...