第六课 Go容器化微服务系统实战-性能监控能力完善

第六课 Go容器化微服务系统实战-性能监控能力完善

tags:

  • GO
  • 慕课网

categories:

  • 监控系统
  • Docker-compose
  • promethues

第一节 监控系统介绍

1.1 promethues 基本介绍

  1. 它是一套开源的监控、报警、时间序列数据库的组合。
  2. 基本原理是通过HTTP协议周期性抓取被监控组件的状态
  3. 适合Docker、Kubernetes环境的监控系统
  4. promethues架构图
    在这里插入图片描述

1.2 promethues重要组件

  1. Prometheus Server:用于收集和存储时间序列数据。
  2. Client Library:客户端库成相应的metrics并暴露给Prometheus server
  3. Push Gateway:主要用于短期的jobs
  4. Exporters:用于暴露已有的第三方服务的metrics 给Prometheus。比如:mysql、redis、mongo等
  5. Alertmanager:从 Prometheus server端接收到 alerts后,会进行去除重复,分组,并路由到对收的接受方式,发出报警。

1.3 promethues工作流程

  1. Prometheus server定期从配置好的jobs、exporters、Pushgateway中拉数据
  2. Prometheus server记录数据并且根据报警规则推送 alert 数据
  3. Alertmanager根据配置文件,对接收到的警报进行处理,发出告警。
  4. 在图形界面中,可视化采集数据

1.4 promethues相关概念

  1. promethue中的数据模型
    • Prometheus中存储的数据为时间序列
    • 格式上由metric的名字和一系列的标签(键值对)唯一标识组成
    • 不同的标签则代表不同的时间序列
  2. metric(指标)类型
    • Counter类型:一种累加的指标,如︰请求的个数,出现的错误数等
    • Gauge类型:可以任意加减,如︰温度,运行的goroutines的个数
    • Histogram类型:可以对观察结果采样,分组及统计,如∶柱状图
    • Summary类型︰提供观测值的count和sum 功能,如:请求持续时间
  3. instance和jobs
    • instance : 一个单独监控的目标,—般对应于一个进程。
    • jobs:一组同种类型的instances (主要用于保证可扩展性和可靠性)
  4. grafana看板
    • 拥有丰富dashboard和图表编辑的指标分析平台
    • 拥有自己的权限管理和用户管理系统
    • Grafana更适合用于数据可视化展示

第二节 订单领域开发环境

2.1 Docker-Compose安装使用

  1. 首先,使用Dockerfile定义应用程序的环境
  2. 其次,使用docker-compose.yml定义构成应用程序的服务
  3. 最后,执行docker-compose up命令来启动并运行整个应用程序
  4. 安装docker-compose
curl -L https://github.com/docker/compose/releases/download/1.26.2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
chmod 777 /usr/local/bin/docker-compose
docker-compose --version
  1. docker-compose常用命令
# 后台运行
docker-compose up -d 
# 列出项目中所有容器
docker-compose ps 
# 停止和删除容器和网络
docker-compose down
  1. docker-compose.yml 例子。
#声明版本
version: "3"
#定义服务
services:
  #服务名称,能够在同一个网络内按照名称访问
  consul:
    #说明采用的镜像地址
    image: consul:1.6.0
    #镜像对外映射的端口
    ports:
      - "8500:8500"
  #服务名称,能够在同一个网络内按照名称访问
  jaeger:
    #说明采用的镜像地址
    image: jaegertracing/all-in-one:latest
    #镜像对外映射的端口
    ports:
      - "6831:6831/udp"
      - "16686:16686"
  #熔断看板
  hystrix-dashboard:
    #说明采用的镜像地址
    image: mlabouardy/hystrix-dashboard
    #镜像对外映射的端口
    ports:
      - "9002:9002"
  #熔断看板
  prometheus:
    #说明采用的镜像地址
    image: prom/prometheus
    #把外部yml文件挂载到容器中
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"
  #熔断看板
  grafana:
    #说明采用的镜像地址
    image: grafana/grafana
    #镜像对外映射的端口
    ports:
      - "3000:3000"

2.2 订单领域开发目录生成

  1. 用上节课方法自动生成我们的目录文件
