golang监控内存交换区和CPU统计信息并通过UDP发送的插件库stats的使用

Golang监控内存交换区和CPU统计信息并通过UDP发送的插件库stats的使用

包简介

stats包允许收集关于Go应用程序及其运行系统的统计信息,并通过UDP将这些统计信息发送到服务器,您可以在服务器上对这些统计信息进行各种处理:显示、存储到数据库或发送到日志服务。

当前收集的Go相关信息

  • 垃圾回收次数
  • 最后一次垃圾回收
  • 最后一次垃圾回收暂停时间
  • 已分配内存
  • 堆内存分配
  • 堆系统内存分配
  • Go版本
  • Goroutine数量
  • HTTP请求日志(通过中间件实现时)

当前收集的系统信息

  • 主机信息(主机名、操作系统等)
  • CPU信息(类型、型号、核心数等)
  • 总CPU时间
  • 每核心CPU时间
  • 内存和交换区信息

安装

使用go get安装:

go get gopkg.in/go-playground/stats.v1

或更新:

go get -u gopkg.in/go-playground/stats.v1

然后在代码中导入:

import "gopkg.in/go-playground/stats.v1"

示例代码

服务器端示例

package main

import (
	"fmt"

	"gopkg.in/go-playground/stats.v1"
)

func main() {

	config := &stats.ServerConfig{
		Domain: "",
		Port:   3008,
		Debug:  false,
	}

	server, err := stats.NewServer(config)
	if err != nil {
		panic(err)
	}

	for stat := range server.Run() {

		// 计算CPU时间
		// totalCPUTimes := stat.CalculateTotalCPUTimes()
		// perCoreCPUTimes := stat.CalculateCPUTimes()

		// 对数据进行任意处理
		// * 保存到数据库
		// * 流式传输到其他地方
		// * 打印到控制台

		fmt.Println(stat)
	}
}

客户端示例

package main

import (
	"fmt"
	"net/http"
	"runtime"

	"gopkg.in/go-playground/stats.v1"
)

var statsClient *stats.ClientStats

func main() {

	serverConfig := &stats.ServerConfig{
		Domain: "remoteserver",
		Port:   3008,
		Debug:  false,
	}

	clientConfig := &stats.ClientConfig{
		Domain:           "",
		Port:             3009,
		PollInterval:     1000,
		Debug:            false,
		LogHostInfo:      true,
		LogCPUInfo:       true,
		LogTotalCPUTimes: true,
		LogPerCPUTimes:   true,
		LogMemory:        true,
		LogGoMemory:      true,
	}

	client, err := stats.NewClient(clientConfig, serverConfig)
	if err != nil {
		panic(err)
	}

	go client.Run()

	// 如果想在中间件中捕获HTTP请求
	// 需要访问客户端
	statsClient = client
}

// LoggingRecoveryHandler ...
//
// 捕获HTTP请求信息的中间件示例
// 注意:这是标准的Go中间件,但可以轻松适应其他类型/风格
func LoggingRecoveryHandler(next http.Handler) http.Handler {

	fn := func(w http.ResponseWriter, r *http.Request) {

		// 记录传入请求
		logReq := statsClient.NewHTTPRequest(w, r)

		defer func() {
			if err := recover(); err != nil {
				trace := make([]byte, 1<<16)
				n := runtime.Stack(trace, true)

				// 记录失败
				logReq.Failure(fmt.Sprintf("%s\n%s", err, trace[:n]))

				http.Error(w, "Friendly error message", 500)
				return
			}
		}()

		next.ServeHTTP(logReq.Writer(), r)

		// 记录完成
		logReq.Complete()
	}

	return http.HandlerFunc(fn)
}

许可证

  • MIT License, Copyright © 2015 Dean Karn
  • BSD License
    • Copyright © 2014, WAKAYAMA Shirou
    • Copyright © 2009 The Go Authors. All rights reserved.
  • BSD License, Copyright © 2010-2012 The w32 Authors. All rights reserved.

更多关于golang监控内存交换区和CPU统计信息并通过UDP发送的插件库stats的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang监控内存交换区和CPU统计信息并通过UDP发送的插件库stats的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang 监控内存、交换区和CPU统计信息并通过UDP发送的插件库

下面我将介绍如何使用Golang编写一个监控系统统计信息并通过UDP发送的插件库。这个库将监控内存使用情况、交换区使用情况和CPU统计信息。

核心代码实现

首先,我们创建一个名为stats的包,包含以下功能:

package stats

import (
	"encoding/json"
	"fmt"
	"net"
	"os"
	"runtime"
	"time"
)

