项目监控之Prometheus初体验(二)——go-micro框架整合prometheus监控

1、前言

本篇内容主要介绍了关于go-micro框架进行prometheus监控的整合,api层采用的是gin框架,服务注册采用 etcdv3(服务启动时确保etcd已启动),有关prometheus的安装可以参考项目监控之Prometheus初体验(一)。
备注:etcd可以到 https://github.com/etcd-io/etcd/releases下载,将压缩包下载解压运行 etcd.exe 即可启动服务。

2、为服务层添加prometheus监控

1)、添加prometheus插件
项目监控之Prometheus初体验(二)——go-micro框架整合prometheus监控_第1张图片
2)、添加prometheus采集指标的路径 “/metrics”

go StartMonitor("127.0.0.1", 8888)

func StartMonitor(ip string, port int) error {
	http.Handle("/metrics", promhttp.Handler())
	err := http.ListenAndServe(fmt.Sprintf("%s:%d", ip, port), nil)
	return err
}

3、为API层添加prometheus监控

1)、添加 prometheus中间件
2)、添加prometheus采集指标的路径 “/metrics”
项目监控之Prometheus初体验(二)——go-micro框架整合prometheus监控_第2张图片

prometheus中间件代码

package utils

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus"
	"net/http"
	"time"
)

type PrometheusMonitor struct {
	ServiceName string //监控服务的名称

	APIRequestsCounter *prometheus.CounterVec
	RequestDuration    *prometheus.HistogramVec
	RequestSize        *prometheus.HistogramVec
	ResponseSize       *prometheus.HistogramVec
}

func NewPrometheusMonitor(namespace, serviceName string) *PrometheusMonitor {
	APIRequestsCounter := prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Namespace: namespace,
			Name:      "http_requests_total",
			Help:      "A counter for requests to the wrapped handler.",
		},
		[]string{"handler", "method", "code", "micro_name"},
	)

	RequestDuration := prometheus.NewHistogramVec(
		prometheus.HistogramOpts{
			Namespace: namespace,
			Name:      "http_request_duration_seconds",
			Help:      "A histogram of latencies for requests.",
		},
		[]string{"handler", "method", "code", "micro_name"},
	)

	RequestSize := prometheus.NewHistogramVec(
		prometheus.HistogramOpts{
			Namespace: namespace,
			Name:      "http_request_size_bytes",
			Help:      "A histogram of request sizes for requests.",
		},
		[]string{"handler", "method", "code", "micro_name"},
	)

	ResponseSize := prometheus.NewHistogramVec(
		prometheus.HistogramOpts{
			Namespace: namespace,
			Name:      "http_response_size_bytes",
			Help:      "A histogram of response sizes for requests.",
		},
		[]string{"handler", "method", "code", "micro_name"},
	)

	//注册指标
	prometheus.MustRegister(APIRequestsCounter, RequestDuration, RequestSize, ResponseSize)

	return &PrometheusMonitor{
		ServiceName:        serviceName,
		APIRequestsCounter: APIRequestsCounter,
		RequestDuration:    RequestDuration,
		RequestSize:        RequestSize,
		ResponseSize:       ResponseSize,
	}
}

func (m *PrometheusMonitor) PromMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		//使用rest风格api路径时可结合group_wrapper使用
		//relativePath := c.GetString(constant.RelativePathKey)
		//if relativePath == "" {
		//	relativePath = c.Request.URL.Path
		//}

		relativePath := c.Request.URL.Path
		start := time.Now()
		reqSize := computeApproximateRequestSize(c.Request)
		c.Next()
		duration := time.Since(start)
		code := fmt.Sprintf("%d", c.Writer.Status())
		m.APIRequestsCounter.With(prometheus.Labels{"handler": relativePath, "method": c.Request.Method, "code": code, "micro_name": m.ServiceName}).Inc()
		m.RequestDuration.With(prometheus.Labels{"handler": relativePath, "method": c.Request.Method, "code": code, "micro_name": m.ServiceName}).Observe(duration.Seconds())
		m.RequestSize.With(prometheus.Labels{"handler": relativePath, "method": c.Request.Method, "code": code, "micro_name": m.ServiceName}).Observe(float64(reqSize))
		m.ResponseSize.With(prometheus.Labels{"handler": relativePath, "method": c.Request.Method, "code": code, "micro_name": m.ServiceName}).Observe(float64(c.Writer.Size()))
	}
}

