使用go写的etcd转发

背景:因为公司中的机器比较老。我们的云上监控规定,使用的token的方式来监控,所以无法监控我们的etcd集群,我这里提供的一个etcd数据转发功能,能让监控到etcd集群

package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io"
	"net/http"
	"os"
	"sync"
	"time"
)

var (
	requestCount  int
	lastTimestamp time.Time
	mu            sync.Mutex
)

const (
	maxRequests   = 10
	resetDuration = time.Minute
)

func fetchEtcdMetrics(certPath, keyPath, caPath string) (string, error) {
	etcdEndpoint := "https://127.0.0.1:2379"
	monitorEndpoint := etcdEndpoint + "/metrics"

	// 读取证书、密钥和 CA 证书
	cert, err := tls.LoadX509KeyPair(certPath, keyPath)
	if err != nil {
		return "", fmt.Errorf("加载证书和密钥失败:%v", err)
	}

	caCert, err := os.ReadFile(caPath)
	if err != nil {
		return "", fmt.Errorf("读取 CA 证书失败:%v", err)
	}

	// 创建自定义的 tls.Config
	tlsConfig := &tls.Config{
		Certificates: []tls.Certificate{cert},
		RootCAs:      x509.NewCertPool(),
	}

	// 添加 CA 证书到 RootCAs
	if ok := tlsConfig.RootCAs.AppendCertsFromPEM(caCert); !ok {
		return "", fmt.Errorf("添加 CA 证书到 RootCAs 失败")
	}

	// 创建带有 TLS 配置的 HTTP 客户端
	client := &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: tlsConfig,
		},
	}

	// 创建 HTTP GET 请求
	req, err := http.NewRequest("GET", monitorEndpoint, nil)
	if err != nil {
		return "", fmt.Errorf("创建 HTTP 请求失败:%v", err)
	}

	// 发送 HTTP 请求并获取响应
	resp, err := client.Do(req)
	if err != nil {
		return "", fmt.Errorf("获取 etcd 指标失败:%v", err)
	}
	defer resp.Body.Close()

	// 检查响应状态码
	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("获取 etcd 指标失败。状态码:%d", resp.StatusCode)
	}

	// 读取响应体
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("读取响应体失败:%v", err)
	}

	return string(body), nil
}

func fetchEtcdMetricsHandler(w http.ResponseWriter, r *http.Request) {
	mu.Lock()
	defer mu.Unlock()

	now := time.Now()
	if now.Sub(lastTimestamp) > resetDuration {
		requestCount = 0
		lastTimestamp = now
	}

	if requestCount >= maxRequests {
		http.Error(w, "请求频率过高", http.StatusTooManyRequests)
		return
	}

	requestCount++

	certPath := os.Args[1]
	keyPath := os.Args[2]
	caPath := os.Args[3]

	metricsData, err := fetchEtcdMetrics(certPath, keyPath, caPath)
	if err != nil {
		http.Error(w, fmt.Sprintf("发生错误:%v", err), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "text/plain")
	fmt.Fprint(w, metricsData)
}

func main() {
	if len(os.Args) < 4 {
		fmt.Println("用法: go run your_script.go <cert_path> <key_path> <ca_path>")
		os.Exit(1)
	}

	http.HandleFunc("/fetch_etcd_metrics", fetchEtcdMetricsHandler)
	port := 5010

	fmt.Printf("服务器正在运行,地址:http://0.0.0.0:%d\n", port)
	err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
	if err != nil {
		fmt.Printf("启动服务器时发生错误:%v\n", err)
	}
}

 

posted @ 2024-03-05 13:33  百因必有果  阅读(7)  评论(0编辑  收藏  举报