您现在的位置是:首页 > 正文

brpc组件bvar源码解析(四)Sampler、SamplerCollector和Window类簇

2024-02-01 00:02:02阅读 1

1.Sampler类

Sampler是所有采样类的基类。采样类中最重要的是take_sample函数,采样类的schedule函数调用之后,它的take_sample函数将会被一个专门的线程每1秒定时调用。

Sampler类的定义:
在这里插入图片描述

Sampler是一个纯虚类,take_sample是纯虚函数需要子类实现。

Sampler继承自LinkNode,即拥有了previous_和next_指针,可以作为双向链表的节点。

Sampler有两个成员变量,一个是_used表示当前采样类是否生效;一个是_mutex保护_used。

构造函数、析构函数和destroy函数

默认_used设为true。

如果采样类不再使用,则调用其destroy函数,设置_used为false,实现延迟删除,至于从哪里删除,后面会讲到。
在这里插入图片描述

schedule函数:

在这里插入图片描述
注册当前采样类的地址到全局单例SamplerCollector中,后面讲到SamplerCollector时再详细介绍内部实现。

2.Sample类

定义:
在这里插入图片描述
这个类很简单,保存了bvar的类型T的值data,和时间time_us。它用于保存一次采样的结果和采样时的时间。

3.SamplerCollector类

定义:
在这里插入图片描述
上面的注释可以帮助理解为什么SamplerCollector要设计成继承自Reducer。正常情况我们一般的实现是这样,用一个容器C保存所有注册的采样类和一个mutex M保护,设置定时线程来定期采样。但是这个性能不好,每次开始采样前都要用M加锁保护,以至于创建Window<>(创建Window<>是会创建sampler类、并注册到SamplerCollector中)需要等锁释放。如果想让创建Window<>的开销可以忽略不计,还需要更好的方案。

这里就提供了一个更好的方案,就是继承自Reducer<Sampler*, CombineSampler>。这样SamplerCollector就是一个bvar,保存的是Sampler*,累加器是CombineSampler,看一下CombineSampler的实现:
在这里插入图片描述

前面说了Sampler*是可以作为双向链表的节点的。CombineSampler的两个参数是2个双向链表,它的操作就是把两个双向链表连接起来。

综合上面的信息,SamplerCollector继承自Reducer<Sampler*, CombineSampler>之后,调用operator<<函数可以轻易的添加Sampler到stl数据中,这个过程是没有锁的,就解决了前面说的问题:想让创建Window<>的开销可以忽略不计;调用get_value函数可以通过累加器CombineSampler将所有的Sampler组成一个双向链表。

成员变量

在这里插入图片描述
_created:是否创建了采样线程
_stop:是否需要退出采样线程中的循环
_cumulated_time_us:采样线程中遍历所有的Sampler*进行采样花费的时间的总和
_tid:采样线程id

函数create_sampling_thread

SamplerCollector类的构造函数中就调用了函数create_sampling_thread。
实现:
在这里插入图片描述

(1)创建采样线程执行函数sampling_thread
(2)如果采样线程创建成功,则通过pthread_atfork注册当fork子进程时在子进程上下文中fork函数返回之前调用child_callback_atfork函数
A)child_callback_atfork函数中创建全局SamplerCollector单例,并调用其after_forked_as_child函数
B)after_forked_as_child中重置_created为false,并调用create_sampling_thread以实现在子进程中创建采样线程。

采样线程执行run函数

采样线程首先调用的函数sampling_thread,它的实现很简单就是调用SamplerCollector::run函数:
在这里插入图片描述

所以看run函数:
在这里插入图片描述
1.获得所有的Sampler
1)调用SamplerCollector::reset函数,Reducer::reset函数中执行了_combiner.reset_all_agents,通过前文知道它把所有stl保存的Sampler*连接成一个双向链表s返回,原来的保存的地方重置为null。
2)reset函数返回的双向链表s和root连接到一起。即root是之前就已经获得的所有的Sampler构造的双向链表,s是最新加入的Sampler构造的双向链表。
2.遍历所有的Sampler
1)遍历root中的所有Sampler,判断是否被destroy(_used字段),如果是,则从root中删除、并delete,否则进行2)
2)调用Sampler::take_sample进行采样操作,take_sample是Sampler具体子类实现的
3.sleep
1)如果上述全过程花费的时间不超过1s,则sleep多余的时间
2)如果花费超过1s,则consecutive_nosleep+1,consecutive_nosleep超过WARN_NOSLEEP_THRESHOLD(默认值2)时打印log

