华为云CES监控与飞书通知

华为云负载均衡连接数监控与飞书通知

在云服务的日常运维中,持续监控资源状态是保障系统稳定性的关键步骤之一。本文通过一个实际案例展示了如何使用华为云的Go SDK获取负载均衡器的连接数,并通过飞书Webhook发送通知到团队群组,以便运维人员及时获取最新的监控信息。本来准备直接使用ces告警,但是看了一下模版以及最佳实践貌似没有很好的支持webhook,就直接自己使用go sdk实现了!

背景知识

在华为云上,负载均衡服务(ELB)用于分发来自客户端的网络请求到多个云服务器,确保系统在面对不同的负载情况时,仍能够提供稳定、可靠的服务。ELB的性能指标,如每分钟连接数(CPS),是反映当前系统承载能力的重要数据。通常情况下,我们希望能够实时监控这些关键指标。

随着云服务技术的成熟,大型企业往往会将监控数据集成到实时通讯工具中,便于团队成员即时查看和响应潜在的问题。本案例中选择的通讯工具是飞书,华为云Go SDK则是我们与华为云服务交互的媒介。

环境准备

华为云提供的Go SDK是一套围绕华为云API构建的开发工具包,使得开发者可以在Go语言环境中便捷地调用云服务。在这里,我们利用Cloud Eye Service (CES) 的API,通过SDK检索ELB的CPS指标数据。

安装华为云Go SDK

首先需要安装华为云Go SDK。可以通过go get命令安装所需的SDK包:

go get -u github.com/huaweicloud/huaweicloud-sdk-go-v3

安装完成后,即可在项目中引入相关的SDK模块。

初始化客户端

要与华为云的服务交互,我们需要创建并初始化一个SDK客户端。如下示例中,我们创建了用于CES(Cloud Eye Service)服务的客户端,并使用了之前提到的AK和SK进行了认证。

package main

// 导入相关的包
import (
    "fmt"
    "bytes"
    "json"
    "http"
    "ioutil"
    "time"

    "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic"
    "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/ces/v1"
    ces "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/ces/v1/model"
)

const (
    feishuWebhookURL = "xxxx" // 飞书Webhook URL
    ak               = "xxx"  // Access Key
    sk               = "xxxxx" // Secret Key
)

func main() {
    // 构建认证信息
    auth := basic.NewCredentialsBuilder().
       WithAk(ak).
       WithSk(sk).
       Build()

    // 初始化CES客户端
    client := ces.NewCesClient(
       ces.CesClientBuilder().
          WithRegion(region.ValueOf("cn-east-3")).
          WithCredential(auth).
          Build())

    // ...后续代码
}

设置定时器和执行任务

我们通过一个定时器来定期检查负载均衡器的最大连接数,例如:

    ticker := time.NewTicker(1 * time.Minute) // 每分钟触发检查
    for {
       select {
       case t := <-ticker.C:
          currentHour := t.Hour()
          // 只在既定的时间范围内执行
          if currentHour >= 7 && currentHour < 24 {
             // 我们设定在59分时收集数据
             if t.Minute() == 59 {
                go collectDataAndSendToFeishu(client)
             }
          }
       }
    }

这里限制了定时器发送的时间范围早上7点到24点执行,0点-7点默认不执行。并且执行的时间是每个小时的59分执行!

收集和发送数据

一旦定时器触发并满足条件,我们会收集负载均衡的最大连接数并发送给飞书,参考 华为云ces ShowMetricData接口:
image.png
具体实现如下,注意**ShowMetricDataRequest **中具体参数:

