0.前言
之前自行开发了一个exporter用来监控服务器接收请求数的情况,是基于命令的方式来获取服务器信息,今天我们通过请求外部服务的方式来获取监控信息。日常工作中短信通知业务通常会用到云上的短信服务,短信服务的使用情况就会是需要关注的一个点,那么今天我们就通过调用云上短信业务相关接口,来实现云上短信使用情况的监控,因为我使用的阿里云短信服务,所以这里以阿里云为例。
1.资源准备
操作系统 | IP地址 | CPU | 内存 | 用途 |
---|---|---|---|---|
rocky9 linux | 10.173.100.15 | 8核 | 32G | 夜莺、exporter所在服务器 |
以上配置仅供参考,大家用其他操作系统和配置的服务器也可以,因为是调用阿里云的接口,所以在一台服务器上调用接口查询数据即可。
2.短信接口解析
因为要调用阿里云短信接口,所以调用之前先来看下云短信SDK使用方法。
阿里云接口文档地址:阿里云API入口地址
进入之后出现如下界面:
之后在搜索框输入"短信",然后点击【搜索】按钮,之后进入短信API文档界面,如下图所示:
我们今天主要要做的就是查询短信发送情况,所以本次调用的接口就是:"QuerySendStatistics",找到该接口点击【去调试】按钮,进入调试界面之后选中"SDK示例"并选中"Go"选项,采用Go语言调试该接口,如下图所示:
这里会告诉用户查询短信使用情况的SDK的用法,配上了代码和代码解释说明以及需要添加的参数作用,我们可以稍微看下每个函数的作用,之后化身CV战士将代码拷贝到我们的IDE即可。
(1)SDK依赖:
import (
"encoding/json"
"strings"
"fmt"
"os"
dysmsapi20170525 "github.com/alibabacloud-go/dysmsapi-20170525/v5/client"
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
util "github.com/alibabacloud-go/tea-utils/v2/service"
credential "github.com/aliyun/credentials-go/credentials"
"github.com/alibabacloud-go/tea/tea"
)
(2)创建连接客户端:
// Description:
//
// 使用凭据初始化账号Client
//
// @return Client
//
// @throws Exception
func CreateClient () (_result *dysmsapi20170525.Client, _err error) {
// 工程代码建议使用更安全的无AK方式,凭据配置方式请参见:https://help.aliyun.com/document_detail/378661.html。
credential, _err := credential.NewCredential(nil)
if _err != nil {
return _result, _err
}
config := &openapi.Config{
Credential: credential,
}
// Endpoint 请参考 https://api.aliyun.com/product/Dysmsapi
config.Endpoint = tea.String("dysmsapi.aliyuncs.com")
_result = &dysmsapi20170525.Client{}
_result, _err = dysmsapi20170525.NewClient(config)
return _result, _err
}
示例代码里获取的是临时的用户凭据,真正调试的时候用户需要使用AK(access-key)方式,才允许创建连接短信接口的客户端,AK请求方式代码需要修改如下:
import (
"fmt"
"os"
"github.com/aliyun/credentials-go/credentials"
)
func main() {
config := new(credentials.Config).
SetType("access_key").
SetAccessKeyId(os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")).
SetAccessKeySecret(os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"))
akCredential, err := credentials.NewCredential(config)
if err != nil {
return
}
accessKeyId, err := akCredential.GetAccessKeyId()
accessSecret, err := akCredential.GetAccessKeySecret()
credentialType := akCredential.GetType()
fmt.Println(accessKeyId, accessSecret, credentialType)
}
(3)私有main函数:
func _main (args []*string) (_err error) {
client, _err := CreateClient()
if _err != nil {
return _err
}
querySendStatisticsRequest := &dysmsapi20170525.QuerySendStatisticsRequest{}
runtime := &util.RuntimeOptions{}
tryErr := func()(_e error) {
defer func() {
if r := tea.Recover(recover()); r != nil {
_e = r
}
}()
// 复制代码运行请自行打印 API 的返回值
_, _err = client.QuerySendStatisticsWithOptions(querySendStatisticsRequest, runtime)
if _err != nil {
return _err
}
return nil
}()
if tryErr != nil {
var error = &tea.SDKError{}
if _t, ok := tryErr.(*tea.SDKError); ok {
error = _t
} else {
error.Message = tea.String(tryErr.Error())
}
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
// 错误 message
fmt.Println(tea.StringValue(error.Message))
// 诊断地址
var data interface{}
d := json.NewDecoder(strings.NewReader(tea.StringValue(error.Data)))
d.Decode(&data)
if m, ok := data.(map[string]interface{}); ok {
recommend, _ := m["Recommend"]
fmt.Println(recommend)
}
_, _err = util.AssertAsString(error.Message)
if _err != nil {
return _err
}
}
return _err
}
获取数据的主要逻辑,以及最终获取数据的结果,都封装在了_main函数里面,我们姑且称其为私有的main函数,要注意的是获取数据的代码为:
_, _err = client.QuerySendStatisticsWithOptions(querySendStatisticsRequest, runtime)
这里的下划线就是获取的返回结果,阿里云示例代码隐去了,让用户自行打印,我们可以将其赋值给request变量。
(4)真正的main函数:
func main() {
err := _main(tea.StringSlice(os.Args[1:]))
if err != nil {
panic(err)
}
}
main函数,调用_main函数,并最终运行程序。
3.exporter开发
其实和获取服务器请求量的exporter没太大区别,只不过指标结构体和请求数据的方式需要改变,还是带着大家把完整的代码过一遍。
(1)项目依赖:
import (
"encoding/json"
"fmt"
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
dysmsapi20170525 "github.com/alibabacloud-go/dysmsapi-20170525/v5/client"
util "github.com/alibabacloud-go/tea-utils/v2/service"
"github.com/alibabacloud-go/tea/tea"
credential "github.com/aliyun/credentials-go/credentials"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"log"
"net/http"
"strings"
"time"
)
主要需要关注的依赖有阿里云短信SDK和prometheus的SDK。
(2)collector结构体:
type messageCollector struct {
noRespondedCountMetric *prometheus.Desc
respondedFailCountMetric *prometheus.Desc
respondedSuccessCountMetric *prometheus.Desc
totalCountMetric *prometheus.Desc
}
根据阿里云短信接口获取到的数据类型,我们可以采集四个指标:无响应短信条数,响应失败短信条数,响应成功短信条数,总短信条数。
(3)初始化collector结构体:
func newMessageCollector() *messageCollector {
return &messageCollector{
noRespondedCountMetric: prometheus.NewDesc("no_responded_count_message",
"collect no responded count message",
[]string{"date"}, nil,
),
respondedFailCountMetric: prometheus.NewDesc("responded_failed_count_message",
"collect responded failed count message",
[]string{"date"}, nil,
),
respondedSuccessCountMetric: prometheus.NewDesc("responded_success_count_message",
"collect responded success count message",
[]string{"date"}, nil,
),
totalCountMetric: prometheus.NewDesc("total_count_message",
"collect message total count",
```,
[]string{"date"}, nil,
),
}
}
指定四个指标的基础信息,并以当天日期为标签。
(4)采集指标必备函数Describe:
func (mc *messageCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- mc.noRespondedCountMetric
ch <- mc.respondedFailCountMetric
ch <- mc.respondedSuccessCountMetric
ch <- mc.totalCountMetric
}
该函数主要是向prometheus解释指标的基础信息。
(5)程序必要变量:
var (
messageSecretId = "XXXXXXXXXXXXXXXXXX" // 阿里云secretId
messageSecretKey = "XXXXXXXXXXXXXXXXXX" // 阿里云secretKey
isGlobe = 1 // 表示查询国内短信
pageIndex = 1 // 页码
pageSize = 1 // 每页的条目
)
我们只调用当天的短信使用情况,所以只采集当前采集到的条目即可。
(6)创建阿里云客户端:
func createClient() (client *dysmsapi20170525.Client, err error) {
// 工程代码建议使用更安全的无AK方式,凭据配置方式请参见:https://help.aliyun.com/document_detail/378661.html。
credentialsConfig := new(credential.Config).
SetType("access_key").
SetAccessKeyId(messageSecretId).
SetAccessKeySecret(messageSecretKey)
akCredential, err := credential.NewCredential(credentialsConfig)
if err != nil {
return client, err
}
config := &openapi.Config{}
// Endpoint 请参考 https://api.aliyun.com/product/Dysmsapi
config.Endpoint = tea.String("dysmsapi.aliyuncs.com")
config.Credential = akCredential
client = &dysmsapi20170525.Client{}
client, err = dysmsapi20170525.NewClient(config)
return
}
采用的AK的方式创建阿里云客户端,代码可以直接从阿里云SDK拷贝。
(7)获取短信消费情况:
func getMessages() (*dysmsapi20170525.QuerySendStatisticsResponse, error) {
client, err := createClient()
if err != nil {
return nil, err
}
todayTm := time.Now().Format("20060102")
querySendStatisticsRequest := &dysmsapi20170525.QuerySendStatisticsRequest{
IsGlobe: tea.Int32(int32(isGlobe)),
StartDate: tea.String(todayTm),
EndDate: tea.String(todayTm),
PageIndex: tea.Int32(int32(pageIndex)),
PageSize: tea.Int32(int32(pageSize)),
}
runtime := &util.RuntimeOptions{}
request := &dysmsapi20170525.QuerySendStatisticsResponse{}
tryErr := func() (_e error) {
defer func() {
if r := tea.Recover(recover()); r != nil {
_e = r
}
}()
// 复制代码运行请自行打印 API 的返回值
request, err = client.QuerySendStatisticsWithOptions(querySendStatisticsRequest, runtime)
if err != nil {
return err
}
fmt.Println("request:", request)
return nil
}()
if tryErr != nil {
var error = &tea.SDKError{}
if _t, ok := tryErr.(*tea.SDKError); ok {
error = _t
} else {
error.Message = tea.String(tryErr.Error())
}
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
// 错误 message
fmt.Println(tea.StringValue(error.Message))
// 诊断地址
var data interface{}
d := json.NewDecoder(strings.NewReader(tea.StringValue(error.Data)))
d.Decode(&data)
if m, ok := data.(map[string]interface{}); ok {
recommend, _ := m["Recommend"]
fmt.Println(recommend)
}
_, err = util.AssertAsString(error.Message)
if err != nil {
return nil, err
}
}
return request, nil
}
通过client请求阿里云短信消费信息,并获取响应数据。 (8)采集指标必要函数collect:
func (mc *messageCollector) Collect(ch chan<- prometheus.Metric) {
collector := newMessageCollector()
request, err := getMessages()
if err != nil {
log.Fatal(err)
}
targets := request.Body.Data.TargetList
for _, t := range targets {
noResponseCount := float64(*t.NoRespondedCount)
m1 := prometheus.MustNewConstMetric(collector.noRespondedCountMetric, prometheus.GaugeValue,
noResponseCount, *t.SendDate)
ch <- m1
respondedFailCount := float64(*t.RespondedFailCount)
m2 := prometheus.MustNewConstMetric(collector.respondedFailCountMetric, prometheus.GaugeValue,
respondedFailCount, *t.SendDate)
ch <- m2
respondedSuccessCount := float64(*t.RespondedSuccessCount)
m3 := prometheus.MustNewConstMetric(collector.respondedSuccessCountMetric, prometheus.GaugeValue,
respondedSuccessCount, *t.SendDate)
ch <- m3
totalCount := float64(*t.TotalCount)
m4 := prometheus.MustNewConstMetric(collector.totalCountMetric, prometheus.GaugeValue,
totalCount, *t.SendDate)
ch <- m4
}
}
根据请求接口获取到的数据,转化为指标数据展示。
(9)初始化函数和主函数:
func init() {
prometheus.MustRegister(newMessageCollector())
}
func main() {
http.Handle("/metrics", promhttp.Handler())
log.Println("Listening on :9095")
log.Fatal(http.ListenAndServe(":9095", nil))
}
初始化函数用来注册collector,主函数用来启动exporter服务,exporter服务使用的是9095端口。
4.prometheus指标采集
exporter已经开发完成,接下来启动exporter服务,然后prometheus就可以采集exporter暴露的数据了。
4.1 启动exporter
在go语言环境下,执行以下命令初始化依赖,并运行exporter服务:
cd /data/go/gopath/src/message_exporter/
go mod init
go mod tidy
nohup go run ./main.go > server.log &
4.2 prometheus注册exporter
修改prometheus的配置,采集exporter暴露出来的数据,步骤如下:
vim /usr/local/prometheus/prometheus.yml
- targets: ["10.173.100.15:9095"]
labels:
app: "message-exporter"
product: "message-counter"
env: "pro"
exporter的标签可以根据自己需要修改,修改完配置之后记得重启prometheus。
4.3 指标查询
通过IP:Port(10.173.100.15:9090)的形式访问prometheus查询页面,然后依次输入之前定义的查询指标:total_count_message,no_responded_count_message,responded_failed_count_message,responded_success_count_message,查询结果如下图所示:
可以看到指标被成功采集到,之后就可以在夜莺平台制作展示页面了。
5.自制仪表盘
现在监控指标已经采集到了,并接成功的集成到了prometheus当中,接下来只需要在夜莺绘制一个仪表盘查询指标即可,如下图所示:
6.总结
本次通过调用阿里云短信服务的接口获取短信使用情况,阿里云调用接口的SDK首次使用还是有很多坑的,需要多看下文档,多调试,exporter的代码编写也需要认真仔细,否则很容易出现指标错误的情况。