// Stats 包含系统统计信息
type Stats struct {
	Timestamp    int64   `json:"timestamp"`
	Hostname     string  `json:"hostname"`
	MemoryUsed   uint64  `json:"memory_used"`   // 已用内存(字节)
	MemoryTotal  uint64  `json:"memory_total"`  // 总内存(字节)
	SwapUsed     uint64  `json:"swap_used"`     // 已用交换区(字节)
	SwapTotal    uint64  `json:"swap_total"`    // 总交换区(字节)
	CPUUser      float64 `json:"cpu_user"`      // 用户态CPU使用率
	CPUSystem    float64 `json:"cpu_system"`    // 内核态CPU使用率
	CPUIdle      float64 `json:"cpu_idle"`      // CPU空闲率
	NumGoroutine int     `json:"num_goroutine"` // Goroutine数量
}

// Collector 统计收集器
type Collector struct {
	udpAddr *net.UDPAddr
	conn    *net.UDPConn
}

// NewCollector 创建新的收集器
func NewCollector(udpServer string) (*Collector, error) {
	addr, err := net.ResolveUDPAddr("udp", udpServer)
	if err != nil {
		return nil, fmt.Errorf("failed to resolve UDP address: %v", err)
	}

	conn, err := net.DialUDP("udp", nil, addr)
	if err != nil {
		return nil, fmt.Errorf("failed to dial UDP: %v", err)
	}

	return &Collector{
		udpAddr: addr,
		conn:    conn,
	}, nil
}

// Close 关闭UDP连接
func (c *Collector) Close() error {
	if c.conn != nil {
		return c.conn.Close()
	}
	return nil
}

// Collect 收集系统统计信息
func (c *Collector) Collect() (*Stats, error) {
	hostname, err := os.Hostname()
	if err != nil {
		return nil, fmt.Errorf("failed to get hostname: %v", err)
	}

	// 获取内存信息
	memStats := &runtime.MemStats{}
	runtime.ReadMemStats(memStats)

	// 获取CPU使用率
	cpuUser, cpuSystem, cpuIdle := getCPUUsage()

	stats := &Stats{
		Timestamp:    time.Now().Unix(),
		Hostname:     hostname,
		MemoryUsed:   memStats.Alloc,
		MemoryTotal:  memStats.Sys,
		NumGoroutine: runtime.NumGoroutine(),
		CPUUser:      cpuUser,
		CPUSystem:    cpuSystem,
		CPUIdle:      cpuIdle,
	}

	// 获取交换区信息(仅Linux)
	if swapUsed, swapTotal, err := getSwapUsage(); err == nil {
		stats.SwapUsed = swapUsed
		stats.SwapTotal = swapTotal
	}

	return stats, nil
}

// Send 发送统计信息到UDP服务器
func (c *Collector) Send(stats *Stats) error {
	data, err := json.Marshal(stats)
	if err != nil {
		return fmt.Errorf("failed to marshal stats: %v", err)
	}

	_, err = c.conn.Write(data)
	if err != nil {
		return fmt.Errorf("failed to send UDP packet: %v", err)
	}

	return nil
}

// 以下为平台特定实现,这里简化为示例
func getCPUUsage() (float64, float64, float64) {
	// 实际实现中应该读取/proc/stat(Linux)或调用系统API
	// 这里返回示例值
	return 20.5, 10.2, 69.3
}

func getSwapUsage() (uint64, uint64, error) {
	// 实际实现中应该读取/proc/meminfo(Linux)或调用系统API
	// 这里返回示例值
	return 1024 * 1024 * 500, 1024 * 1024 * 2000, nil
}

使用示例

下面是如何使用这个stats库的示例:

package main

import (
	"log"
	"time"

	"./stats" // 替换为你的stats包路径
)

func main() {
	// 创建收集器,连接到UDP服务器
	collector, err := stats.NewCollector("127.0.0.1:8125")
	if err != nil {
		log.Fatalf("Failed to create collector: %v", err)
	}
	defer collector.Close()

	// 定时收集和发送统计信息
	ticker := time.NewTicker(5 * time.Second)
	defer ticker.Stop()

	for range ticker.C {
		// 收集统计信息
		stats, err := collector.Collect()
		if err != nil {
			log.Printf("Failed to collect stats: %v", err)
			continue
		}

		// 发送统计信息
		if err := collector.Send(stats); err != nil {
			log.Printf("Failed to send stats: %v", err)
			continue
		}

		log.Printf("Stats sent successfully: %+v", stats)
	}
}

功能扩展建议

  1. 平台兼容性:添加对不同操作系统(Linux, Windows, macOS)的支持
  2. 性能优化:使用连接池或批量发送减少UDP包数量
  3. 错误处理:添加重试机制和错误恢复
  4. 配置支持:从配置文件读取UDP服务器地址和采集间隔
  5. 数据压缩:对大体积数据使用压缩算法
  6. 安全传输:添加简单的加密或认证机制

注意事项

  1. UDP是不可靠协议,如果需要可靠传输,可以考虑TCP或添加确认机制
  2. 频繁采集可能会影响系统性能,需要合理设置采集间隔
  3. 跨平台实现需要考虑不同操作系统的API差异
  4. 生产环境应考虑添加日志记录和监控功能

这个实现提供了基本框架,你可以根据实际需求进行扩展和完善。

回到顶部