func collectDataAndSendToFeishu(client *ces.CesClient) {
    currentTime := time.Now().UTC()
    startTime := currentTime.Truncate(time.Hour).Add(time.Minute * 58)
    endTime := startTime.Add(time.Minute)

    startTimestamp := startTime.UnixNano() / int64(time.Millisecond)
    endTimestamp := endTime.UnixNano() / int64(time.Millisecond)

    request := &model.ShowMetricDataRequest{
       Namespace:  "SYS.ELB",
       MetricName: "m1_cps",
       Dim0:       "lbaas_instance_id,xxxxxx",
       Filter:     model.GetShowMetricDataRequestFilterEnum().MAX,
       Period:     int32(1),
       From:       startTimestamp,
       To:         endTimestamp,
    }

    response, err := client.ShowMetricData(request)
    if err != nil {
       fmt.Println("Error querying CES data:", err)
       return
    }
    fmt.Printf("CES response: %+v\n", response)

    // Extract max value and timestamp from the response
    var maxConnection float64
    var timestamp int64
    if response.Datapoints != nil && len(*response.Datapoints) > 0 {
       datapoints := *response.Datapoints
       maxConnection = *datapoints[0].Max
       timestamp = datapoints[0].Timestamp
    }

    // Format the timestamp to a readable form
    readableTime := time.Unix(timestamp/1000, (timestamp%1000)*int64(time.Millisecond)).Format("2006-01-02 15:04:05")
    // Prepare the message to send to Feishu
    feishuMessage := fmt.Sprintf("当前时间 %s 负载均衡最大连接数是 %.2f", readableTime, maxConnection)

    if err := sendToFeishuWebhook(feishuWebhookURL, feishuMessage); err != nil {
       fmt.Println("Error sending to Feishu webhook:", err)
    }
}

发送Webhook通知

最后,实现sendToFeishuWebhook方法以将消息推送到飞书。

func sendToFeishuWebhook(webhookURL string, message string) error {
    webhookMessage := FeishuWebhookMessage{
       MsgType: "text",
    }
    webhookMessage.Content.Text = message

    jsonData, err := json.Marshal(webhookMessage)
    if err != nil {
       return fmt.Errorf("failed to marshal webhook message: %v", err)
    }

    req, err := http.NewRequest("POST", webhookURL, bytes.NewBuffer(jsonData))
    if err != nil {
       return fmt.Errorf("failed to create HTTP request: %v", err)
    }
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
       return fmt.Errorf("failed to send HTTP request: %v", err)
    }
    defer resp.Body.Close()

    responseBody, err := ioutil.ReadAll(resp.Body)
    if err != nil {
       return fmt.Errorf("failed to read webhook response body: %v", err)
    }

    fmt.Println("Feishu webhook response:", string(responseBody))
    return nil
}

完整代码:

每小时59分统计58-59分最大值发送统计到飞书:

package main
type FeishuWebhookMessage struct {
    MsgType string `json:"msg_type"`
    Content struct {
       Text string `json:"text"`
    } `json:"content"`
}

const (
    // 定时器间隔,用于根据特定时间点触发数据检索。例如:59分时执行任务,就是(59 - 当前时间的分钟数) x 每分钟的秒数
    feishuWebhookURL = "xxxx"
    ak               = "xxx"
    sk               = "xxxxx"
)

func main() {
    auth := basic.NewCredentialsBuilder().
       WithAk(ak).
       WithSk(sk).
       Build()

    client := ces.NewCesClient(
       ces.CesClientBuilder().
          WithRegion(region.ValueOf("cn-east-3")).
          WithCredential(auth).
          Build())

    ticker := time.NewTicker(1 * time.Minute) // Check every 10 minutes to adjust for the next 59th minute.
    for {
       select {
       case t := <-ticker.C:
          // 这里设置只在7-24点执行
          currentHour := t.Hour()
          if currentHour >= 7 && currentHour < 24 {
             if t.Minute() == 59 {
                go collectDataAndSendToFeishu(client)
             }
          }
       }
    }
}
func collectDataAndSendToFeishu(client *ces.CesClient) {
    currentTime := time.Now().UTC()
    startTime := currentTime.Truncate(time.Hour).Add(time.Minute * 58)
    endTime := startTime.Add(time.Minute)

    startTimestamp := startTime.UnixNano() / int64(time.Millisecond)
    endTimestamp := endTime.UnixNano() / int64(time.Millisecond)

    request := &model.ShowMetricDataRequest{
       Namespace:  "SYS.ELB",
       MetricName: "m1_cps",
       Dim0:       "lbaas_instance_id,xxxxxx",
       Filter:     model.GetShowMetricDataRequestFilterEnum().MAX,
       Period:     int32(1),
       From:       startTimestamp,
       To:         endTimestamp,
    }

    response, err := client.ShowMetricData(request)
    if err != nil {
       fmt.Println("Error querying CES data:", err)
       return
    }
    fmt.Printf("CES response: %+v\n", response)

    // Extract max value and timestamp from the response
    var maxConnection float64
    var timestamp int64
    if response.Datapoints != nil && len(*response.Datapoints) > 0 {
       datapoints := *response.Datapoints
       maxConnection = *datapoints[0].Max
       timestamp = datapoints[0].Timestamp
    }

    // Format the timestamp to a readable form
    readableTime := time.Unix(timestamp/1000, (timestamp%1000)*int64(time.Millisecond)).Format("2006-01-02 15:04:05")
    // Prepare the message to send to Feishu
    feishuMessage := fmt.Sprintf("当前时间 %s 负载均衡最大连接数是 %.2f", readableTime, maxConnection)

    if err := sendToFeishuWebhook(feishuWebhookURL, feishuMessage); err != nil {
       fmt.Println("Error sending to Feishu webhook:", err)
    }
}

