通过gRPC来获取服务器CPU/内存使用率
前期准备
- 系统信息
admin@hpc-1:~$ cat /etc/issue
Ubuntu 20.04 LTS \n \l
admin@hpc-1:~$ go version
go version go1.20.3 linux/amd64
- 安装软件
admin@hpc-1:~$ sudo apt install protobuf-compiler -y
admin@hpc-1:~$ protoc --version
libprotoc 3.6.1
admin@hpc-1:~$
admin@hpc-1:~$ sudo apt install golang-goprotobuf-dev -y
admin@hpc-1:~$ protoc-gen-go --version
protoc-gen-go v1.33.0
- 创建文件夹并初始化项目
admin@hpc-1:~/go$ mkdir my_gRPC
admin@hpc-1:~/go$ cd my_gRPC
admin@hpc-1:~/go/my_gRPC$ go mod init my_gRPC
- 下载需要的包
admin@hpc-1:~/go/my_gRPC$ go get google.golang.org/grpc
admin@hpc-1:~/go/my_gRPC$ go get github.com/shirou/gopsutil/cpu
admin@hpc-1:~/go/my_gRPC$ go get github.com/shirou/gopsutil/mem
proto
- Protocol Buffers是google开源的一种结构数据序列化机制
- 编写proto文件
admin@hpc-1:~/go/my_gRPC$ cat proto/system_info.proto
syntax = "proto3";
package subscription;
option go_package ="../system_info"; //指定产生的go文件路径
service SubscriptionService {
rpc Subscribe(SubscriptionRequest) returns (stream SubscriptionResponse) {}
}
message SubscriptionRequest {
string sample_interval = 1; //设置取样间隔时间,单位为秒;这里的“1”和后面的"2"3"纯粹就是序列号
bool cpu_info = 2; //设置是否需要推送CPU信息
bool mem_info = 3; //设置是否需要推送内存信息
}
message SubscriptionResponse {
string message = 1;
}
admin@hpc-1:~/go/my_gRPC$
- 用protoc工具自动生成两个代码文件
admin@hpc-1:~/go/my_gRPC/proto$ protoc --go_out=. --go-grpc_out=. ./system_info.proto
admin@hpc-1:~/go/my_gRPC/proto$
admin@hpc-1:~/go/my_gRPC/proto$ ls -lt ../system_info/
total 16
-rw-rw-r-- 1 centec centec 4699 4-р сар 23 15:29 system_info_grpc.pb.go
-rw-rw-r-- 1 centec centec 8107 4-р сар 23 15:29 system_info.pb.go
- 确认一下system_info.pb.go文件中service的变量名,跟proto文件中写的是不一样的哦
type SubscriptionRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
SampleInterval string `protobuf:"bytes,1,opt,name=sample_interval,json=sampleInterval,proto3" json:"sample_interval,omitempty"`
CpuInfo bool `protobuf:"varint,2,opt,name=cpu_info,json=cpuInfo,proto3" json:"cpu_info,omitempty"`
MemInfo bool `protobuf:"varint,3,opt,name=mem_info,json=memInfo,proto3" json:"mem_info,omitempty"`
}
实现
- server源码
admin@hpc-1:~/go/my_gRPC$ cat server/server.go
package main
import (
"fmt"
"log"
"net"
"time"
"strconv"
"google.golang.org/grpc"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/mem"
pb "my_gRPC/system_info" //注意这里的路径,指向自动生成的system_info目录
)
type subscriptionServer struct {
pb.UnimplementedSubscriptionServiceServer
}
func get_cpu_util() (float64, error) {
usagePercent, err := cpu.Percent(time.Duration(1)*time.Second, false)
if err != nil {
fmt.Println("Error:", err)
return 0,err
}
return usagePercent[0],nil
}
func get_memory_util() (float64, error) {
memory, err := mem.VirtualMemory()
if err != nil {
fmt.Println("Error:", err)
return 0,err
}
usagePercent := memory.UsedPercent
return usagePercent,nil
}
func (s *subscriptionServer) Subscribe(req *pb.SubscriptionRequest, stream pb.SubscriptionService_SubscribeServer) error {
// 处理订阅请求,并通过stream发送订阅响应
fmt.Println("SampleInterval:", req.SampleInterval)
fmt.Println("CPU_Info:", req.CpuInfo)
fmt.Println("MemInfo:", req.MemInfo)
for {
msg := time.Now().Format("2006-01-02 15:04:05") + " : "
if req.CpuInfo { //添加判断,是否将CPU信息写入message
cu, err := get_cpu_util()
if err != nil {
fmt.Println("Get CPU util failure:", err)
return err
}
msg = msg + "CPU " + strconv.FormatFloat(cu, 'f', 2, 64) + "% "
}
if req.MemInfo { //添加判断,是否将内存信息写入message
mu, err := get_memory_util()
if err != nil {
fmt.Println("Get memory util failure:", err)
return err
}
msg = msg + "Memory " + strconv.FormatFloat(mu, 'f', 2, 64) + "%"
}
response := &pb.SubscriptionResponse{
Message: msg,
}
if err := stream.Send(response); err != nil {
return err
}
si, err := strconv.Atoi(req.SampleInterval) // 获取req中传来的取样间隔参数
if err != nil {
fmt.Println("Conversion failure:", err)
return err
}
time.Sleep(time.Duration(si) * time.Second) //等待指定的间隔时间,再进入下一次获取信息和发送信息的周期
}
return nil
}
func main() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterSubscriptionServiceServer(s, &subscriptionServer{})
fmt.Println("gRPC server is running and listen tcp port 50051...")
if err := s.Serve(lis); err != nil {
log.Fatalf("Failed to serve: %v", err)
}
}
admin@hpc-1:~/go/my_gRPC$
- client源码
admin@hpc-1:~/go/my_gRPC$ cat client/client.go
package main
import (
"context"
"fmt"
"log"
"flag"
"time"
"google.golang.org/grpc"
pb "my_gRPC/system_info"
)
func main() {
sample_interval := flag.String("i", "5", "指定信息推送间隔(单位:秒)")
cpuFlag := flag.Bool("cpu", false, "Receive CPU Info")
memFlag := flag.Bool("mem", false, "Receive Memory Info")
flag.Parse()
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
if err != nil {
log.Fatalf("Failed to connect: %v", err)
}
defer conn.Close()
client := pb.NewSubscriptionServiceClient(conn)
// 创建订阅请求,填入req参数
req := &pb.SubscriptionRequest{
SampleInterval : *sample_interval,
CpuInfo : *cpuFlag,
MemInfo : *memFlag,
}
// 发起订阅
stream, err := client.Subscribe(context.Background(), req)
if err != nil {
log.Fatalf("Failed to subscribe: %v", err)
}
// 处理接收到的订阅响应
for {
resp, err := stream.Recv()
if err != nil {
log.Fatalf("Failed to receive: %v", err)
}
// 处理订阅响应,从resp中提取数据
fmt.Println("Received response:", resp)
time.Sleep(time.Second) // 暂停一秒钟以模拟订阅的持续接收
}
}
admin@hpc-1:~/go/my_gRPC$
验证
- 启动server,保持一直运行
admin@hpc-1:~/go/my_gRPC/server$ go run server.go
gRPC server is running and listen tcp port 50051...
- 另开一个终端,启动client,指定取样间隔,只订阅CPU信息
admin@hpc-1:~/go/my_gRPC/client$ go run client.go -i 4 -cpu=true
Received response: message:"2024-04-23 15:39:00 : CPU 9.42% "
Received response: message:"2024-04-23 15:39:05 : CPU 0.94% "
Received response: message:"2024-04-23 15:39:10 : CPU 1.31% "
- 重新启动client,指定取样间隔,CPU/内存新都订阅
admin@hpc-1:~/go/my_gRPC/client$ go run client.go -i 6 -cpu=true -mem=true
Received response: message:"2024-04-23 15:40:48 : CPU 0.50% Memory 25.67%"
Received response: message:"2024-04-23 15:40:55 : CPU 1.38% Memory 25.67%"
Received response: message:"2024-04-23 15:41:02 : CPU 0.31% Memory 25.72%"