上一篇文章聊了下bvar的整体组织架构,本篇将从最基本的计数器adder的使用出发,分析reducer和adder的源码来阐述下最基本也是最重要的bvar工作机制,后续再进一步深入分析相关基础组件和其他类型的bvar,我觉得这也是一个比较有效地阅读分析这种基础类库源码的方式。整个brpc源码解析系列其实也是这么一个组织结构,先从使用的部分切入,相对熟悉之后再进一步深入到各个具体的重要部分。
首先这里先贴一个上篇文章引用了的brpc官方文档里的例子:
bvar::adder value;
value << 1 << 2 << 3 << -4;
暂时不考虑全局曝光,以上也是bvar最常见的使用方式,用类型作为模板参数定义一个bvar变量后,就可以在各个线程中使用运算符<<往里面追加值了。adder继承自reducer(用于利用二元运算符把多个值合并为一个值),因此我们先来分析下reducer类,然后再看reducer。
reducer定义如下:
reducer继承自variable,variable中主要是曝光相关的,这里先不过多讨论。reducer是一个三个模板参数的模板类,分别是数据类型t;reduce操作符op;op的逆向操作符invop,默认是voidop,也就是没有逆向操作符。invop在定时采样的时候会用到,本篇文章暂不涉及,后面解析采样相关机制的时候再详细说明。注意这三个typedef,分别定义了combiner、agent和sampler的类型,和reducer本身的模板参数强相关。
类变量如下:
两种sampler因为不是必须的,所以是指针,而combiner和invop则是需要直接初始化的普通类变量,之所以只有invop是因为combiner里面会保存op,不需要重复保存。
构造函数如下:
将模板参数提供给给combiner和_inv_op初始化,sampler初始化为null,只有被window之类的追踪了才会去新建sampler,单纯的使用reducer不需要sampler。
看完了构造相关的再看下核心功能,根据bvar的用途,核心功能自然也就两个,往里写新值和读汇总值。
往里写值是通过重载的<<运算符实现的,这个重载函数里。首先是调用combiner的get_or_create_tls_agent函数拿到当前线程的agent,内部实现上就是tls的相关操作,如果还未分配则会新建agent,得到agent之后用操作符和值进行修改操作。
get_value比较简单,主要就是通过combiner聚合各个agent的值,不过要注意上面的check,这个check是说如果当前bvar已被window跟踪定时采样而且不存在invop,比如maxer,是不能调用这个函数的,因为依赖tls的关系,对于这类没有invop的reducer,在定时采样(take_sample)的时候需要reset agent,调用这个函数取不到正确值。
adder直接继承自reducer,定义如下:
很简洁,三个构造函数分别对应不全局曝光、不指定前缀曝光指定和前缀曝光的场景,分别会调用reducer的构造函数。注意这里继承的是reducer
这里主要介绍了下reducer和adder这个最常见的reducer,以及bvar最基本的数据读写机制,可以看到主要就是通过运算符调用底层的agent和combiner进行数据的聚合等操作。后面将会进一步介绍底层依赖的agent、combiner实现、window等时间窗口类型如何通过sampler采样等比较关键的机制。