// sendToFeishuWebhook sends a message to Feishu webhook
func sendToFeishuWebhook(webhookURL string, message string) error {
    webhookMessage := FeishuWebhookMessage{
       MsgType: "text",
    }
    webhookMessage.Content.Text = message

    jsonData, err := json.Marshal(webhookMessage)
    if err != nil {
       return fmt.Errorf("failed to marshal webhook message: %v", err)
    }

    req, err := http.NewRequest("POST", webhookURL, bytes.NewBuffer(jsonData))
    if err != nil {
       return fmt.Errorf("failed to create HTTP request: %v", err)
    }
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
       return fmt.Errorf("failed to send HTTP request: %v", err)
    }
    defer resp.Body.Close()

    responseBody, err := ioutil.ReadAll(resp.Body)
    if err != nil {
       return fmt.Errorf("failed to read webhook response body: %v", err)
    }

    fmt.Println("Feishu webhook response:", string(responseBody))
    return nil
}

运行以上代码:
img_v3_026i_b57d3d26-178b-4f64-8ade-5ecfcd3917dg.jpg

其它的扩展玩法:

每分钟检查一次,当连接数大于100报警触发

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic"
	ces "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/ces/v1"
	"github.com/huaweicloud/huaweicloud-sdk-go-v3/services/ces/v1/model"
	region "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/ces/v1/region"
	"io/ioutil"
	"net/http"
	"time"
)

type FeishuWebhookMessage struct {
	MsgType string `json:"msg_type"`
	Content struct {
		Text string `json:"text"`
	} `json:"content"`
}

const (
	feishuWebhookURL = "xxxx"
	ak               = "xxxx"
	sk               = "xxxxx"
)

func main() {
	auth := basic.NewCredentialsBuilder().
		WithAk(ak).
		WithSk(sk).
		Build()

	client := ces.NewCesClient(
		ces.CesClientBuilder().
			WithRegion(region.ValueOf("cn-east-3")).
			WithCredential(auth).
			Build())

	ticker := time.NewTicker(1 * time.Minute) // Check every 10 minutes to adjust for the next 59th minute.
	for {
		select {
		case t := <-ticker.C:
			// 这里设置只在7-24点执行
			currentHour := t.Hour()
			if currentHour >= 7 && currentHour < 24 {
				go collectDataAndSendToFeishu(client)
			}
		}
	}
}
func collectDataAndSendToFeishu(client *ces.CesClient) {
	currentTime := time.Now().UTC().Truncate(time.Minute)
	startTime := currentTime.Add(-1 * time.Minute)
	endTime := currentTime

	startTimestamp := startTime.UnixNano() / int64(time.Millisecond)
	endTimestamp := endTime.UnixNano() / int64(time.Millisecond)

	request := &model.ShowMetricDataRequest{
		Namespace:  "SYS.ELB",
		MetricName: "m1_cps",
		Dim0:       "lbaas_instance_id,xxxxx",
		Filter:     model.GetShowMetricDataRequestFilterEnum().MAX,
		Period:     int32(1),
		From:       startTimestamp,
		To:         endTimestamp,
	}

	response, err := client.ShowMetricData(request)
	if err != nil {
		fmt.Println("Error querying CES data:", err)
		return
	}
	fmt.Printf("CES response: %+v\n", response)

	// Extract max value and timestamp from the response
	var maxConnection float64
	var timestamp int64
	if response.Datapoints != nil && len(*response.Datapoints) > 0 {
		datapoints := *response.Datapoints
		maxConnection = *datapoints[0].Max
		timestamp = datapoints[0].Timestamp
	}

	// Format the timestamp to a readable form
	readableTime := time.Unix(timestamp/1000, (timestamp%1000)*int64(time.Millisecond)).Format("2006-01-02 15:04:05")
	// Prepare the message to send to Feishu
	if maxConnection > 100 {
		// Prepare the alert message to send to Feishu
		feishuMessage := fmt.Sprintf("警告:当前时间 %s 负载均衡连接数超越100,当前数值是 %.2f", readableTime, maxConnection)

		if err := sendToFeishuWebhook(feishuWebhookURL, feishuMessage); err != nil {
			fmt.Println("Error sending to Feishu webhook:", err)
		}
	}
}