// From https://github.com/DanielHeckrath/gin-prometheus/blob/master/gin_prometheus.go
func computeApproximateRequestSize(r *http.Request) int {
	s := 0
	if r.URL != nil {
		s = len(r.URL.Path)
	}

	s += len(r.Method)
	s += len(r.Proto)
	for name, values := range r.Header {
		s += len(name)
		for _, value := range values {
			s += len(value)
		}
	}
	s += len(r.Host)

	// N.B. r.Form and r.MultipartForm are assumed to be included in r.URL.

	if r.ContentLength != -1 {
		s += int(r.ContentLength)
	}
	return s
}

4、测试是否生效

4.1、分别为两个服务写了一个简单的测试代码

4.1.1、 api层

//访问路径为 /demo-api/sayHello
func (d *Demo) SayHello(c *gin.Context)  {
	req := &demoService.GetUsernameReq{}
	res, err := d.demoService.GetUsername(context.TODO(), req)
	if err != nil {
		fmt.Print(err)
		c.JSON(http.StatusOK, "inner err")
		return
	}
	c.JSON(http.StatusOK, "hello,"+res.Username)
}

4.1.2、service层
1)、编写proto文件

syntax = "proto3";
package service;

message GetUsernameReq{

}

message GetUsernameRes{
    string username = 1;
}

service DemoService {
    rpc GetUsername(GetUsernameReq) returns(GetUsernameRes);
}

2)、生成proto代码

protoc --proto_path=. --go_out=. --micro_out=. *.proto

3)、实现protoc生成的接口DemoServiceHandler
项目监控之Prometheus初体验(二)——go-micro框架整合prometheus监控_第3张图片

//简单返回一个名字
func (d *Demo) GetUsername(ctx context.Context, in *service.GetUsernameReq, out *service.GetUsernameRes) error {
	out.Username = "jack"
	return nil
}

4.2、编写并启动api网关
1)、编写 main.go

package main

import (
	"github.com/micro/micro/cmd"
)

func main() {
	cmd.Init()
}

2)、编写 plugin.go,这里及以下服务演示注册采用的是注册到 etcdv3,使用默认的mdns可以不需要该文件(window上注册时网络连接有时会出现地址类似10.0.0.?的这种内部网络,导致转发时出现500异常)

package main

import _ "github.com/micro/go-plugins/registry/etcdv3"
import _ "github.com/micro/go-plugins/transport/grpc"

3)、启动网关接口

go run main.go plugin.go --registry_address localhost:2379  --registry etcdv3  api --handler=http --namespace go-micro.api

注:这里的namespace参数由注册时的服务名决定
项目监控之Prometheus初体验(二)——go-micro框架整合prometheus监控_第4张图片
在这里插入图片描述
4.3、分别启动api服务和service服务
下面为代码的结构
项目监控之Prometheus初体验(二)——go-micro框架整合prometheus监控_第5张图片
分别进入到两个服务的项目的目录下运行服务

go run main.go plugin.go --registry_address localhost:2379  --registry etcdv3

4.4、测试服务是否启动成功
1)、访问一下我们所写 demo-api 的路径,成功访问
项目监控之Prometheus初体验(二)——go-micro框架整合prometheus监控_第6张图片
2)、接下来来看看我们注册的指标是否存在,分别输入我们监控的两个服务的指标采集路径
项目监控之Prometheus初体验(二)——go-micro框架整合prometheus监控_第7张图片
项目监控之Prometheus初体验(二)——go-micro框架整合prometheus监控_第8张图片
3)、可以看到指标成功获取到了,到此我们的项目已经成功整合了prometheus
补充:micro service层使用prometheus插件生成的是以 micro_ 开头的标签,自定义的(如上图demo_api_*)标签则是以我们传入的namespace开头的


有什么不对的地方,欢迎指正!
获取项目源码

你可能感兴趣的:(Prometheus,go-micro)