trpc.group/trpc-go/trpc-go@v1.0.3/docs/developer_guide/develop_plugins/metrics.zh_CN.md (about)

     1  [English](metrics.md) | 中文
     2  
     3  # 怎么开发一个 metric 类型的插件
     4  
     5  本指南将介绍如何开发一个依赖配置进行加载的 metric 类型的插件。
     6  该插件将上报发起 RPC 时,client 端发送请求到 server 端收到回复的耗时, 以及 server 端收到请求到回复 client 的耗时。
     7  开发该插件需要实现以下三个子功能:
     8  
     9  - 实现插件依赖配置进行加载,详细说明请参考 [plugin](/plugin/README.zh_CN.md)
    10  - 实现让监控指标上报到外部平台,详细说明请参考 [metrics](/metrics/README.zh_CN.md)
    11  - 实现在拦截器中上报监控指标,详细说明请参考 [filter](/filter/README.zh_CN.md)
    12  
    13  下面以 [trpc-metrics-prometheus](https://github.com/trpc-ecosystem/go-metrics-prometheus) 为例,来介绍相关开发步骤。
    14  
    15  ## 实现插件依赖配置进行加载
    16  
    17  ### 1. 确定插件的配置
    18  
    19  ```yaml
    20  plugins:                                          # 插件配置
    21    metrics:                                        # 引用metrics
    22      prometheus:                                   # 启动prometheus
    23        ip: 0.0.0.0                                 # prometheus绑定地址
    24        port: 8090                                  # prometheus绑定端口
    25        path: /metrics                              # metrics路径
    26        namespace: Development                      # 命名空间
    27        subsystem: trpc                             # 子系统
    28        rawmode:   false                            # 原始模式,不会对metrics的特殊字符进行转换 
    29        enablepush: true                            # 启用push模式,默认不启用
    30        gateway: http://localhost:9091              # prometheus gateway地址
    31        password: username:MyPassword               # 设置账号密码, 以冒号分割
    32        job: job                                    # job名称
    33        pushinterval: 1                             # push间隔,默认1s上报一次
    34  ```
    35  
    36  ```go
    37  const (
    38      pluginType = "metrics"
    39      pluginName = "prometheus"
    40  )
    41  
    42  type Config struct {
    43      IP           string `yaml:"ip"`           // metrics monitoring address.
    44      Port         int32  `yaml:"port"`         // metrics listens to the port.
    45      Path         string `yaml:"path"`         // metrics path.
    46      Namespace    string `yaml:"namespace"`    // formal or test.
    47      Subsystem    string `yaml:"subsystem"`    // default trpc.
    48      RawMode      bool   `yaml:"rawmode"`      // by default, the special character in metrics will be converted.
    49      EnablePush   bool   `yaml:"enablepush"`   // push is not enabled by default.
    50      Password     string `yaml:"password"`     // account Password.
    51      Gateway      string `yaml:"gateway"`      // push gateway address.
    52      PushInterval uint32 `yaml:"pushinterval"` // push interval,default 1s.
    53      Job          string `yaml:"job"`          // reported task name.
    54  }
    55  ```
    56  
    57  ### 2. 实现 `plugin.Factory` 接口
    58  
    59  ```go
    60  type Plugin struct {
    61  }
    62  
    63  func (p *Plugin) Type() string {
    64      return pluginType
    65  }
    66  
    67  func (p *Plugin) Setup(name string, decoder plugin.Decoder) error {
    68      cfg := Config{}.Default()
    69      
    70      err := decoder.Decode(cfg)
    71      if err != nil {
    72          log.Errorf("trpc-metrics-prometheus:conf Decode error:%v", err)
    73          return err
    74      }
    75      go func() {
    76          err := initMetrics(cfg.IP, cfg.Port, cfg.Path)
    77          if err != nil {
    78              log.Errorf("trpc-metrics-prometheus:running:%v", err)
    79          }
    80      }()
    81      
    82      initSink(cfg)
    83      
    84      return nil
    85  }
    86  ```
    87  
    88  ### 3. 调用 `plugin.Register` 把插件自己注册到 `plugin` 包
    89  
    90  ```go
    91  func init() {
    92      plugin.Register(pluginName, &Plugin{})
    93  }
    94  ```
    95  
    96  ## 让监控指标上报到外部平台
    97  
    98  ### 1. 实现 `metrics.Sink` 接口
    99  
   100  ```go
   101  const (
   102      sinkName = "prometheus"
   103  )
   104  
   105  func (s *Sink) Name() string {
   106      return sinkName
   107  }
   108  
   109  func (s *Sink) Report(rec metrics.Record, opts ...metrics.Option) error {
   110      if len(rec.GetDimensions()) <= 0 {
   111          return s.ReportSingleLabel(rec, opts...)
   112  }
   113      labels := make([]string, 0)
   114      values := make([]string, 0)
   115      prefix := rec.GetName()
   116      
   117      if len(labels) != len(values) {
   118          return errLength
   119      }
   120  
   121      for _, dimension := range rec.GetDimensions() {
   122          labels = append(labels, dimension.Name)
   123          values = append(values, dimension.Value)
   124      }
   125      for _, m := range rec.GetMetrics() {
   126          name := s.GetMetricsName(m)
   127          if prefix != "" {
   128              name = prefix + "_" + name
   129          }
   130          if !checkMetricsValid(name) {
   131              log.Errorf("metrics %s(%s) is invalid", name, m.Name())
   132              continue
   133          }
   134          s.reportVec(name, m, labels, values)
   135      }
   136      return nil
   137  }
   138  ```
   139  
   140  ### 2. 将实现的 Sink 注册到 metrics 包。
   141  
   142  ```go
   143  func initSink(cfg *Config) {
   144      defaultPrometheusPusher = push.New(cfg.Gateway, cfg.Job) 
   145      // set basic auth if set. 
   146      if len(cfg.Password) > 0 { 
   147          defaultPrometheusPusher.BasicAuth(basicAuthForPasswordOption(cfg.Password))
   148      }
   149      defaultPrometheusSink = &Sink{
   150          ns:         cfg.Namespace,
   151          subsystem:  cfg.Subsystem,
   152          rawMode:    cfg.RawMode,
   153          enablePush: cfg.EnablePush,
   154          pusher:     defaultPrometheusPusher
   155      }
   156      metrics.RegisterMetricsSink(defaultPrometheusSink)
   157      // start up pusher if needed.
   158      if cfg.EnablePush {
   159      defaultPrometheusPusher.Gatherer(prometheus.DefaultGatherer)
   160          go pusherRun(cfg, defaultPrometheusPusher)
   161      }
   162  }
   163  ```
   164  
   165  ## 在拦截器中上报监控指标
   166  
   167  ### 1. 确定拦截器的配置
   168  
   169  ```yaml
   170    filter:
   171      - prometheus                                   # Add prometheus filter
   172  ```
   173  
   174  ### 2. 实现 `filter.ServerFilter` 和 `filter.ServerFilter` 
   175  
   176  ```go
   177  func ClientFilter(ctx context.Context, req, rsp interface{}, handler filter.ClientHandleFunc) error {
   178  	begin := time.Now()
   179  	hErr := handler(ctx, req, rsp)
   180  	msg := trpc.Message(ctx)
   181  	labels := getLabels(msg, hErr)
   182  	ms := make([]*metrics.Metrics, 0)
   183  	t := float64(time.Since(begin)) / float64(time.Millisecond)
   184  	ms = append(ms,
   185  		metrics.NewMetrics("time", t, metrics.PolicyHistogram),
   186  		metrics.NewMetrics("requests", 1.0, metrics.PolicySUM))
   187  	metrics.Histogram("ClientFilter_time", clientBounds)
   188  	r := metrics.NewMultiDimensionMetricsX("ClientFilter", labels, ms)
   189  	_ = GetDefaultPrometheusSink().Report(r)
   190  	return hErr
   191  }
   192  
   193  func ServerFilter(ctx context.Context, req interface{}, handler filter.ServerHandleFunc) (rsp interface{}, err error) {
   194  	begin := time.Now()
   195  	rsp, err = handler(ctx, req)
   196  	msg := trpc.Message(ctx)
   197  	labels := getLabels(msg, err)
   198  	ms := make([]*metrics.Metrics, 0)
   199  	t := float64(time.Since(begin)) / float64(time.Millisecond)
   200  	ms = append(ms,
   201  		metrics.NewMetrics("time", t, metrics.PolicyHistogram),
   202  		metrics.NewMetrics("requests", 1.0, metrics.PolicySUM))
   203  	metrics.Histogram("ServerFilter_time", serverBounds)
   204  	r := metrics.NewMultiDimensionMetricsX("ServerFilter", labels, ms)
   205  	_ = GetDefaultPrometheusSink().Report(r)
   206  	return rsp, err
   207  }
   208  ```
   209  
   210  ### 3. 将拦截器注册到 `filter` 包
   211  
   212  ```go
   213  func init() {
   214      filter.Register(pluginName, ServerFilter, ClientFilter)
   215  }
   216  ```