// sendToFeishuWebhook sends a message to Feishu webhook
func sendToFeishuWebhook(webhookURL string, message string) error {
	webhookMessage := FeishuWebhookMessage{
		MsgType: "text",
	}
	webhookMessage.Content.Text = message

	jsonData, err := json.Marshal(webhookMessage)
	if err != nil {
		return fmt.Errorf("failed to marshal webhook message: %v", err)
	}

	req, err := http.NewRequest("POST", webhookURL, bytes.NewBuffer(jsonData))
	if err != nil {
		return fmt.Errorf("failed to create HTTP request: %v", err)
	}
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return fmt.Errorf("failed to send HTTP request: %v", err)
	}
	defer resp.Body.Close()

	responseBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("failed to read webhook response body: %v", err)
	}

	fmt.Println("Feishu webhook response:", string(responseBody))
	return nil
}

为了方便代码的复用性,可读性。将100作为一个可配置常量提取出来:

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic"
	ces "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/ces/v1"
	"github.com/huaweicloud/huaweicloud-sdk-go-v3/services/ces/v1/model"
	region "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/ces/v1/region"
	"io/ioutil"
	"net/http"
	"time"
)

type FeishuWebhookMessage struct {
	MsgType string `json:"msg_type"`
	Content struct {
		Text string `json:"text"`
	} `json:"content"`
}

const (
	// 定时器间隔,用于根据特定时间点触发数据检索。例如:59分时执行任务,就是(59 - 当前时间的分钟数) x 每分钟的秒数
	feishuWebhookURL                = "xxxxx"
	ak                              = "xxxx"
	sk                              = "xxxx"
	loadBalancerConnectionThreshold = 100.00 // 负载均衡连接数阈值
)

