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 ```