首页 >要闻 > > 正文

每日观察!go 实现ringbuffer以及ringbuffer使用场景介绍

博客园 2023-06-05 16:32:30

ringbuffer因为它能复用缓冲空间,通常用于网络通信连接的读写,虽然市面上已经有了go写的诸多版本的ringbuffer组件,虽然诸多版本,实现ringbuffer的核心逻辑却是不变的。但发现其内部提供的方法并不能满足我当下的需求,所以还是自己造一个吧。

源码已经上传到github

https://github.com/HobbyBear/ringbuffer
需求分析

我在基于epoll实现一个网络框架时,需要预先定义好的和客户端的通信协议,当从连接读取数据时需要判读当前连接是否拥有完整的协议(实际网络环境中可能完整的协议字节只到达了部分),有才会将数据全部读取出来,然后进行处理,否则就等待下次连接可读时,再判断连接是否具有完整的协议。


【资料图】

由于在读取时需要先判断当前连接是否有完整协议,所以读取时不能移动读指针的位置,因为万一协议不完整的话,下次读取还要从当前的读指针位置开始读取。

所以对于ringbuffer 组件我会实现一个peek方法

func (r *RingBuffer) Peek(readOffsetBack, n int) ([]byte, error)

peek方法两个参数,n代表要读取的字节数, readOffsetBack代表读取是要在当前读位置偏移的字节数,因为在设计协议时,往往协议不是那么简单(可能是由多个固定长度的数据构成) ,比如下面这样的协议格式。

完整的协议有三段构成,每段开头都会有一个4字节的大小代表每段的长度,在判断协议是否完整时,就必须看着3段的数据是否都全部到达。 所以在判断第二段数据是否完整时,会跳过前面3个字节去判断,此时readOffsetBack将会是3。

此外我还需要一个通过分割符获取字节的方法,因为有时候协议不是固定长度的数组了,而是通过某个分割符判断某段协议是否结束,比如换行符。

func (r *RingBuffer) PeekBytes(readOffsetBack int, delim byte) ([]byte, error) 

接着,还需要提供一个更新读位置的方法,因为一旦判断是一个完整的协议后,我会将协议数据全部读取出来,此时应该要更新读指针的位置,以便下次读取新的请求。

func (r *RingBuffer) AddReadPosition(n int) 

n 便是代表需要将读指针往后偏移的n个字节。

ringbuffer 原理解析

接着,我们再来看看实际上ringbuffer的实现原理是什么。

首先来看下一个ringbuffer应该有的属性