sudo docker pull micro/micro
sudo docker run --rm -v $(pwd):$(pwd) -w $(pwd) micro/micro new order
  1. 新建domain文件夹,并在domain中创建model、repository、service三个文件夹。
  2. 删除go.mod,重新通过下面命令生成。
go mod init git.imooc.com/qnhyn/cart
go mod tidy
  1. 新建common文件夹。

2.3 订单领域proto开发

  1. proto下order文件夹下order.proto编写。
  2. 生成go文件。
# 生成之后看下product.pb.micro.go 用到的版本 主要要是v2(v3会在编译时报错)
protoc *.proto --gofast_out=. --micro_out=. 
  1. 不写条件可以为空如:AllOrderRequest
syntax = "proto3";

package go.micro.service.order;

service Order {
	rpc GetOrderByID(OrderID) returns (OrderInfo){}
	rpc GetAllOrder(AllOrderRequest) returns (AllOrder){}
	rpc CreateOrder(OrderInfo) returns (OrderID){}
	rpc DeleteOrderByID(OrderID) returns (Response){}
	rpc UpdateOrderPayStatus(PayStatus) returns (Response){}
	rpc UpdateOrderShipStatus(ShipStatus) returns (Response){}
	rpc UpdateOrder(OrderInfo) returns (Response){}
}

message AllOrderRequest {

}

message AllOrder{
	repeated OrderInfo order_info = 1;
}


message OrderID {
	int64 order_id = 1;
}

message OrderInfo{
	int64 id = 1;
	int32 pay_status =2;
	int32 ship_status =3;
	double price =4;
	repeated OrderDetail order_detail =5;
}

message OrderDetail {
	int64 id = 1;
	int64 product_id =2;
	int64 product_num =3;
	int64 product_size_id =4;
	int64 product_price =5;
	int64 order_id =6;
}

message Response{
	string msg = 1;
}

message PayStatus{
	int64 order_id =1;
	int32 pay_status =2;
}

message ShipStatus{
	int64 order_id =1;
	int32 ship_status =2;
}

第三节 订单领域代码开发

3.1 订单领域domain开发-model

  1. domain下model下order.go.
package model

import "time"

type Order struct{
	ID int64 `gorm:"primary_key;not_null;auto_increment",json:"id"`
	OrderCode string `gorm:"unique_index;not_null",json:"order_code"`
	PayStatus int32 `json:"pay_status"`
	ShipStatus int32 `json:"ship_status"`
	Price float64 `json:"price"`
	OrderDetail []OrderDetail `gorm:"ForeignKey:OrderID" json:"order_detail"`
	CreateAt time.Time
	UpdateAt time.Time
}
  1. domain下model下order_detail.go
package model

type OrderDetail struct {
	ID int64 `grom:"primary_key;not_null;auto_increment",json:"id"`
	ProductID int64 `json:"product_id"`
	ProductNum int64 `json:"product_num"`
	ProductSizeID int64 `json:"product_size_id"`
	ProductPrice float64 `json:"product_price"`
	OrderID int64 `json:"order_id"`
}

3.2 订单领域domain开发-repository

  1. domain下repository下order_repository.go
package repository
import (
	"errors"
	"github.com/jinzhu/gorm"
	"git.imooc.com/qnhyn/order/domain/model"
)
type IOrderRepository interface{
	InitTable() error
	FindOrderByID(int64) (*model.Order, error)
	CreateOrder(*model.Order) (int64, error)
	DeleteOrderByID(int64) error
	UpdateOrder(*model.Order) error
	FindAll()([]model.Order,error)
	UpdateShipStatus(int64,int32) error
	UpdatePayStatus(int64,int32) error

}
//创建orderRepository
func NewOrderRepository(db *gorm.DB) IOrderRepository  {
	return &OrderRepository{mysqlDb:db}
}

type OrderRepository struct {
	mysqlDb *gorm.DB
}

//初始化表
func (u *OrderRepository)InitTable() error  {
	return u.mysqlDb.CreateTable(&model.Order{},&model.OrderDetail{}).Error
}

//根据ID查找Order信息
func (u *OrderRepository)FindOrderByID(orderID int64) (order *model.Order,err error) {
	order = &model.Order{}
	return order, u.mysqlDb.Preload("OrderDetail").First(order,orderID).Error
}