func main() {
	auth := basic.NewCredentialsBuilder().
		WithAk(ak).
		WithSk(sk).
		Build()

	client := ces.NewCesClient(
		ces.CesClientBuilder().
			WithRegion(region.ValueOf("cn-east-3")).
			WithCredential(auth).
			Build())

	ticker := time.NewTicker(1 * time.Minute) // Check every 10 minutes to adjust for the next 59th minute.
	for {
		select {
		case t := <-ticker.C:
			// 这里设置只在7-24点执行
			currentHour := t.Hour()
			if currentHour >= 7 && currentHour < 24 {
				go collectDataAndSendToFeishu(client)
			}
		}
	}
}
func collectDataAndSendToFeishu(client *ces.CesClient) {
	currentTime := time.Now().UTC().Truncate(time.Minute)
	startTime := currentTime.Add(-1 * time.Minute)
	endTime := currentTime

	startTimestamp := startTime.UnixNano() / int64(time.Millisecond)
	endTimestamp := endTime.UnixNano() / int64(time.Millisecond)

	request := &model.ShowMetricDataRequest{
		Namespace:  "SYS.ELB",
		MetricName: "m1_cps",
		Dim0:       "lbaas_instance_id,xxxx",
		Filter:     model.GetShowMetricDataRequestFilterEnum().MAX,
		Period:     int32(1),
		From:       startTimestamp,
		To:         endTimestamp,
	}

	response, err := client.ShowMetricData(request)
	if err != nil {
		fmt.Println("Error querying CES data:", err)
		return
	}
	fmt.Printf("CES response: %+v\n", response)

	// Extract max value and timestamp from the response
	var maxConnection float64
	var timestamp int64
	if response.Datapoints != nil && len(*response.Datapoints) > 0 {
		datapoints := *response.Datapoints
		maxConnection = *datapoints[0].Max
		timestamp = datapoints[0].Timestamp
	}

	// Format the timestamp to a readable form
	readableTime := time.Unix(timestamp/1000, (timestamp%1000)*int64(time.Millisecond)).Format("2006-01-02 15:04:05")
	// Prepare the message to send to Feishu
	if maxConnection > loadBalancerConnectionThreshold {
		// Prepare the alert message to send to Feishu
		feishuMessage := fmt.Sprintf("警报:在%s,负载均衡器的连接数超过了%.2f,当前连接数:%.2f", readableTime, loadBalancerConnectionThreshold, maxConnection)

		if err := sendToFeishuWebhook(feishuWebhookURL, feishuMessage); err != nil {
			fmt.Println("Error sending to Feishu webhook:", err)
		}
	}
}

// sendToFeishuWebhook sends a message to Feishu webhook
func sendToFeishuWebhook(webhookURL string, message string) error {
	webhookMessage := FeishuWebhookMessage{
		MsgType: "text",
	}
	webhookMessage.Content.Text = message

	jsonData, err := json.Marshal(webhookMessage)
	if err != nil {
		return fmt.Errorf("failed to marshal webhook message: %v", err)
	}

	req, err := http.NewRequest("POST", webhookURL, bytes.NewBuffer(jsonData))
	if err != nil {
		return fmt.Errorf("failed to create HTTP request: %v", err)
	}
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return fmt.Errorf("failed to send HTTP request: %v", err)
	}
	defer resp.Body.Close()

	responseBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("failed to read webhook response body: %v", err)
	}

	fmt.Println("Feishu webhook response:", string(responseBody))
	return nil
}

注意loadBalancerConnectionThresholdfloat64.

增加 MetricName多个条件

这里以弹性IP EIP与负载均衡为例,我想查询负载均衡连接数大于100报警,并根据负载均衡对应eip的四个指标:“upstream_bandwidth_usage”,“downstream_bandwidth_usage”,“upstream_bandwidth”,"downstream_bandwidth"报警,完整代码如下:

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic"
	ces "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/ces/v1"
	"github.com/huaweicloud/huaweicloud-sdk-go-v3/services/ces/v1/model"
	region "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/ces/v1/region"
	"io/ioutil"
	"net/http"
	"time"
)

type FeishuWebhookMessage struct {
	MsgType string `json:"msg_type"`
	Content struct {
		Text string `json:"text"`
	} `json:"content"`
}

const (
	// 定时器间隔,用于根据特定时间点触发数据检索。例如:59分时执行任务,就是(59 - 当前时间的分钟数) x 每分钟的秒数
	feishuWebhookURL                = "xxxxxxx"
	ak                              = "xxx"
	sk                              = "xxxx"
	upstreamBandwidthThreshold      = 40    // 出网带宽阈值,单位Mbps
	downstreamBandwidthThreshold    = 80    // 入网带宽阈值,单位Mbps(示例中以百分比为单位,根据实际单位调整)
	upstreamUsageThreshold          = 20    // 出网带宽使用率阈值,单位百分比
	downstreamUsageThreshold        = 40    // 入网带宽使用率阈值,单位百分比
)

var metricNames = []string{
	"upstream_bandwidth_usage",
	"downstream_bandwidth_usage",
	"upstream_bandwidth",
	"downstream_bandwidth",
}