type RingBuffer struct {     buf             []byte     reader          io.Reader     r               int // 标记下次读取开始的位置     unReadSize      int // 缓冲区中未读数据大小  }

buf 用作连接读取的缓冲区,reader 代表了原链接,r代表读取ringbuffer时应该从字节数组的哪个位置开始读取,unReadSize 代表缓冲区当中还有多少数据没有读取,因为你可能一次性从reader里读取了很多数据到buf里,但是上层应用只取buf里的部分数据,剩余的未读数据就留在了buf里,等待下次被应用层继续读取。

我们用一个5字节的字节数组当做缓冲区, 首先从ringbuffer读取数据时,由于ringbuffer内部没有数据,所以需要从连接中读取数据然后写到ringbuffer里。

如下图所示:

假设ringBuffer规定每次向原网络连接读取时 按4字节读取到缓冲区中(实际情况为了减少系统调用开销,这个值会更多,尽可能会一次性读取更多数据到缓冲区) write pos 指向的位置则代表从reader读取的数据应该从哪个位置开始写入到buf字节数组里。

writePos = (r + unReadSize) % len(buf)

接着,上层应用只读取了3个字节,缓冲区中的读指针r和未读空间就会变成下面这样

如果此时上层应用还想再读取3个字节,那么ringbuffer就必须再向reader读取字节填充到缓冲区上,我们假设这次向reader索取3个字节。缓冲区的空间就会变成下面这样

此时已经复用了首次向reader读取数据时占据的缓冲空间了。

当填充上字节后,应用层继续读取3个字节,那么ringBuffer会变成这样

读指针又指向了数组的开头了,可以得出读指针的计算公式

r = (r + n)% len(buf)
ringBuffer 代码解析

有了前面的演示后,再来看代码就比较容易了。用peek 方法举例进行分析,

func (r *RingBuffer) Peek(readOffsetBack, n int) ([]byte, error) {    // 由于目前实现的ringBuffer还不具备自动扩容,所以不支持读取的字节数大于缓冲区的长度   if n > len(r.buf) {        return nil, fmt.Errorf("the unReadSize is over range the buffer len")     }  peek:     if n <= r.UnReadSize()-readOffsetBack {        // 说明缓冲区中的未读字节数有足够长的n个字节,从buf缓冲区直接读取      readPos := (r.r + readOffsetBack) % len(r.buf)        return r.dataByPos(readPos, (r.r+readOffsetBack+n-1)%len(r.buf)), nil     }     // 说明缓冲区中未读字节数不够n个字节那么长,还需要从reader里读取数据到缓冲区中   err := r.fill()     if err != nil {        return nil, err     }     goto peek  }

peek方法的大致逻辑是首先判断要读取的n个字节能不能从缓冲区buf里直接读取,如果能则直接返回,如果不能,则需要从reader里继续读取数据,直到buf缓冲区数据够n个字节那么长。

dataByPos方法是根据传入的元素位置,从buf中读取在这个位置区间内的数据。

// dataByPos 返回索引值在start和end之间的数据,闭区间  func (r *RingBuffer) dataByPos(start int, end int) []byte {     // 因为环形缓冲区原因,所以末位置索引值有可能小于开始位置索引     if end < start {        return append(r.buf[start:], r.buf[:end+1]...)     }     return r.buf[start : end+1]  }

fill()方法则是从reader中读取数据到buf里。

fill 情况分析reader填充新数据到buf后,未读空间未跨越buf末尾

当从reader读取完数据后,如果 end := r.r + r.unReadSize + readBytes end指向了未读空间的末尾,如果没有超过buf的长度,那么将数据复制到buf里的逻辑很简单,直接在当前write pos的位置追加读取到的字节就行。

// 此时writePos 没有超过 len(buf)writePos = (r + unReadSize)
未读 空间 本来就 已经从头覆盖

当未读空间本来就重新覆盖了buf头部,和上面类似,这种情况也是直接在write pos 位置追加数据即可。

未读空间未跨越buf末尾,当从reader追加数据到buf后发现需要覆盖buf头部

这种情况需要将读取的数据一部分覆盖到buf的末尾

writePos := (r.r + r.unReadSize) % len(r.buf)   n := copy(r.buf[writePos:], buf[:readBytes])  

一部分覆盖到buf的头部

end := r.r + r.unReadSize + readBytes  copy(r.buf[:end%len(r.buf)], buf[len(r.buf)-writePos:])  

现在再来看fill的源码就比较容易理解了。

func (r *RingBuffer) fill() error {     if r.unReadSize == len(r.buf) {        // 当未读数据填满buf后 ,就应该等待上层应用把未读数据读取一部分再来填充缓冲区      return fmt.Errorf("the unReadSize is over range the buffer len")     }     // batchFetchBytes 为每次向reader里读取多少个字节,如果此时buf的剩余空间比batchFetchBytes小,则应该只向reader读取剩余空间的字节数   readLen := int(math.Min(float64(r.batchFetchBytes), float64(len(r.buf)-r.unReadSize)))     buf := make([]byte, readLen)     readBytes, err := r.reader.Read(buf)     if readBytes > 0 {       // 查看读取readBytes个字节后,未读空间有没有超过buf末尾指针,如果超过了,在复制数据时需要特殊处理      end := r.r + r.unReadSize + readBytes        if end < len(r.buf) {        // 没有超过末尾指针,直接将数据copy到writePos后面           copy(r.buf[r.r+r.unReadSize:], buf[:readBytes])        } else {          // 超过了末尾指针,有两种情况,看下图分析         writePos := (r.r + r.unReadSize) % len(r.buf)           n := copy(r.buf[writePos:], buf[:readBytes])           if n < readBytes {              copy(r.buf[:end%len(r.buf)], buf[len(r.buf)-writePos:])           }        }        r.unReadSize += readBytes        return nil     }     if err != nil {        return err     }     return nil  }
上一篇:惊天魔盗团2 惊天魔盗团2迅雷下载电影天堂 下一篇:最后一页
x
推荐阅读

每日观察!go 实现ringbuffer以及ringbuffer使用场景介绍

2023-06-05

惊天魔盗团2 惊天魔盗团2迅雷下载电影天堂

2023-06-05

世界热点评!逼仄的空间_逼仄

2023-06-05

2023抖音旅游行业白皮书(100页下载) 最新消息

2023-06-05

法网:王欣瑜/谢淑薇晋级女双第三轮

2023-06-05

全球热推荐:头发染什么颜色好看 好看的发色推荐(棕色短发) 每日快讯

2023-06-05

爱茉莉太平洋中国发布2022可持续发展报告

2023-06-05

龙之谷黑暗复仇者加点攻略_龙之谷黑暗复仇者加点 天天最新

2023-06-05

环球看点!ST迪威迅6月5日快速回调

2023-06-05

关注:工人日报:求职被盘问“婚育”,性别歧视何时休

2023-06-05

【机构调研记录】兴合基金调研国芯科技、亚威股份等5只个股(附名单)

2023-06-05

天天短讯!油价涨超2%!沙特7月额外减产100万桶/日 现有减产计划延长至2024年底

2023-06-05

十年生死两茫茫全诗阅读 十年生死两茫茫全诗|环球今亮点

2023-06-05

翁美玲为什么不选择黄日华(翁美玲为什么自杀)_热门

2023-06-05

虎牙卡尔直播间(虎牙卡尔个人资料)

2023-06-05

当前聚焦:法拉第未来FF2.0,贾老板的量产版终发售,试驾后的外媒这样评价

2023-06-04

祝贺!理大三位教授当选香港工程科学院院士!|热点

2023-06-04

罚金直逼 350万元!华贵人寿董事长等一众高管被问责,天价罚单“漏洞”如何补?|环球热资讯

2023-06-04

环球要闻:Acer宏碁笔记本电脑Aspire新蜂鸟fun S50-54原厂专用Win11系统工厂模式,恢复原装出厂

2023-06-04

世界最新:陕西合阳:上下联动 “干”沉一线 全力做好小麦抢收工作

2023-06-04

黄金比的例子有哪些_黄金比的例子 快资讯

2023-06-04

突发!绍兴一车开出路面,滚落山崖… 环球热头条

2023-06-04

美媒:无论美国多么努力,都将失败

2023-06-04

烽燧怎么读(秦汉烽燧)

2023-06-04

sbs沥青混凝土是什么意思_sbs沥青是什么意思

2023-06-04

群接龙要如何操作

2023-06-04

【热闻】2023乡宁“戎子杯”半程马拉松鸣枪开跑 选手领略山乡风光

2023-06-04

当前视讯!第12届东盟残运会在柬埔寨首都开幕

2023-06-04

应对需求压力 激发内生动力——当前工业经济走势观察 世界热头条

2023-06-04

即时看!晃眼!设计师晒给拉梅洛-鲍尔设计的表 满眼都是金黄和亮钻

2023-06-04

中日友好医院体检中心几点开门_中日友好医院体检中心 热文

2023-06-04

【全球聚看点】冒号引号的三种用法_冒号引号的用法及举例

2023-06-04

股市中的内盘和外盘是什么意思图解_股市中的内盘和外盘是什么意思

2023-06-04

怎么查苹果手机是不是国行_怎么看苹果手机是不是国行版

2023-06-04

六小龄童29岁女儿也愁嫁,看到本人照片后,网友这谁敢娶啊?

2023-06-04

全球最资讯丨多重因素导致欧洲机票价格大幅上涨

2023-06-03

全球观速讯丨新华社记者说|这里的乡村有文艺范儿!来看“洋记者”的旅行笔记

2023-06-03

专家“挑”出来的瓜菜,来试试?_环球热消息

2023-06-03

全球看点:梁山传奇170服_梁山传奇1 70

2023-06-03

讯息:莱奥:伊布对我在米兰的成长很重要 想继续和球队书写历史

2023-06-03

@高考考生 一组神奇的蓄能壁纸请查收!

2023-06-03

融业态、延链条、优布局——湖南娄底发展城郊高效农业经济见闻_最新消息

2023-06-03

光伏观察:隆基失其鹿,天下共逐之光伏观察:隆基失其鹿,天下共逐之光伏观察:隆基失其鹿,天下共逐之

2023-06-03

浪潮在鄂每年营收超10亿 近200家鄂企逐“浪”数字产业链|即时看

2023-06-03

民有所呼,我有所应—安仁城管高效处置群众诉求

2023-06-03

热门看点:惠人榨汁机,唤醒果汁原力美味

2023-06-03

【世界时快讯】ps移动工具不能用(ps移动工具用不)

2023-06-03

当前快讯:让“思想伟力”转为“办学动力”

2023-06-03

【天天报资讯】冀中能源井矿集团纪检委_冀中能源井矿集团

2023-06-03

华硕装win10不识别硬盘-(华硕装win10不识别硬盘怎么办)-全球今日讯

2023-06-03

直击河南多地抢收小麦:有收割机几乎“24小时作业”_当前消息

2023-06-03

港股异动 | 海吉亚医疗(06078)午后涨超4% 拟收购开远解化医院有限公司30%股权

2023-06-03

天天微速讯:海阳跨海大桥在哪里_海阳跨海大桥

2023-06-03

每日消息!八卦媒体爆baby杨颖新恋情?称两人同行回家疑同居

2023-06-03

百变公主酒酿蛋视频_百变公主酒酿蛋

2023-06-03

Ram为六号飓风放弃HemiV8-世界实时

2023-06-03

当前短讯!撼讯 RX 7900 XTX 显卡降至 6699 元:24GB 大显存

2023-06-03

当前看点!B站一季度给up主分成降8%!陈睿回应B站停更潮争议:up主挣钱是b站最关注的工作

2023-06-03

股票行情快报:飞龙股份(002536)6月2日主力资金净卖出123.68万元

2023-06-03

注意!*ST美谷将于6月19日召开股东大会 天天观察

2023-06-02

最新快讯!富士通发布AI平台Fujitsu Kozuchi

2023-06-02

世界简讯:星火成炬 | 遇见有趣的灵魂

2023-06-02

今日视点:劲仔食品(003000.SZ)拟向湖南咚咚增资8000万元 用于加快推进食品生产项目及研发中心项目建设

2023-06-02

中国铁物与天山股份签订战略合作协议|当前热点

2023-06-02

九月九日忆山东兄弟的诗意简短版_九月九日忆山东兄弟的诗意?

2023-06-02

ST泰禾:公司股票将被终止上市 6月5日起停牌|每日快讯

2023-06-02

阿斯顿·马丁DB12将6月12日国内首发_全球播资讯

2023-06-02

玉树治多:税法知识进校园 别样“六一”意义大

2023-06-02

南水北调东线通水以来惠及8359万人

2023-06-02

么字组词_么如何组词

2023-06-02

虚无世界3boss攻略_虚无世界3boss召唤大全

2023-06-02

天天视讯!火箭队很有可能留下助教老卢卡斯

2023-06-02

【环球播资讯】深交所:将开展深市可转债退市整理期业务仿真测试

2023-06-02

绿城14.6亿元竞得绍兴上虞区1宗商住地 溢价率13.53%

2023-06-02

惠州市新时代文明实践捡跑系列活动走进龙田镇_今日热闻

2023-06-02

五花肉和小米怎么做好吃(五花肉焖小米饭的做法)_精选

2023-06-02

心急如焚的意思用情景表现出来(心急如焚的意思)

2023-06-02

住宅贷款(住宅贷款)_每日关注

2023-06-02

天地在线6月2日开盘涨幅达5%

2023-06-02

科技新突破丨人造病毒载体可用于基因编辑_每日热点

2023-06-02

新界_关于新界简介

2023-06-02

边城世家房价_边城世家

2023-06-02

:奥迪e-tron怎么样及宾利添越Bentayga现在报价多少钱|全球今亮点

2023-06-02

中石化由来_中石化成立于哪一年 焦点热议

2023-06-02

上海第一生化药业有限公司雅乐_上海第一生化药业有限公司 环球百事通

2023-06-02

中国信通院林美玉:加强工业互联网安全体系建设,守护新型工业化高质量发展_环球精选

2023-06-02

每日时讯!100℃硫酸钠的溶解度(硫酸钠的溶解度)

2023-06-02

天天时讯:英国天然气期货跌超14% 欧洲大陆天然气期货跌9%

2023-06-02

幸福树修剪造型图片_幸福树的修剪方法图_天天新资讯

2023-06-02

世界聚焦:巴拉格:梅西很难回归巴萨,团队人员告知巴萨不能再等了

2023-06-02

全球通讯!医药市场营销专业考研方向(医药市场营销专业)

2023-06-02

【世界独家】平凉市4项文物保护研究课题获批立项!

2023-06-01

博主发布迄今最清晰小米汽车谍照:猎跑风格,体积特别大-全球要闻

2023-06-01

今夏自由身球员最佳阵:梅西、本泽马、菲尔米诺搭档锋线 观热点

2023-06-01

6月券商金股陆续出炉:TMT热度降温 “中特估”成热门金股聚集地 天天视讯

2023-06-01

巴特勒:希望内马尔来现场看NBA总决赛,他每个集锦我都看很多遍 天天资讯

2023-06-01

什么?艺术的敌人?在哪里? 要闻速递

2023-06-01

加拿大出台移民新政 优先吸纳具特定职业经验的新移民

2023-06-01

瑞普生物:拟投资设立瑞嘉基金,总金额2000万元

2023-06-01

全球观热点:新西兰奶粉代理商_新西兰婴幼儿配方奶粉出口协会

2023-06-01