新技术产生必然是为了解决某些需求、痛点,否则这项技术就没有意义。——佚名
遇到问题,先把问题的共性总结并分类,然后一类一类地解决,这叫批处理。——斯科特·扬
舍弃一切去做一件事情,没有值不值得,只有想不想。——吾之秦韵
有时候会想,这里面的重要信息是什么?但自己的水平还不够,知道的不够全面,只能先都记录下来。
第7关 性能监控能力完善
- 7.1 监控系统prometheus基本介绍
- 7.2 docker-compose 基础介绍
- 7.3 订单领域 proto 开发
- 7.4 订单领域 model 开发
- 7.5 订单领域 repository 开发
- 7.6 订单领域 service 开发
- 7.7 微服务handler 代码编写
- 7.8 订单main.go 添加 prometheus 监控
- 7.9 监控系统可视化
7.1 监控系统prometheus基本介绍
1.本小节目标:
(1)学习微服里面另一个非常重要的能力就是性能监控。
2.主要内容
(1)监控系统介绍
(2)Docker-Compose使用
为什么要使用docker-compose呢?
因为在前面的章节,大家在使用容器的时候,每章节都会用到不同的容器,
但是在生产环境或者日常工作中,我们不会一个一个地去启动容器,因为这很费时间。
我们会用docker-compose统一的管理我们的容器,常用的操作:
a. 会写docker-compose.yml
b. docker-compose up -d
c. docker-compose down
(3)订单服务开发
以此达到使用监控系统和docker-compose的目的。
3.监控系统介绍 prometheus
接入的监控系统是普罗米修斯,分3个方面介绍:
(1)监控的基本词汇
(2)监控系统的工作原理
(3)监控系统的安装
4.微服务监控系统prometheus基本介绍
(1)是开源的监控 & 报警 & 时间序列数据库的组合
(2)基本原理是通过HTTP协议周期性地抓取被监控组件的状态
HTTP访问的方式,告诉监控系统,需要监控的指标。
(3)适合docker,k8s环境的监控系统
之所以引入监控系统,就是要在生产环境中,我们需要知道:
某些时段里面,应用程序用了多少CPU,用了多少内存,用了多少硬盘空间。
5.prometheus架构图
解读信息:
(1)最左边第一个Short-lived jobs 短暂的任务
短暂的任务 是什么意思?
短暂的任务是不会提供长时间的HTTP的方式 给 prometheus 去抓取数据,
而是这个服务很短暂,它运行完了以后,它很可能被关闭了,这是短暂的服务。
那么这个短暂的服务怎么去监控呢?
短暂的服务它是通过主动的方式去Push到Gateway上面【Gateway就是数据收集的网关】,然后进而由prometheus 的server端去抓取PushGateway推上来的数据。
那么还有另一种方式就是下面 ClientLib/Exporters。
可以通过不同的Exporters提供HTTP的方式来给prometheus 的server端 去抓取数据。
(2)
抓取数据说完了以后,我们再来看prometheus 的server端。
prometheus 的server端主要分为3个环节:
首先就是抓取数据,
然后它会把抓取完以后的数据,
最后存储的基础上,因为这是监控系统,所以我们要对存储的数据进行查询。
以上3个环节组成了prometheus内部的存储结构和存储逻辑。
那么这里还要说一点,我们在抓取的过程中,即抓取我们网关数据的时候,
prometheus 的服务端它是通过怎么样的方式来知道我们需要抓取哪个端上的链接呢?
如何获取抓取目标?
即图中目标发现的框框。
获取监控,获取地址 是通过:
a. 配置文件
b. 文本文件
c. consuld等方式
来存储我们所需要的抓取目标。
我们实践是用配置文件的方式,来记录我们需要抓取的目标,prometheus 根据这些抓取目标,定期的去采集我们的数据。
再来看一下prometheus,当数据抓取完了以后,存储进来的时候,prometheus 会根据我们的报警规则计算一下是否满足报警规则,如果在我们的报警规则里面,它会提供报警消息,并且主动的推送给我们报警模块,报警模块收到消息以后,它会经过一系列的处理来进行报警。
另外数据存储平台提供给客户访问的时候,报警规则还有一个简单的WebUI,可以通过界面的方式去查询所存储的一些数据。
以上就是prometheus 整体架构图的解读。
总结一下:
这里有Push网关,主要是针对我们短时的任务,
还有ClientLib就是客户端,然后还有各个Exporters,
通过这些我们可以提供数据给prometheus的服务端进行采集,
当服务端进行采集以后,我们服务端的内部它会把它存储起来,然后通过报警规则计算出它是否需要推送我们的报警消息,然后服务端也提供我们的查询能力,提供这些查询能力是给WebUI使用的。
那么我们说完架构图,它上面有很多重要的组件,那么重要的组件有哪些呢?我们就来说一下。
6.微服务监控系统 prometheus重要组件
(1)Prometheus Server:用于收集和存储时间序列数据。
存储用到了实时数据库。
(2)Client Library:客户端库,它会生成相应的metrics【指标】并暴露给Prometheus Server端进行采集信息。
(3)Push Gateway:主要用于短期的jobs。
如果你的程序是短期的一个任务,这个短期的任务,它Prometheus可能是5秒钟去采集一次,但是短期的任务可能在5秒之内就已经完成了,这样的服务端是采集不到你这个短期任务的指标的。
所以我们这个短期任务可以主动上报到我们到Push Gateway上,我们的Prometheus定期的去采集Push Gateway就可以了。
(4)Exporters:用于暴露已有的第三方的服务的metrics【指标】给Prometheus 使用。
这里的第三方的服务比如说Redis,MySQL,MongoDB等已经写好的Exporters我们可以把它拿过来直接用。
拿来用了以后,把需要采集的功能暴露出来,Prometheus去采集。
(5)Alertmanager:报警模块。从Prometheus Server端接收到alerts后,会进行去重,分组,对路由接受的方式发出一些警报。
比如说它通过微信,通过Email,还有QQ等等,它可以进行多种形式的发送。
7.prometheus普罗米修斯的工作流程是什么?
prometheus服务端会定期的从配置好的jobs/exports/PushGateway这里面去拉取数据,定期的拉取目标的数据。
当我们把数据拉起完了以后,会在服务端记录这些数据,并且根据我们的报警规则推送我们的报警数据,给Alertmanager就是报警模块,我们的Alertmanager根据配置文件对接收到的报警进行处理,并发出我们的警告。
最后就是存储和提供查询。提供简单的WebUI,在图形化界面中,把我们采集到的数据可视化展示出来。后面会介绍Grafana,它可以美观直接大气的展示图形。
8.微服务监控系统prometheus相关概念 数据模型
prometheus它要存储数据,它的数据就会有一个规定的方式去存储数据,这个规定方式就是数据模型,那么它的数据模型是什么呢?
(1)Prometheus 中存储的数据是时间序列。
数据存储到实时数据库里面。
(2)格式上:由metrix的名字和一系列的标签(键值对)唯一标识组成。
(3)不同的标签代表不同的时间序列。
再来说一下数据模型里面的metrix【指标】。
metrix怎么去理解呢?通俗的叫法叫做 指标。
下面来说一下指标有哪些类型。
(a)Conter类型:
一种累加性的指标,
比如请求的个数【某个时间点请求了多少,累加性】
比如出现的错误数【某段时间里面出现了错误的总数是什么】
(b)Gauge类型:
可以任意的加减,
比如温度,还有运行的goroutines的个数。
(c)Histogram类型:
可以对观察的结果进行采样分组及统计,
比如柱状图。
(d)Summary类型:
它可以提供观测的count和sum的功能,
比如说我们的请求使用时间。
我知道了这么多指标的类型有什么用?
我们在监控系统配置的时候,会给大家看一下这个类型有什么用,这里大家要知道它这个指标的类型主要有4大类。
9.微服务监控系统prometheus相关概念 instance和jobs
(1) instance
instance它也可以单独的监控一个目标,一般对应一个应用进程。
(2)jobs
jobs就一组同类型的instances的组合,主要是用于保证应用的可扩展性和可靠性。
这句话怎么去理解呢?
假设应用程序处理能力不够,我横向扩展了三个应用程序,我们分别要对这个三个应用进行监控,但是我代码上面写的包括端口都基本上是一样的,我们监控这一块,就是要通过instance和jobs进行组合。
当然它提供的handler,以及暴露数据的服务,每个不同的服务它会认为是不同的instance,然后服务即使不一样,但是它的类型是一样的。类型把它组成一个jobs,相同类型它会组成一个jobs,这样不管我们的应用程序扩展成多少个,我们都可以很细腻的去监控我们的应用程序。
10.微服务监控系统Grafana看板
Grafana是用来展示prometheus里面监控的数据的。
Grafana的3个好处:
(1)它拥有丰富的dashboard和图表编辑的指标分析平台。
(2)它拥有自己的权限管理和用户管理系统,如果要扩展的话,也可以去在这个基础上进行定制。
(3)更适用于我们数据可视化和展示。
在平时工作中我们就发现Grafana的数据展示能力特别的强,在微服务提供数据可视化的过程中,包括大数据化可视化的系统里面,Grafana用的也是非常的多。
如何去安装?
docker pull cap1573/promethues-grafana
docker run -d -p 9090:9090 -p 9100:9100 -p 3000:3000 cap1573/promethues-grafana
// 监控系统控制台 ip:3000
7.2 docker-compose 基础介绍
目标:
(1)什么是docker-compose?有哪些作用?
(2)docker-compose安装与yml文件
(3)docker-compose常用的命令
1.docker-compose介绍
(a)它是用来定义和运行多容器的docker应用程序的工具。
就是我们很零散的一些docker容器,比如说我们前面几个章节里面,每个章节里面都启用了不同的docker容器。
我们在平时工作中也不会去一个一个的启动,因为这样很浪费时间,包括我们各个容器之间启动的顺序,它这里也可以去定义,如果没有了这个工具,我们一个一个启动是人为控制的,这样非常麻烦。
(b)使用yml文件来配置应用程序需要的所有服务。
这个配置文件它可以用来配置应用程序所需要的一些服务,也就是说我们通过这个配置文件来标识我们这个容器是什么服务名称,以及它所暴露的端口等等一系列的,我们都可以在这个配置文件里面写。
(c)使用一个命令,就可以创建并启动所有的服务。
比如说我们到时候可能在yml文件里面定义10个服务,我们只需要1个简单命令就可以把它启动,同样也只需使用1个简单命令就可以把它给关掉,这样是非常方便的。
2.docker-compose使用三步曲
(1)首先会使Dockerfile来定义应用程序的环境
(2)其次是使用docker-compose.yml文件定义构成应用程序的服务
它所需要暴露这个端口,它所需要挂载的一些目录结构等等,都会在docker-compose.yml文件中把它写出来。
主要的工作量就在这里。
(3)最后,
执行docker-compose up -d 命令,在后台启动并运行应用程序;
执行docker-compose down 命令,关闭应用程序。
3. docker-compose的安装
(1)Linux
Linux 上可从 Github 下载它的二进制包来使用.
最新发行的版本地址:https://github.com/docker/compose/releases
/*运行以下命令以下载 Docker Compose 的当前稳定版本:
*要安装其他版本的 Compose,请替换 1.24.1
*/
sudo curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
// 将可执行权限应用于二进制文件:
sudo chmod +x /usr/local/bin/docker-compose
//创建软链:
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
//测试是否安装成功:
docker-compose --version
(2)macOS
Mac 的 Docker 桌面版和 Docker Toolbox 已经包括 Compose 和其他 Docker 应用程序,因此 Mac 用户不需要单独安装 Compose。
(3)Windows
Windows 的 Docker 桌面版和 Docker Toolbox 已经包括 Compose 和其他 Docker 应用程序,因此 Windows 用户也不需要单独安装 Compose。
3.docker-compose的yml文件
(1)使用docker-compose.yml文件来定义构成的应用程序的服务。
(2)用户通过模板文件来定义一组相关联的应用的容器。
(3)标准模板文件应该包含version、services、networks三大部分,最关键的是services部分。
4.docker-compose常用命令
docker-compose up -d // 后台运行一组容器
docker-compose ps // 列出项目中所有的容器
docker-compose down // 停止和删除容器及网络
5.docker-compose使用的一个简单的例子
图片:
代码:
# 声明版本
version: "3"
services:
# 服务注册。服务名称,该服务名称可以在同一个网络进行访问
consul-imooc:
# 说明采用的镜像的地址
image: cap1573/consul
ports:
- "8500:8500"
# 链路追踪
jaeger-imooc: # 名称
image: cap1573/jaeger
ports:
- "6831:6831/udp"
- "16686:16686"
# 熔断器
hystrix-dashboard:
image: cap1573/hystrix-dashboard
ports:
- "9002:9002"
7.3 订单领域 proto 开发
步骤1:
文件位置:order/proto/order/order.proto
syntax = "proto3";
package go.micro.service.order;
// 创建 订单服务
service Order {
// 根据订单ID查找订单的接口
rpc GetOrderByID(OrderID) returns (OrderInfo){}
// 获取所有订单的接口
rpc GetAllOrder(AllOrderRequest) returns (AllOrder){}
// 新增订单的接口
rpc CreateOrder(OrderInfo) returns (OrderID){}
// 根据订单ID删除订单的接口
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;
}
步骤2:
make proto
7.4 订单领域 model 开发
下一步的行动是:
完成订单领域层domain下的model代码开发。
其次是repository,最后是service。
步骤1:
文件位置:order/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
}
文件位置:order/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"`
}
7.5 订单领域 repository 开发
文件位置:order/domain/repository/order_repository.go
package repository
import (
"errors"
// 改成你自己的git.imooc order仓库的地址。
"git.imooc.com/keegan/order/domain/model"
"github.com/jinzhu/gorm"
)
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
}
7.6 订单领域 service 开发
文件位置:order/domain/service/order_data_service.go
package service
import (
"git.imooc.com/keegan/order/domain/model"
"git.imooc.com/keegan/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)
}
7.7 微服务handler 代码编写
如果缺少依赖包,那么:
/*
*go mod tidy
*go get git_url
*/
文件位置:order/handler/order.go
package handler
import (
"context"
"git.imooc.com/keegan/common"
"git.imooc.com/keegan/order/domain/model"
"git.imooc.com/keegan/order/domain/service"
. "git.imooc.com/keegan/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
}
7.8 订单main.go 添加 prometheus 监控
步骤0:
为了提高prometheus 配置代码的复用,把配置信息的文件放到common模块下。
文件位置:common/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))
}()
}
然后推送代码:
git add .
git commit -m "监控"
git push origin master
步骤1:
每个服务/模块依赖的服务不一样,main.go文件的配置也不一样,极大的降低了代码的耦合度。
不把鸡蛋放到同一个篮子,这也会降低我们的排错范围。
获取common 依赖发现问题:
go get git.imooc.com/keegan/common 获取不到最新的代码。
解决方法是更改代理 GOPROXY的设置:
go env -w GOPROXY=https://mirrors.aliyun.com/goproxy,direct
然后再执行 go get git.imooc.com/keegan/common 就没问题了。
修改代理参考链接
解决思路来源链接
文件位置:order/main.go
package main
import (
"git.imooc.com/keegan/common"
"git.imooc.com/keegan/order/domain/repository"
service2 "git.imooc.com/keegan/order/domain/service"
"git.imooc.com/keegan/order/handler"
order "git.imooc.com/keegan/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 + "@tcp(" + mysqlInfo.Host + ":" + mysqlInfo.Port + ")/"+ 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)
}
}
在main.go 的同级目录下执行 go run main.go
浏览器访问:http://127.0.0.1:8500/
7.9 监控系统可视化
当你发现时间是贼的时候,它早已偷光你所有的选择。
如何使用普罗米修斯的监控?
(1)docker-compose.yml配置文件
(2)prometheus.yml配置文件
global: # 全局参数
scrape_interval: 15s # 默认每15秒采集一次
external_labels: # 用于外部系统标签的,不是用于metrics数据
monitor: 'go-micro-monitor'
scrape_configs:
# 监控的服务
- job_name: 'order' # jobs的名称
scrape_interval: 5s # 覆盖默认值,设置5秒一次
static_configs: # 需要抓取的目标
- targets: ['192.168.0.123:9092'] # 设置监控地址。本机地址获取ifconfig
(3)启动
准备工作:
docker ps
docker stop container_id
// docker-compose.yml里面的服务,都要stop掉,否则会端口冲突
现在,可以:
在docker-compose.yml文件统计的目录下
docker-compose up -d
docker ps
第二次启动main.go文件的内容如下:
package main
import (
"git.imooc.com/keegan/common"
"git.imooc.com/keegan/order/domain/repository"
service2 "git.imooc.com/keegan/order/domain/service"
"git.imooc.com/keegan/order/handler"
order "git.imooc.com/keegan/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 + "@tcp(" + mysqlInfo.Host + ":" + mysqlInfo.Port + ")/"+ 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)
}
}
看一下表:
order服务也注册上了:
关掉代理clash
看看图表:
登录进来以后:
如何配置看板?