//创建Order信息
func (u *OrderRepository) CreateOrder(order *model.Order) (int64, error) {
	return order.ID, u.mysqlDb.Create(order).Error
}

//根据ID删除Order信息
func (u *OrderRepository) DeleteOrderByID(orderID int64) error {
	tx := u.mysqlDb.Begin()
	//遇到错误回滚
	defer func() {
		if r:=recover();r!=nil {
			tx.Rollback()
		}
	}()

	if tx.Error !=nil {
		return tx.Error
	}

	//彻底删除 Order 信息
	if err:= tx.Unscoped().Where("id = ?",orderID).Delete(&model.Order{}).Error;err!=nil{
		tx.Rollback()
		return err
	}

	//彻底删除 OrderDetail 信息
	if err:=tx.Unscoped().Where("order_id = ?",orderID).Delete(&model.OrderDetail{}).Error;err!=nil{
		tx.Rollback()
		return err

	}
	return tx.Commit().Error
}

//更新Order信息
func (u *OrderRepository) UpdateOrder(order *model.Order) error {
	return u.mysqlDb.Model(order).Update(order).Error
}

//获取结果集
func (u *OrderRepository) FindAll()(orderAll []model.Order,err error) {
	return orderAll, u.mysqlDb.Preload("OrderDetail").Find(&orderAll).Error
}

//更新订单的发货状态
func (u *OrderRepository) UpdateShipStatus(orderID int64,shipStatus int32) error {
	db:=u.mysqlDb.Model(&model.Order{}).Where("id = ?",orderID).UpdateColumn("ship_status",shipStatus)
	if db.Error !=nil{
		return db.Error
	}
	if db.RowsAffected == 0 {
		return errors.New("更新失败")
	}
	return nil
}

//更新订单的支付状态
func (u *OrderRepository) UpdatePayStatus(orderID int64,payStatus int32) error {
	db:=u.mysqlDb.Model(&model.Order{}).Where("id = ?",orderID).UpdateColumn("pay_status",payStatus)
	if db.Error !=nil{
		return db.Error
	}
	if db.RowsAffected == 0 {
		return errors.New("更新失败")
	}
	return nil
}

3.3 订单领域domain开发-service

  1. domain下service下order_data_service.go
package service

import (
	"git.imooc.com/qnhyn/order/domain/model"
	"git.imooc.com/qnhyn/order/domain/repository"
)

type IOrderDataService interface {
	AddOrder(*model.Order) (int64, error)
	DeleteOrder(int64) error
	UpdateOrder(*model.Order) error
	FindOrderByID(int64) (*model.Order, error)
	FindAllOrder() ([]model.Order, error)
	UpdateShipStatus(int64, int32) error
	UpdatePayStatus(int64, int32) error
}

//创建
func NewOrderDataService(orderRepository repository.IOrderRepository) IOrderDataService {
	return &OrderDataService{orderRepository}
}

type OrderDataService struct {
	OrderRepository repository.IOrderRepository
}

//插入
func (u *OrderDataService) AddOrder(order *model.Order) (int64, error) {
	return u.OrderRepository.CreateOrder(order)
}

//删除
func (u *OrderDataService) DeleteOrder(orderID int64) error {
	return u.OrderRepository.DeleteOrderByID(orderID)
}

//更新
func (u *OrderDataService) UpdateOrder(order *model.Order) error {
	return u.OrderRepository.UpdateOrder(order)
}

//查找
func (u *OrderDataService) FindOrderByID(orderID int64) (*model.Order, error) {
	return u.OrderRepository.FindOrderByID(orderID)
}

//查找
func (u *OrderDataService) FindAllOrder() ([]model.Order, error) {
	return u.OrderRepository.FindAll()
}

func (u *OrderDataService) UpdateShipStatus(orderID int64, shipStatus int32) error {
	return u.OrderRepository.UpdateShipStatus(orderID,shipStatus)
}

func (u *OrderDataService) UpdatePayStatus(orderID int64, payStatus int32) error {
	return u.OrderRepository.UpdatePayStatus(orderID,payStatus)
}

3.4 订单领域Handle开发

  1. 开发暴露服务的Handle下order.go
package handler