4.ReducerSampler类

ReducerSampler是Sampler类的子类,定义如下:
在这里插入图片描述
继承自Sampler,有4个模板参数:
R:一般就是Reducer类或Reducer子类
T、Op、InvOp:Reducer类的三个模板参数

成员变量:
在这里插入图片描述
_reducer:保存传入的R类型对象指针
_window_size:窗口大小
_q:保存所有Sample的有界队列(即循环队列);当容量全部有值、再加入新增时将替换最早加入的值。

构造函数

在这里插入图片描述
初始化_reducer,设置_window_size为1;调用take_sample。

函数take_sample

函数是具体的采样操作。
在这里插入图片描述
1._q容量不足_window_size,则扩容量
2.获得_reducer最新值
1)如果没有反向op,例如Maxer、Miner,则获得当前的最新值返回、并重置原保存的值
2)如果有反向op,例如Adder,则获得当前的最新值返回即可,之后需要窗口内的值时可以通过反向op操作来得到
3)无论有没有反向op都可以通过1)来实现,但是对于有反向op的,用2)效率会更高
3.加入到_q中
1)保存当前时间(采样时间)到time_us
2)采样结果Sample保存到_q中

函数get_value

在这里插入图片描述
1.获得窗口前后的采样值
1)最新采样的值保存到latest
2)从现在往过去数第window_size个采样结果保存到oldest;如果全部采样结果就不足window_size个,则保存最老的采样结果到oldest
3)上述两个值作为窗口前后的采样值
2.根据上述值计算窗口内的值
1)如果没有反向op,那么就老老实实地从oldest依次到latest,依次执行op操作(op例如取最大值)
2)如果有反向op,那么就可以根据inv_op、oldest、latest快速计算得到(例如对于Adder,latest - oldest就是窗口类的值)
3)oldest、latest采样时间间隔保存到result->time_us

使用ReducerSampler的地方

ReducerSampler中有如下定义:
typedef detail::ReducerSampler<Reducer, T, Op, InvOp> sampler_type;

在Reducer::get_sampler首次被调用时将创建ReducerSampler对象(传入this指针)、并加入到全局单例SamplerCollector中:
在这里插入图片描述

那么Reducer::get_sampler什么时候被调用呢,下面的Window<>创建的时候。下面会介绍到。

5.WindowBase类

定义:
在这里插入图片描述
模板参数:
R:具体的bvar类型
SeriesFrequency:和SeriesSampler有关,我们这次不介绍这个。

成员变量:
在这里插入图片描述
_var:指针指向具体的bvar的对象
_window_size:窗口大小;根据前面我们知道,窗口是指采样点的窗口,虽然采样线程是每1s采样一次,但由于要遍历所有的Sampler的take_sample,所以对于每个Sampler而言不一定是每1s采样一次
_sampler:属于bvar对象_var的采样对象,弱引用
_series_sampler:我们这次不介绍SeriesSampler相关的。

构造函数

在这里插入图片描述

WindowBase类的构造函数需要传2个内容:bvar对象的指针和窗口大小。
_sampler赋值bvar对象的sampler指针,这里是弱引用,并修改_sampler的窗口大小。

get_span函数

在这里插入图片描述
获得指定窗口大小的Sample对象值。

get_value函数

在这里插入图片描述

调用get_span获得指定窗口大小的Sample对象值,然后返回Sample.data,即具体的bvar存储的类型的值。

6.WindowBase子类

1.Window类继承自WindowBase,比较简单,不赘述
2.PerSecond类也继承自WindowBase,特别的,它override了get_value函数:
在这里插入图片描述
它在获得了Sample.data之后,又除了一下窗口首末真正的采样时间间隔,正如类名一样表示每秒的采样值。

7.最后

其他的一些类,例如LatencyRecorder(时延),PassiveStatus(get_value时调用指定函数对象获得value)都比较简单,就不赘述了。