func main() {
	auth := basic.NewCredentialsBuilder().
		WithAk(ak).
		WithSk(sk).
		Build()

	client := ces.NewCesClient(
		ces.CesClientBuilder().
			WithRegion(region.ValueOf("cn-east-3")).
			WithCredential(auth).
			Build())

	ticker := time.NewTicker(time.Minute) // Check every minute.
	for {
		select {
		case <-ticker.C:
			// 每分钟执行
			go collectDataAndSendToFeishu(client)
		}
	}
}

func collectDataAndSendToFeishu(client *ces.CesClient) {
	currentTime := time.Now().UTC().Truncate(time.Minute)
	startTime := currentTime.Add(-1 * time.Minute)
	endTime := currentTime

	startTimestamp := startTime.UnixMilli()
	endTimestamp := endTime.UnixMilli()
	dimensionValues := "bandwidth_id,xxxx"
	for _, metricName := range metricNames {
		request := &model.ShowMetricDataRequest{
			Namespace:  "SYS.VPC",
			MetricName: metricName,
			Dim0:       dimensionValues, // Replace with actual dimension value
			Filter:     model.GetShowMetricDataRequestFilterEnum().MAX,
			Period:     int32(1),
			From:       startTimestamp,
			To:         endTimestamp,
		}

		response, err := client.ShowMetricData(request)
		if err != nil {
			fmt.Printf("Error querying CES data for %s: %v\n", metricName, err)
			continue
		}

		if response.Datapoints == nil || len(*response.Datapoints) == 0 {
			fmt.Printf("No datapoints received for %s\n", metricName)
			continue
		}

		datapoints := *response.Datapoints
		var maxUsage float64
		for _, point := range datapoints {
			if point.Max != nil {
				if metricName == "upstream_bandwidth" || metricName == "downstream_bandwidth" {
					// Convert from bits to Mbits
					maxUsage = *point.Max / 1000000.0
				} else {
					maxUsage = *point.Max // for utilization metrics, which are percentages
				}
				break // Assuming there's only 1 datapoint with MAX filter
			}
		}

		if (metricName == "upstream_bandwidth" && maxUsage > upstreamBandwidthThreshold) ||
			(metricName == "downstream_bandwidth" && maxUsage > downstreamBandwidthThreshold) ||
			(metricName == "upstream_bandwidth_usage" && maxUsage > upstreamUsageThreshold) ||
			(metricName == "downstream_bandwidth_usage" && maxUsage > downstreamUsageThreshold) {

			alertMessage := createAlertMessage(metricName, maxUsage, endTime)
			if err := sendToFeishuWebhook(feishuWebhookURL, alertMessage); err != nil {
				fmt.Printf("Error sending to Feishu webhook: %v\n", err)
			}
		}
	}
}

func createAlertMessage(metricName string, usage float64, endTime time.Time) string {
	readableTime := endTime.Add(+8 * time.Hour).Format("2006-01-02 15:04:05")
	var alertMessage string

	// 注意阈值和单位已更新,具体文本格式根据实际需要调整
	switch metricName {
	case "upstream_bandwidth":
		alertMessage = fmt.Sprintf("警报:在%s,出网带宽超过了%.2fMbps,当前带宽:%.2fMbps", readableTime, upstreamBandwidthThreshold, usage)
	case "downstream_bandwidth":
		alertMessage = fmt.Sprintf("警报:在%s,入网带宽超过了%.2fMbps,当前带宽:%.2fMbps", readableTime, downstreamBandwidthThreshold, usage)
	case "upstream_bandwidth_usage":
		alertMessage = fmt.Sprintf("警报:在%s,出网带宽使用率超过了%.2f%%,当前使用率:%.2f%%", readableTime, upstreamUsageThreshold, usage)
	case "downstream_bandwidth_usage":
		alertMessage = fmt.Sprintf("警报:在%s,入网带宽使用率超过了%.2f%%,当前使用率:%.2f%%", readableTime, downstreamUsageThreshold, usage)
	}

	return alertMessage
}