import (
	"context"
	"git.imooc.com/qnhyn/common"
	"git.imooc.com/qnhyn/order/domain/model"
	"git.imooc.com/qnhyn/order/domain/service"
	. "git.imooc.com/qnhyn/order/proto/order"
)
type Order struct{
	OrderDataService service.IOrderDataService
}
//根据订单ID查询订单
func (o *Order) GetOrderByID(ctx context.Context,request *OrderID,response *OrderInfo) error {
	order,err:=o.OrderDataService.FindOrderByID(request.OrderId)
	if err !=nil {
		return err
	}
	if err:= common.SwapTo(order,response);err !=nil {
		return err
	}
	return nil
}
//查找所有订单
func (o *Order) GetAllOrder(ctx context.Context, request *AllOrderRequest, response *AllOrder) error {
	orderAll,err := o.OrderDataService.FindAllOrder()
	if err !=nil {
		return err
	}

	for _,v:=range orderAll{
		order := &OrderInfo{}
		if err:=common.SwapTo(v,order);err!=nil {
			return err
		}
		response.OrderInfo = append(response.OrderInfo, order)
	}
	return nil
}
//创建订单
func (o *Order) CreateOrder(ctx context.Context,request *OrderInfo,response *OrderID) error {
	orderAdd := &model.Order{}
	if err:=common.SwapTo(request,orderAdd);err!=nil{
		return err
	}
	orderID,err:=o.OrderDataService.AddOrder(orderAdd)
	if err !=nil {
		return err
	}
	response.OrderId = orderID
	return nil
}

//删除订单
func (o *Order) DeleteOrderByID(ctx context.Context, request *OrderID, response *Response) error {
	if err:=o.OrderDataService.DeleteOrder(request.OrderId);err!=nil {
		return err
	}
	response.Msg = "删除成功"
	return  nil
}

//更新订单支付状态
func (o *Order) UpdateOrderPayStatus(ctx context.Context, request *PayStatus,response *Response) error {
	if err:=o.OrderDataService.UpdatePayStatus(request.OrderId, request.PayStatus);err!=nil {
		return err
	}
	response.Msg = "支付状态更新成功"
	return nil
}

//更新发货状态
func (o *Order) UpdateOrderShipStatus(ctx context.Context, request *ShipStatus, response *Response) error {
	if err:=o.OrderDataService.UpdateShipStatus(request.OrderId,request.ShipStatus);err!=nil{
		return err
	}
	response.Msg = "发货状态更新成功"
	return nil
}
//更新订单状态
func (o *Order) UpdateOrder(ctx context.Context, request *OrderInfo, response *Response) error {
	order := &model.Order{}
	if err:=common.SwapTo(request,order);err!=nil{
		return err
	}
	if err:=o.OrderDataService.UpdateOrder(order);err!=nil {
		return err
	}
	response.Msg = "订单更新成功"
	return nil
}

第四节 main.go添加监控

4.1 prometheus工具函数

  1. prometheus.go
package common

import (
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/prometheus/common/log"
	"net/http"
	"strconv"
)

func PrometheusBoot(port int)  {
	http.Handle("/metrics",promhttp.Handler())
	//启动web 服务
	go func() {
		err := http.ListenAndServe("0.0.0.0:"+strconv.Itoa(port),nil)
		if err !=nil {
			log.Fatal("启动失败")
		}
		log.Info("监控启动,端口为:"+strconv.Itoa(port))
	}()
}
  1. 更新工具包
go clean -i git.imooc.com/qnhyn/common
go get git.imooc.com/qnhyn/common

4.2 main函数编写

  1. main.go
package main

import (
	"git.imooc.com/qnhyn/common"
	"git.imooc.com/qnhyn/order/domain/repository"
	service2 "git.imooc.com/qnhyn/order/domain/service"
	"git.imooc.com/qnhyn/order/handler"
	order "git.imooc.com/qnhyn/order/proto/order"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
	"github.com/micro/go-micro/v2"
	log "github.com/micro/go-micro/v2/logger"
	"github.com/micro/go-micro/v2/registry"
	consul2 "github.com/micro/go-plugins/registry/consul/v2"
	"github.com/micro/go-plugins/wrapper/monitoring/prometheus/v2"
	ratelimit "github.com/micro/go-plugins/wrapper/ratelimiter/uber/v2"
	opentracing2 "github.com/micro/go-plugins/wrapper/trace/opentracing/v2"
	"github.com/opentracing/opentracing-go"
)