网站文章

  • 练习-C语言恶搞程序

    在网上看了C语言也能干大事的视频,觉得不错,便把最后留下的作业,练习写一下。作为自己的提高。练习题如下:    自己动手写恶搞程序。运行以后弹出询问对话框(有【是】、【否】两个按钮以及问号图标)“你是好人吗?”,如果点击【是】,则弹出对话框(只有一个【确定】按钮以及一个警告图标)“你看你就不像好人,点击【确定】开始格式化C盘!”;如果点击【否】,则弹出对话框(有【重试】、【取消

    2024-02-01 00:01:34
  • vs2017 正则表达式删除C#中以///开头的summary注释

    使用替换方式,将以下正则表达式替换为空: [\t]*///[^\n]*\n 即可将全部以///开头的summary注释删除

    2024-02-01 00:01:27
  • 71 Simplify Path

    Given an absolute path for a file (Unix-style), simplify it.For example,path = &quot;/home/&quot;, =...

    2024-02-01 00:01:21
  • 计算机窗口菜单栏中的选项,电脑右键点击界面下拉菜单中的选项怎么删除啊?...

    要删除这些无用的右键菜单项,请按下述方法操作:1. 单击Windows的“开始”菜单,单击“运行”,在“打开”框中键入“regedit”,单击“确定”按钮,打开“注册表编辑器”窗口。2. 展开“HKE...

    2024-02-01 00:00:51
  • 猫为什么要抓老鼠

    鼠猖獗,菜市购花猫一只,甚矫健,回家养之。未及一月出现异象:此猫不与鼠为敌,视鼠若无睹,鼠偷食,猫仅睥睨之,仍以四四拍闲庭信步。忧虑,长此以往会否堕落至与鼠打情骂俏。饿之,果然奋起捕鼠,三日捕两鼠,奖汤姆牌高级猫粮一盒,猫喵喵……再过一月异象升级:此猫饱之则不捕鼠,饿之则戏鼠,兴趣盎然、捕而不食,专等高级“汤姆”猫粮。余大怒,责之,不予猫粮以饿其体肤壮其猫志,未及,猫冷眼看我,抓烂沙发决绝而去。于

    2024-02-01 00:00:45
  • css清除浮动无效,CSS清除浮动方法总结

    css清除浮动无效,CSS清除浮动方法总结

    清除浮动的原因假设一个 div 内部有个浮动的 div,当内部 div 的高度要比外层的父级 div 高度大时,将会导致父级 div 高度无法随着内部 div 的高度自适应,这是由于浮动元素已经脱离了...

    2024-02-01 00:00:37
  • (转)张一鸣10年面试过2000人:混得好的年轻人都有这 5 种特质!

    转:https://mp.weixin.qq.com/s?__biz=MjM5OTAzMjc4MA==&mid=2650052208&idx=1&sn=5cb9750aaaca392dec02e221...

    2024-02-01 00:00:08
  • BugKu: 粉色的猫

    BugKu: 粉色的猫

    这道题目涉及到了DNA编码,BPG图片格式,PDU编码,Piet编程语言,猫脸变化,一共是5个知识点,这道题让我受益良多,出题者水平真高啊!httpshttpshttpshttpshttpshttpshttpshttpshttpshttpshttpshttpshttps。

    2024-02-01 00:00:01
  • iOS组件化方案对比

    iOS组件化方案对比

    背景 随着公司业务的不断发展,项目的功能越来越复杂,各个业务代码耦合也越来越多,代码量也是急剧增加,传统的MVC或者MVVM架构已经无法高效的管理工程代码,因此需要用一种技术来更好地管理工程,而组件化(也可称为模块化)是一种能够解决代码耦合的技术。项目经过组件化的拆分,不仅可以解决代码耦合的问题,还可以增强代码的复用性,工程的易管理性等等。 市场上的方案: 方案一、url-block 这是...

    2024-01-31 23:59:53
  • 一般服务器的并发响应数量,服务器高并发?4个简单的处理方法!

    一般服务器的并发响应数量,服务器高并发?4个简单的处理方法!

    原标题:服务器高并发?4个简单的处理方法!高并发问题是大部分服务器都经历过的,由于资源的有限性,其同时处理请求的能力自然也有限制。当高并发出现时,服务端的处理和响应速度会大幅降低,更严重的会使服务器崩...

    2024-01-31 23:59:47