func sendToFeishuWebhook(webhookURL string, message string) error {
	webhookMessage := FeishuWebhookMessage{
		MsgType: "text",
	}
	webhookMessage.Content.Text = message

	jsonData, err := json.Marshal(webhookMessage)
	if err != nil {
		return fmt.Errorf("failed to marshal webhook message: %v", err)
	}

	req, err := http.NewRequest("POST", webhookURL, bytes.NewBuffer(jsonData))
	if err != nil {
		return fmt.Errorf("failed to create HTTP request: %v", err)
	}
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return fmt.Errorf("failed to send HTTP request: %v", err)
	}
	defer resp.Body.Close()

	responseBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("failed to read webhook response body: %v", err)
	}

	fmt.Printf("Feishu webhook response: %s\n", responseBody)
	return nil
}

测试报警如下:
image.png

其他

  1. 遍历负载均衡列表,批量查询所有负载均衡连接数?发送告警时候传入负载均衡的名称?
  2. 根据负载均衡列表查询绑定的eip实例,查询所有eip对应bandwidth_id,输出所有eip的指标?

总结

此文为你展示了如何通过Go SDK获取华为云上的负载均衡最大连接数and eip指标的多个条件查询,并通过飞书Webhook发送通知的过程。以上的实现可以根据你自己的需求进行调整,比如改变监测的指标或者消息发送的方式。希望本文能帮助你更好地监控和管理华为云上的资源。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/295009.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

超维空间M1无人机使用说明书——31、基于模板匹配的物体识别功能

引言&#xff1a;ROS提供的物体识别功能包find_object_2d&#xff0c;该功能包用起来相对简单&#xff0c;只需要简单进行模板匹配即可。需要接显示器进行模板训练&#xff0c;远程比较卡&#xff0c;不建议 一、功能包find_object_2d简介 ROS的优点之一是有大量可以在应用程…

vivado 支持的XDC和SDC命令

支持的XDC和SDC命令 本附录讨论了支持的Xilinx设计约束&#xff08;XDC&#xff09;和Synopsys设计AMD Vivado中的约束&#xff08;SDC&#xff09;命令™ 集成设计环境&#xff08;IDE&#xff09;。 XDC文件中的有效命令 支持的SDC命令 注意&#xff1a;由于所有AMD Tcl命…

基于SSM的人事档案管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

【保研记录】2023年(24届)SE上岸经历

先开个坑&#xff0c;慢慢填~ 个人信息 学校&#xff1a;某双非 专业&#xff1a;软件工程 第四轮学科评估&#xff1a;无&#xff08;对就是没有等级&#xff09; 排名&#xff1a;1/400 竞赛/荣誉&#xff1a;国奖x2&#xff0c;省三好&#xff0c;大英国二&#xff0c;…

【uniapp】多规格选择

效果图 VUE <template> <view><view class"wp-80 pd-tb-40 mg-auto"><button type"warn" click"showDrawer(showRight)">筛选</button></view><!-- 筛选-uni-drawer --><uni-drawer ref"s…

为 validator 对象添加链式调用功能,并 return 校验后的值

目录 一、前置说明1、总体目录2、相关回顾3、本节目标 二、操作步骤1、项目目录2、代码实现3、测试代码4、日志输出 三、后置说明1、要点小结2、下节准备 一、前置说明 1、总体目录 《 pyparamvalidate 参数校验器&#xff0c;从编码到发布全过程》 2、相关回顾 使用 Raise…

JavaScript——BOM中所有对象的常用属性和方法【万字长篇超宝典】

目录 什么是BOM&#xff1f; BOM中的对象 一、window对象 1、控制台打印方法 2、弹窗相关方法 &#xff08;1&#xff09;、alert( )提示框 &#xff08;2&#xff09;、confrim( )交互框 &#xff08;3&#xff09;、prompt( )输入框 3、窗口打开关闭的方法 &#…

企业级实践为“燃料”,大模型助推Kyligence产品力向上

回顾2023年&#xff0c;最火热的科技话题无疑是生成式AI。 从ChatGPT横空出世&#xff0c;到“千模大战”如火如荼&#xff0c;AIGC正式破圈&#xff0c;成为企业数字化转型的新关键词。 在红杉中国《2023企业数字化年度指南》中&#xff0c;通过调研235家企业可知&#xff0…

所有单片机使用的汇编语言是统一的吗?

所有单片机使用的汇编语言是统一的吗&#xff1f; 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「单片机的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&…