var (
	//qps = os.Getenv("QPS")
	QPS = 1000
)

func main() {
	// 1.配置中心
	consulConfig,err:=common.GetConsulConfig("localhost",8500,"/micro/config")
	if err !=nil {
		log.Error(err)
	}
	// 2.注册中心
	consul :=consul2.NewRegistry(func(options *registry.Options) {
		options.Addrs = []string{
			"localhost:8500",
		}
	})
	// 3.jaeger 链路追踪
	t,io,err := common.NewTracer("go.micro.service.order","localhost:6831")
	if err !=nil {
		log.Error(err)
	}
	defer io.Close()
	opentracing.SetGlobalTracer(t)
	// 4.初始化数据库
	mysqlInfo := common.GetMysqlFromConsul(consulConfig,"mysql")
	db,err:= gorm.Open("mysql",mysqlInfo.User+":"+mysqlInfo.Pwd+"@/"+mysqlInfo.Database+"?charset=utf8&parseTime=True&loc=Local")
	if err !=nil {
		log.Error(err)
	}
	defer db.Close()
	//禁止副表
	db.SingularTable(true)

	//第一次运行的时候创建表
	//tableInit := repository.NewOrderRepository(db)
	//tableInit.InitTable()

	//创建实例
	orderDataService := service2.NewOrderDataService(repository.NewOrderRepository(db))

	// 5.暴露监控地址
	common.PrometheusBoot(9092)

	// New Service
	service := micro.NewService(
		micro.Name("go.micro.service.order"),
		micro.Version("latest"),
		//暴露的服务地址
		micro.Address(":9085"),
		//添加consul 注册中心
		micro.Registry(consul),
		//添加链路追踪
		micro.WrapHandler(opentracing2.NewHandlerWrapper(opentracing.GlobalTracer())),
		//添加限流
		micro.WrapHandler(ratelimit.NewHandlerWrapper(QPS)),
		//添加监控
		micro.WrapHandler(prometheus.NewHandlerWrapper()),
	)

	// Initialise service
	service.Init()

	// Register Handler
	order.RegisterOrderHandler(service.Server(), &handler.Order{OrderDataService:orderDataService})

	// Run service
	if err := service.Run(); err != nil {
		log.Fatal(err)
	}
}

4.3 配置数据库信息

  1. http://192.168.242.142:8500/ 配置 /micro/config/mysql
{
  "host":"127.0.0.1",
  "user":"root",
  "pwd":"123456",
  "database":"order",
  "port":3306
}
  1. 运行程序,如果报错/clientv3时出错
go mod edit -require=google.golang.org/grpc@v1.26.0
go get -u -x google.golang.org/grpc@v1.26.0

4.4 配置promethues和grafana

  1. docker-compose.yml 因为其他组件都已经启动了所以这里只启动需要的。
    • prometheus.yml 挂载配置文件到/etc/prometheus/prometheus.yml
#声明版本
version: "3"
#定义服务
services:
  #熔断看板
  prometheus:
    #说明采用的镜像地址
    image: prom/prometheus
    #把外部yml文件挂载到容器中
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"
  #熔断看板
  grafana:
    #说明采用的镜像地址
    image: grafana/grafana
    #镜像对外映射的端口
    ports:
      - "3000:3000"
  1. prometheus.yml
    • 设置监控地址 这个就是我们代码中暴露的监控端口
    • 注意:本章采用配置文件记录监控目标,也可以采用配置中心的方式
global:
  scrape_interval:     15s # 默认每15秒采集一次
  external_labels:
    monitor: 'go-micro-monitor'
scrape_configs:
  #监控的服务
  - job_name: 'order'  #名称
    scrape_interval: 5s   #覆盖默认值,设置5秒一次
    static_configs:
      - targets: ['192.168.242.142:9092']  #设置监控地址
  1. 启动promethues和grafana
# 启动
docker-compose up -d
# 特别注意docker-compose 中down命令会清除数据
# 看下监控 选择target 点进去metric可以看到一些参数
http://192.168.242.142:9090/
# grafana看板 admin admin 
http://192.168.242.142:3000/

在这里插入图片描述
4. 添加数据源,并测试一下
在这里插入图片描述
5. 简单看板配置
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值