通过一个例子演示gRPC服务的实现

通过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%"

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值