基于多反应堆的高并发服务器【C/C++/Reactor】(中)主线程反应堆模型的事件添加和处理详解

>>服务器和客户端建立连接和通信流程&#xff1a; 基于多反应堆模型的服务器结构图&#xff0c;这主要是一个TcpServer&#xff0c;关于HttpServer,主要是用了Http协议&#xff0c;核心模块是TcpServer。这里边有两种线程&#xff1a;主线程和子线程。子线程是在线程池里…

windows 10 安装wsl ubuntu

1.首先管理员模式打卡powershell&#xff0c;执行 dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart 2.执行 wsl --update wsl --…

静态网页设计——崩坏3(HTML+CSS+JavaScript)

前言 声明&#xff1a;该文章只是做技术分享&#xff0c;若侵权请联系我删除。&#xff01;&#xff01; 感谢大佬的视频&#xff1a; 使用技术&#xff1a;HTMLCSSJS&#xff08;静态网页设计&#xff09; 主要内容&#xff1a;对游戏崩坏3进行简单介绍。 https://www.bilib…

Spring声明式事务业务bug

Spring 针对 Java Transaction API (JTA)、JDBC、Hibernate 和 Java Persistence API (JPA) 等事务 API&#xff0c;实现了一致的编程模型&#xff0c;而 Spring 的声明式事务功能更是提供了极其方便的事务配置方式&#xff0c;配合 Spring Boot 的自动配置&#xff0c;大多数 …

openGauss + Datakit

openGauss Datakit 1. 简介1.1 openGauss1.2 Datakit 2. 环境准备2.1 支持系统2.2 安装包获取2.3 注意事项2.4 系统环境设置 3. openGauss 安装3.1 创建用户和组3.2 创建工作目录3.3 关闭HISTORY记录/关闭交换内存3.4 解压安装包3.5 安装3.6 启动数据库3.7 连接数据库3.8 添加…

数据库设计——DQL

D Q L \huge{DQL} DQL ⭐⭐⭐⭐⭐ DQL&#xff1a;数据库查询语言&#xff0c;用来查询数据库中的记录&#xff0c;非常的重要&#xff0c;对于数据库的操作修改相对来讲还是较少部分&#xff0c;绝大多数操作都是数据查询。 整体的语法结构&#xff1a; 基本查询 示例&#…

【Java】LockSupport原理与使用

LockSupport&#xff1a; 关键字段&#xff1a; private static final sun.misc.Unsafe UNSAFE;private static final long parkBlockerOffset; Unsafe&#xff1a;"魔法类"&#xff0c;较为底层&#xff0c;在LockSupport类中用于线程调度(线程阻塞、线程恢复等)。…

【Unity】如何在Unity中使用C#的NuGet 包资源

【背景】 Unity的脚本语言是C#&#xff0c;而C#有很多功能和能力可以通过nuget包提供。有没有办法把这些能力结合到Unity中一起使用呢&#xff1f;如果可以&#xff0c;那将大大扩展Unity中各类功能实现的便捷性。 【方法】 答案是&#xff1a;你可以&#xff01; 获取Nuge…

经典八股文之RocketMQ

核心概念 NameServer nameserver是整个rocketmq的大脑&#xff0c;是rocketmq的注册中心。broker在启动时向所有nameserver注册。生产者在发送消息之前先从 NameServer 获取 Broker 服务器地址列表(消费者一 样)&#xff0c;然后根据负载均衡算法从列表中选择一台服务器进行消…

SSM在线手机品牌商城----计算机毕业设计

项目介绍 该项目为前后台项目&#xff0c;分为普通用户与管理员两种角色&#xff0c;前台普通用户登录&#xff0c;后台管理员登录&#xff1b; 管理员角色包含以下功能&#xff1a; 管理员登录,用户管理,品牌管理,子品牌管理,商品管理,订单管理,留言板管理等功能。 用户角…

Note: A Journey Across Canada

A Journey Across Canada 一场横穿加拿大的旅行 across journey After a quiz last autumn, Kuang crossed the continent eastward to Toronto to visit his schoolmate, the distance measuring approximately 5000 kilometers. 去年秋天一次考试后&#xff0c;Kuang向东穿…