使用原生gopsutil库取代top实现Prometheus对系统和进程的监控

在Linux系统中,/proc 目录下的文件和文件夹代表了系统中的进程和硬件信息,包括CPU、内存、硬盘、网络等。之前的文章prometheus client_java实现进程的CPU、内存、IO、流量的可观测介绍了使用top命令来获取相关信息,本文使用原生的go库gopsutil来读取/proc目录的信息,实现系统和进程的性能监控

gopsutil是一个用于Go语言的库,旨在提供类似于Python的psutil库的功能,用于检索系统信息和进程管理。它支持多种操作系统,包括FreeBSD、Linux、Windows、macOS、OpenBSD和Solaris等‌。
github官方地址:https://github.com/shirou/gopsutil
API文档地址:https://pkg.go.dev/github.com/shirou/gopsutil

部分实现代码

import (
	"container/list"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/shirou/gopsutil/v4/cpu"
	"github.com/shirou/gopsutil/v4/load"
	"github.com/shirou/gopsutil/v4/mem"
	"github.com/shirou/gopsutil/v4/process"
	"math"
	"runtime"
	"strconv"
	"sync"
	"time"
)

// ......忽略掉部分代码

// 通过gopsutil库读取/proc/pid目录,获取系统和进程信息
func handleSystemInfo() (TopData, error) {
	var topData = TopData{ProcessList: list.New()}

    // 获取系统1分钟、5分钟、15分钟负载
	avg, uptimeError := load.Avg()
	if uptimeError == nil {
		topData.Uptime1 = avg.Load1
		topData.Uptime5 = avg.Load5
		topData.Uptime15 = avg.Load15
	}
    
    // 获取CPU使用率
	cpuUse, _ := cpu.Percent(0, false)
	topData.CupUsed = cpuUse[0]

    // 获取物理内存
	memInfo, _ := mem.VirtualMemory()
	topData.MemTotal = float64(memInfo.Total / 1024)
	topData.MemFree = float64(memInfo.Free / 1024)
	topData.MemUsed = float64(memInfo.Used / 1024)
	topData.MemBuff = float64(memInfo.Buffers / 1024)
    
    // 获取交换区内存
	topData.SwapTotal = float64(memInfo.SwapTotal / 1024)
	topData.SwapFree = float64(memInfo.SwapFree / 1024)
	topData.SwapUsed = float64((memInfo.SwapTotal - memInfo.SwapFree) / 1024)
	topData.SwapAvail = float64(memInfo.SwapCached / 1024)

	// 获取所有进程pid
	info, _ := process.Pids() //获取当前所有进程的pid
	
	// 计算各个进程状态的进程数量
	taskMap := map[string]int{
		process.Running: 0,
		process.Blocked: 0,
		process.Idle:    0,
		process.Lock:    0,
		process.Sleep:   0,
		process.Stop:    0,
		process.Wait:    0,
		process.Zombie:  0,
	}
	for _, pid := range info {
		p, _ := process.NewProcess(pid)
		cmd, cmderror := p.Cmdline()
		name, _ := p.Name()
		exe, _ := p.Exe()
	
		status, _ := p.Status()
		statusCount, exists := taskMap[status[0]]
		if exists {
			taskMap[status[0]] = statusCount + 1
		}
		if isSystemProcess(name, exe) == true {
			continue
		}
		proc := ProcessInfo{In: "n"}
		proc.Pid = strconv.Itoa(int(p.Pid))
		proc.User, _ = p.Username()
		// p.CPUPercent()  // 与top值不一样,
		// p.Percent(time.Second) // 统计的CPU使用率,与top值相近,但是有阻塞
		// CalculateCpuPercent自己实现CPU使用率的统计,与top值一致,参考p.Percent的实现
		cpuPercent, _ := CalculateCpuPercent(p)
		proc.Cpu = float64(cpuPercent)
		
		memoryPercent, _ := p.MemoryPercent()
		proc.Mem = math.Round(float64(memoryPercent)*100) / 100
		proc.Res = math.Round(float64(topData.MemTotal*proc.Mem)) / 100

		// 计算进程读写硬盘,基于p.IOCounters()数据统计
		readBytes, writeBytes, _ := CalculateDiskPercent(p)
		proc.ReadBytes = readBytes
		proc.WriteBytes = writeBytes

		topData.ProcessList.PushBack(proc)
	}

	topData.TaskRunning = taskMap[process.Running]
	topData.TaskBlocked = taskMap[process.Blocked]
	topData.TaskIdle = taskMap[process.Idle]
	topData.TaskLock = taskMap[process.Lock]
	topData.TaskSleeping = taskMap[process.Sleep]
	topData.TaskStopped = taskMap[process.Stop]
	topData.TaskWait = taskMap[process.Wait]
	topData.TaskZombie = taskMap[process.Zombie]
	for _, v := range taskMap {
		topData.TaskTotal += v
	}
	return topData, nil
}

通过gopsutil,已经能够获取到系统的CPU使用率、内存使用率、进程数量、进程状态,还有进程的CPU使用率、内存使用率、进程读写硬盘等数据。不需要调用top、iotop命令的功能。

获取正在监听端口的进程

import (
	"container/list"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/shirou/gopsutil/v4/net"
	"github.com/shirou/gopsutil/v4/process"
	"strconv"
	"syscall"
)

// 省略部分代码

func getListenPort() (*list.List, error) {
	var portList = list.New()
	conns, err := net.Connections("all")
	if err != nil {
		return nil, err
	}

	for _, conn := range conns {
		if conn.Status == "LISTEN" {
			pid := conn.Pid
			if pid == 0 {
				continue
			}
			p, _ := process.NewProcess(pid)
			cmdName, _ := p.Name()

			proc := ProcessInfo{In: "n",
				Pid: strconv.Itoa(int(p.Pid)),
			}
			cmd, _ := p.Cmdline()
			proc.Cmd = common.FilterAlias(cmd)
			proc.User, _ = p.Username()
			portInfo := PortInfo{
				Proto: getProto(conn.Family, conn.Type),
				Ip:    conn.Laddr.IP,
				Port:  uint16(conn.Laddr.Port),
				Pid:   strconv.Itoa(int(conn.Pid)),
				Cmd:   cmdName, 
				Proc:  proc,
			}
			portList.PushFront(portInfo)
		}
	}
	return portList, nil
}

func getProto(family uint32, sockType uint32) string {
	switch family {
	case syscall.AF_INET:
		if sockType == syscall.SOCK_STREAM {
			return "tcp"
		} else if sockType == syscall.SOCK_DGRAM {
			return "udp"
		}
	case syscall.AF_INET6:
		if sockType == syscall.SOCK_STREAM {
			return "tcp6"
		} else if sockType == syscall.SOCK_DGRAM {
			return "udp6"
		}
	}
	return ""
}

以上代码实现获取正在端口监控的进程的功能。不需要再调用netstat命令。
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

penngo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值