1.Variable类
Variable是所有bvar的基类,是一个纯虚类。拥有的唯一的成员变量是_name。
Variable类中的接口分为几类:
- 描述相关的
子类实现纯虚函数describe,目的是将bvar的值写入ostream。
get_description不是纯虚函数,它调用了describe写入ostringstream,返回ostringstream的string。
- 曝光相关的
例如:expose,expose_impl,expose_as,list_exposed,count_exposed,describe_exposed,dump_exposed等。核心是expose_impl,下文介绍。
函数expose_impl
函数expose_impl是实现曝光的核心逻辑。所谓“曝光”就是把bvar的地址存到一个公共的map中,以便后续可以遍历和查询到。
具体的做法是:
(1)根据prefix+name生成统一格式的曝光名称_name:非字母、非数字的全部转成下划线
(2)根据(1)生成的曝光名称_name查询所属的VarMapWithLock:
首先对_name进行了简单的hash,然后对VarMapWithLock数组s_var_maps的长度SUB_MAP_COUNT取模得到i,返回s_var_maps的第i个成员。这里这么做的目的是为了减少VarMapWithLock中的锁竞争的频率。
(3)根据_name查询VarMapWithLock中是否存在key为_name的记录
VarMapWithLock其实就是加了锁的hashmap。
如果查不到,则插入pair<name, VarEntry>,VarEntry的var变量赋值this值,display_filter变量赋值入参display_filter。
display_filter是枚举类型DisplayFilter,表示显示到文本中、html中还是全部。list_exposed函数会将入参和所有VarEntry的display_filter进行与操作,只输出满足条件的bvar。
(4)如果(3)能查到记录,说明有同名bar已经进行了曝光,则根据gflags进行相关处理(直接abort或者打印error log)
函数hide
很容易理解,就是根据_name从对应的VarMapWithLock中删除记录。
函数list_exposed
遍历所有曝光的bvar,即遍历s_var_maps中的所有map的所有pair,选出满足display_filter条件的bvar的name保存到names数组中。
遍历s_var_maps中的每个map时,首先都需要先加锁,才能遍历map中的每个pair。为了防止个数太多、加锁时间过长,遍历每超过一定个数(256个),都主动释放一下锁。
函数describe_exposed
即根据name从s_var_maps查询到VarEntry*,调用该bvar的describe函数,结果保存到os中。
函数dump_exposed
查询所有曝光的bvar,将符合options中的通配符white_wildcards和black_wildcards条件的bvar都调用dumper->dump函数。例如white_wildcards = “foo_bar_*”; black_wildcards = “*var5”。函数返回值是dump的bvar的个数。
如果通配符white_wildcards是空的,那么先通过list_exposed得到所有曝光的bvar的name,然后对name进行排序,遍历name数组,对于符合通配符条件的name,调用describe_exposed函数将该bvar的描述保存到ostream,然后调用dumper->dump函数,传入name和保存描述的ostream转成的字符串。
Dumper是一个纯虚类,由开发者自己继承实现。
2.Reducer类
Reducer类定义:
3个模板类型:
T:bvar保存的值的类型
Op:“累加”的op
InvOp:与“累加”相反操作的OP,默认是VoidOp即无具体操作;这个介绍采样的时候再具体介绍。
成员变量:
构造函数:
add_cr_non_integral是如果T不是整型则增加const&修饰。
identity赋值T的默认构造函数生成的对象,如果T是POD类型,则T()就是0值。
_combiner:AgentCombiner<T, T, Op>类的对象;AgentCombiner的第1、第2模板参数都是T,说明它的结果类型和元素类型是相同的、都是T(介绍AgentCombiner时说过这2个类型可以不同)
_sampler:ReducerSampler<Reducer, T, Op, InvOp>类型的指针,此处设置为NULL,只有当前bvar有采样需要,即成员函数get_sampler被调用时_sampler才会被new出来。
_series_sampler:嵌套类SeriesSampler类型的指针,此处设置为NULL,后面介绍采样时再介绍。
_inv_op:InvOp类的对象。
Op类的对象在_combiner中有保存,所以不需要重复保存;
函数operator<<
函数operator<<是对bvar增加值时调用的,例如
bvar::Adder<int> sum;
sum << 1 << 2 << 3 << 4;
LOG(INFO) << sum.get_value(); // 10
具体实现是:
(1)调用_combiner的get_or_create_tls_agent函数,获得此bvar在当前线程tls数据中存储的agent的地址;
(2)然后调用agent->element.modify,传入Op对象和入参value,内部通过call_op_returning_void调用Op对象的函数operator()实现element当前值与入参value的“累加”、并将新值保存到agent->element。
(3)返回当前对象的引用,以便实现连续操作;
(4)这个函数调用栈完全没有锁,所以即使bvar在不同线程调用性能也很高。
函数get_value
这个函数目的是获得bvar的最新值,那么就要把在各个线程tls存储的agent的值进行“累加”。
实现上由于_combiner.combine_agents已经封装好了这个功能,所以直接调用并返回即可。
_combiner.combine_agents调用栈底部有加锁操作,所以应该尽量少调用。
函数describe
调用get_value获得最新值保存到os中,如果T为string类型并且quote_string为true,则前后加上引号。
函数expose_impl
这里override了基类虚函数Variable::expose_impl。主要是调用了Variable::expose_impl进行了实际的曝光操作,然后根据条件和参数初始化了_series_sampler(这个后面介绍采样时再介绍)。
3.Adder类
由于父类Reducer该实现的都实现了,所以Adder定义就比较简洁了。重点就是传3个模板参数,一个是值类型T,一个是“累加”Op:AddTo类,一个是“逆反”Op:MinusFrom类。
Op有一个注意点是:必须把新值保存到第一个参数lhs中,而不是返回。
(待续)