You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@brpc.apache.org by ww...@apache.org on 2022/08/16 02:08:46 UTC

[incubator-brpc] branch master updated: implement bvar::WindowEx and bvar::PerSecondEx (#1603)

This is an automated email from the ASF dual-hosted git repository.

wwbmmm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-brpc.git


The following commit(s) were added to refs/heads/master by this push:
     new a0b670f6 implement bvar::WindowEx and bvar::PerSecondEx (#1603)
a0b670f6 is described below

commit a0b670f64b02a6a25efcbb311bf3d225360409b2
Author: serverglen <se...@gmail.com>
AuthorDate: Tue Aug 16 10:08:41 2022 +0800

    implement bvar::WindowEx and bvar::PerSecondEx (#1603)
    
    * implement bvar::WindowEx and bvar::PerSecondEx
    
    * Update bvar_c++.md
    
    * Update bvar_c++.md
    
    * Update bvar_c++.md
    
    * Update bvar.md
---
 docs/cn/bvar_c++.md           | 154 ++++++++++++++++++++++++++++++++++++++----
 src/bvar/window.h             | 119 ++++++++++++++++++++++++++++++++
 test/bvar_window_unittest.cpp |  97 ++++++++++++++++++++++++++
 3 files changed, 358 insertions(+), 12 deletions(-)

diff --git a/docs/cn/bvar_c++.md b/docs/cn/bvar_c++.md
index eece49c7..3cc77036 100644
--- a/docs/cn/bvar_c++.md
+++ b/docs/cn/bvar_c++.md
@@ -20,13 +20,20 @@
 
 bvar分为多个具体的类,常用的有:
 
-- `bvar::Adder<T>` : 计数器,默认0,varname << N相当于varname += N。
-- `bvar::Maxer<T>` : 求最大值,默认std::numeric_limits<T>::min(),varname << N相当于varname = max(varname, N)。
-- `bvar::Miner<T>` : 求最小值,默认std::numeric_limits<T>::max(),varname << N相当于varname = min(varname, N)。
-- `bvar::IntRecorder` : 求自使用以来的平均值。注意这里的定语不是“一段时间内”。一般要通过Window衍生出时间窗口内的平均值。
-- `bvar::Window<VAR>` : 获得某个bvar在一段时间内的累加值。Window衍生于已存在的bvar,会自动更新。
-- `bvar::PerSecond<VAR>` : 获得某个bvar在一段时间内平均每秒的累加值。PerSecond也是会自动更新的衍生变量。
-- `bvar::LatencyRecorder` : 专用于记录延时和qps的变量。输入延时,平均延时/最大延时/qps/总次数 都有了。
+| 类型 | 说明 |
+|-----------------|-----------------------------------------------------------------------------------------|
+| bvar::Adder\<T\>| 计数器,默认0,varname << N相当于varname += N |
+| bvar::Maxer\<T\> | 求最大值,默认std::numeric_limits<T>::min(),varname << N相当于varname = max(varname, N) |
+| bvar::Miner\<T\>| 求最小值,默认std::numeric_limits<T>::max(),varname << N相当于varname = min(varname, N) |
+| bvar::IntRecorder| 求自使用以来的平均值。注意这里的定语不是“一段时间内”。一般要通过Window衍生出时间窗口内的平均值      |
+| bvar::Window\<VAR\>| 获得某个bvar在一段时间内的累加值。Window衍生于已存在的bvar,会自动更新 |
+| bvar::PerSecond\<VAR\>| 获得某个bvar在一段时间内平均每秒的累加值。PerSecond也是会自动更新的衍生变量 |
+| bvar::WindowEx\<T\> | 获得某个bvar在一段时间内的累加值。不依赖其他的bvar,需要给它发送数据 |
+| bvar::PerSecondEx\<T\>|  获得某个bvar在一段时间内平均每秒的累加值。不依赖其他的bvar,需要给它发送数据 |
+| bvar::LatencyRecorder| 专用于记录延时和qps的变量。输入延时,平均延时/最大延时/qps/总次数 都有了 |
+| bvar::Status\<T\> | 记录和显示一个值,拥有额外的set_value函数 |
+| bvar::PassiveStatus | 按需显示值。在一些场合中,我们无法set_value或不知道以何种频率set_value,更适合的方式也许是当需要显示时才打印。用户传入打印回调函数实现这个目的 |
+| bvar::GFlag | 将重要的gflags公开为bvar,以便监控它们 |
 
 例子:
 ```c++
@@ -276,6 +283,7 @@ if (google::SetCommandLineOption("bvar_dump_include", "*service*").empty()) {
 }
 LOG(INFO) << "Successfully set bvar_dump_include to *service*";
 ```
+
 请勿直接设置FLAGS_bvar_dump_file / FLAGS_bvar_dump_include / FLAGS_bvar_dump_exclude。
 一方面这些gflag类型都是std::string,直接覆盖是线程不安全的;另一方面不会触发validator(检查正确性的回调),所以也不会启动后台导出线程。
 
@@ -415,6 +423,22 @@ template <typename R>
 class Window : public Variable;
 ```
 
+## 如何使用
+```c++
+bvar::Adder<int> sum;
+bvar::Maxer<int> max_value;
+bvar::IntRecorder avg_value;
+  
+// sum_minute.get_value()是sum在之前60秒内的累加值。
+bvar::Window<bvar::Adder<int> > sum_minute(&sum, 60);
+  
+// max_value_minute.get_value()是max_value在之前60秒内的最大值。
+bvar::Window<bvar::Maxer<int> > max_value_minute(&max_value, 60);
+ 
+// avg_value_minute.get_value()是avg_value在之前60秒内的平均值。
+bvar::Window<IntRecorder> avg_value_minute(&avg_value, 60);
+```
+
 # bvar::PerSecond
 
 获得之前一段时间内平均每秒的统计值。它和Window基本相同,除了返回值会除以时间窗口之外。
@@ -443,11 +467,119 @@ bvar::Window<bvar::Maxer<int> > max_value_per_second(&max_value, 1);
 
 Window的优点是精确值,适合一些比较小的量,比如“上一分钟的错误数“,如果这用PerSecond的话,得到可能是”上一分钟平均每秒产生了0.0167个错误",这相比于”上一分钟有1个错误“显然不够清晰。另外一些和时间无关的量也要用Window,比如统计上一分钟cpu占用率的方法是用一个Adder同时累加cpu时间和真实时间,然后用Window获得上一分钟的cpu时间和真实时间,两者相除就得到了上一分钟的cpu占用率,这和时间无关,用PerSecond会产生错误的结果。
 
+# bvar::WindowEx
+
+获得之前一段时间内的统计值。WindowEx是独立存在的,不依赖其他的计数器,需要给它发送数据。出于性能考虑,WindowEx每秒对数据做一次统计,在最差情况下,WindowEx的返回值有1秒的延时。
+```c++
+// Get data within a time window.
+// The time unit is 1 second fixed.
+// Window not relies on other bvar.
+ 
+// R must:
+// - window_size must be a constant
+template <typename R, time_t window_size = 0>
+class WindowEx : public adapter::WindowExAdapter<R, adapter::WindowExType<R> > {
+public:
+    typedef adapter::WindowExAdapter<R, adapter::WindowExType<R> > Base;
+ 
+    WindowEx() : Base(window_size) {}
+ 
+    WindowEx(const base::StringPiece& name) : Base(window_size) {
+        this->expose(name);
+    }
+ 
+    WindowEx(const base::StringPiece& prefix,
+             const base::StringPiece& name)
+        : Base(window_size) {
+        this->expose_as(prefix, name);
+    }
+};
+```
+
+## 如何使用
+```c++
+const int window_size = 60;
+ 
+// sum_minute.get_value()是60秒内的累加值,省略最后一个window_size(时间窗口)的话默认为bvar_dump_interval。
+bvar::WindowEx<bvar::Adder<int>, window_size> sum_minute("sum_minute");
+sum_minute << 1 << 2 << 3;
+ 
+// max_minute.get_value()是60秒内的最大值,省略最后一个window_size(时间窗口)的话默认为bvar_dump_interval。
+bvar::WindowEx<bvar::Maxer<int>, window_size> max_minute("max_minute");
+max_minute << 1 << 2 << 3;
+ 
+// min_minute.get_value()是60秒内的最小值,省略最后一个window_size(时间窗口)的话默认为bvar_dump_interval。
+bvar::WindowEx<bvar::Miner<int>, window_size> min_minute("min_minute");
+min_minute << 1 << 2 << 3;
+ 
+// avg_minute.get_value是60秒内的平均值(返回值是bvar::Stat),省略最后一个window_size(时间窗口)的话默认为bvar_dump_interval。
+bvar::WindowEx<bvar::IntRecorder, window_size> avg_minute("avg_minute");
+avg_minute << 1 << 2 << 3;
+// 获得avg_minuter 60秒内的平均值stat
+bvar::Stat avg_stat = avg_minute.get_value();
+// 获得整型平均值
+int64_t avg_int = avg_stat.get_average_int();
+// 获得double类型平均值
+double avg_double = avg_stat.get_average_double();
+```
+
+## bvar::WindowEx和bvar::Window的区别
+
+- bvar::Window 不能独立存在,必须依赖于一个已有的计数器。Window会自动更新,不用给它发送数据;window_size是通过构造函数参数传递的。
+
+- bvar::WindowEx 是独立存在的,不依赖其他的计数器,需要给它发送数据。使用起来比较方便;window_size是通过模板参数传递的,省略最后一个window_size(时间窗口)的话默认为bvar_dump_interval。
+
+# bvar::PerSecondEx
+获得之前一段时间内平均每秒的统计值。它和WindowEx基本相同,除了返回值会除以时间窗口之外。
+```c++
+// Get data per second within a time window.
+// The only difference between PerSecondEx and WindowEx is that PerSecondEx divides
+// the data by time duration.
+ 
+// R must:
+// - window_size must be a constant
+template <typename R, time_t window_size = 0>
+class PerSecondEx : public adapter::WindowExAdapter<R, adapter::PerSecondExType<R> > {
+public:
+    typedef adapter::WindowExAdapter<R, adapter::PerSecondExType<R> > Base;
+ 
+    PerSecondEx() : Base(window_size) {}
+ 
+    PerSecondEx(const base::StringPiece& name) : Base(window_size) {
+        this->expose(name);
+    }
+ 
+    PerSecondEx(const base::StringPiece& prefix,
+                const base::StringPiece& name)
+        : Base(window_size) {
+        this->expose_as(prefix, name);
+    }
+};
+```
+
+## 如何使用
+
+```c++
+const int window_size = 60;
+ 
+// sum_per_second.get_value()是60秒内*平均每秒*的累加值,省略最后一个window_size(时间窗口)的话默认为bvar_dump_interval。
+bvar::PerSecondEx<bvar::Adder<int>, window_size> sum_per_second("sum_per_second");
+sum_per_second << 1 << 2 << 3;
+```
+
+## bvar::PerSecondEx和bvar::WindowEx的区别
+
+- 获得之前一段时间内平均每秒的统计值。它和WindowEx基本相同,除了返回值会除以时间窗口之外。
+
+## bvar::PerSecondEx和bvar::PerSecond的区别
+- bvar::PerSecond 不能独立存在,必须依赖于一个已有的计数器。PerSecond会自动更新,不用给它发送数据;window_size是通过构造函数参数传递的。
+- bvar::PerSecondEx 是独立存在的,不依赖其他的计数器,需要给它发送数据。使用起来比较方便;window_size是通过模板参数传递的,省略最后一个window_size(时间窗口)的话默认为bvar_dump_interval。
+
 # bvar::Status
 
 记录和显示一个值,拥有额外的set_value函数。
-```c++
 
+```c++
 // Display a rarely or periodically updated value.
 // Usage:
 //   bvar::Status<int> foo_count1(17);
@@ -485,7 +617,6 @@ class PassiveStatus : public Variable;
 ```
 虽然很简单,但PassiveStatus是最有用的bvar之一,因为很多统计量已经存在,我们不需要再次存储它们,而只要按需获取。比如下面的代码声明了一个在linux下显示进程用户名的bvar:
 ```c++
-
 static void get_username(std::ostream& os, void*) {
     char buf[32];
     if (getlogin_r(buf, sizeof(buf)) == 0) {
@@ -499,12 +630,11 @@ PassiveStatus<std::string> g_username("process_username", get_username, NULL);
 ```
 
 # bvar::GFlag
-
-Expose important gflags as bvar so that they're monitored (in noah).
+Expose important gflags as bvar so that they're monitored.
 ```c++
 DEFINE_int32(my_flag_that_matters, 8, "...");
 
-// Expose the gflag as *same-named* bvar so that it's monitored (in noah).
+// Expose the gflag as *same-named* bvar so that it's monitored.
 static bvar::GFlag s_gflag_my_flag_that_matters("my_flag_that_matters");
 //                                                ^
 //                                            the gflag name
diff --git a/src/bvar/window.h b/src/bvar/window.h
index a5780bf0..e0e02e54 100644
--- a/src/bvar/window.h
+++ b/src/bvar/window.h
@@ -236,6 +236,125 @@ public:
             return static_cast<value_type>(round(s.data * 1000000.0 / s.time_us));
         }
     }
+
+    value_type get_value() const { return Base::get_value(); }
+};
+
+namespace adapter {
+
+template <typename R>
+class WindowExType {
+public:
+    typedef R var_type;
+    typedef bvar::Window<var_type > window_type;
+    typedef typename R::value_type value_type;
+    struct WindowExVar {
+        WindowExVar(time_t window_size) : window(&var, window_size) {}
+        var_type var;
+        window_type window;
+    };
+};
+
+template <typename R>
+class PerSecondExType {
+public:
+    typedef R var_type;
+    typedef bvar::PerSecond<var_type > window_type;
+    typedef typename R::value_type value_type;
+    struct WindowExVar {
+        WindowExVar(time_t window_size) : window(&var, window_size) {}
+        var_type var;
+        window_type window;
+    };
+};
+
+template <typename R, typename WindowType>
+class WindowExAdapter : public Variable{
+public:
+    typedef typename R::value_type value_type;
+    typedef typename WindowType::WindowExVar WindowExVar;
+
+    WindowExAdapter(time_t window_size)
+        : _window_size(window_size > 0 ? window_size : FLAGS_bvar_dump_interval)
+        , _window_ex_var(_window_size) {
+    }
+
+    value_type get_value() const {
+        return _window_ex_var.window.get_value();
+    }
+
+    template <typename ANT_TYPE>
+    WindowExAdapter& operator<<(ANT_TYPE value) {
+        _window_ex_var.var << value;
+        return *this;
+    }
+
+    // Implement Variable::describe()
+    void describe(std::ostream& os, bool quote_string) const {
+        if (butil::is_same<value_type, std::string>::value && quote_string) {
+            os << '"' << get_value() << '"';
+        } else {
+            os << get_value();
+        }
+    }
+
+    virtual ~WindowExAdapter() {
+        hide();
+    }
+
+private:
+    time_t      _window_size;
+    WindowExVar _window_ex_var;
+};
+
+}  // namespace adapter
+
+// Get data within a time window.
+// The time unit is 1 second fixed.
+// Window not relies on other bvar.
+
+// R must:
+// - window_size must be a constant
+template <typename R, time_t window_size = 0>
+class WindowEx : public adapter::WindowExAdapter<R, adapter::WindowExType<R> > {
+public:
+    typedef adapter::WindowExAdapter<R, adapter::WindowExType<R> > Base;
+
+    WindowEx() : Base(window_size) {}
+
+    WindowEx(const butil::StringPiece& name) : Base(window_size) {
+        this->expose(name);
+    }
+
+    WindowEx(const butil::StringPiece& prefix,
+             const butil::StringPiece& name)
+        : Base(window_size) {
+        this->expose_as(prefix, name);
+    }
+};
+
+// Get data per second within a time window.
+// The only difference between PerSecondEx and WindowEx is that PerSecondEx divides
+// the data by time duration.
+
+// R must:
+// - window_size must be a constant
+template <typename R, time_t window_size = 0>
+class PerSecondEx : public adapter::WindowExAdapter<R, adapter::PerSecondExType<R> > {
+public:
+    typedef adapter::WindowExAdapter<R, adapter::PerSecondExType<R> > Base;
+
+    PerSecondEx() : Base(window_size) {}
+
+    PerSecondEx(const butil::StringPiece& name) : Base(window_size) {
+        this->expose(name);
+    }
+
+    PerSecondEx(const butil::StringPiece& prefix,
+                const butil::StringPiece& name)
+        : Base(window_size) {
+        this->expose_as(prefix, name);
+    }
 };
 
 }  // namespace bvar
diff --git a/test/bvar_window_unittest.cpp b/test/bvar_window_unittest.cpp
new file mode 100644
index 00000000..e50cf109
--- /dev/null
+++ b/test/bvar_window_unittest.cpp
@@ -0,0 +1,97 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include <pthread.h>
+#include <cstddef>
+#include <memory>
+#include <list>
+#include <iostream>
+#include <sstream>
+#include <butil/time.h>
+#include <butil/macros.h>
+#include <gflags/gflags.h>
+#include <gtest/gtest.h>
+#include "bvar/bvar.h"
+#include "bvar/window.h"
+
+class WindowTest : public testing::Test {
+protected:
+    void SetUp() {}
+    void TearDown() {}
+};
+
+TEST_F(WindowTest, window) {
+    const int window_size = 2;
+    // test bvar::Adder
+    bvar::Adder<int> adder;
+    bvar::Window<bvar::Adder<int> > window_adder(&adder, window_size);
+    bvar::PerSecond<bvar::Adder<int> > per_second_adder(&adder, window_size);
+    bvar::WindowEx<bvar::Adder<int>, 2> window_ex_adder("window_ex_adder");
+    bvar::PerSecondEx<bvar::Adder<int>, window_size> per_second_ex_adder("per_second_ex_adder");
+
+    // test bvar::Maxer
+    bvar::Maxer<int> maxer;
+    bvar::Window<bvar::Maxer<int> > window_maxer(&maxer, window_size);
+    bvar::WindowEx<bvar::Maxer<int>, window_size> window_ex_maxer;
+
+    // test bvar::Miner
+    bvar::Miner<int> miner;
+    bvar::Window<bvar::Miner<int> > window_miner(&miner, window_size);
+    bvar::WindowEx<bvar::Miner<int>, window_size> window_ex_miner;
+
+    // test bvar::IntRecorder
+    bvar::IntRecorder recorder;
+    bvar::Window<bvar::IntRecorder> window_int_recorder(&recorder, window_size);
+    bvar::WindowEx<bvar::IntRecorder, window_size> window_ex_int_recorder("window_ex_int_recorder");
+
+    adder << 10;
+    window_ex_adder << 10;
+    per_second_ex_adder << 10;
+
+    maxer << 10;
+    window_ex_maxer << 10;
+    miner << 10;
+    window_ex_miner << 10;
+
+    recorder << 10;
+    window_ex_int_recorder << 10;
+
+    sleep(1);
+    adder << 2;
+    window_ex_adder << 2;
+    per_second_ex_adder << 2;
+
+    maxer << 2;
+    window_ex_maxer << 2;
+    miner << 2;
+    window_ex_miner << 2;
+
+    recorder << 2;
+    window_ex_int_recorder << 2;
+    sleep(1);
+
+    ASSERT_EQ(window_adder.get_value(), window_ex_adder.get_value());
+    ASSERT_EQ(per_second_adder.get_value(), per_second_ex_adder.get_value());
+
+    ASSERT_EQ(window_maxer.get_value(), window_ex_maxer.get_value());
+    ASSERT_EQ(window_miner.get_value(), window_ex_miner.get_value());
+
+    bvar::Stat recorder_stat = window_int_recorder.get_value();
+    bvar::Stat window_ex_recorder_stat = window_ex_int_recorder.get_value();
+    ASSERT_EQ(recorder_stat.get_average_int(), window_ex_recorder_stat.get_average_int());
+    ASSERT_DOUBLE_EQ(recorder_stat.get_average_double(), window_ex_recorder_stat.get_average_double());
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@brpc.apache.org
For additional commands, e-mail: dev-help@brpc.apache.org