feat(mqtt): 实现MQTT客户端,支持系统资源监控和关机控制
添加新的MQTT客户端实现,具有系统资源使用情况监测和远程关机功能。客户端通过MQTT协议与服务器交换信息,订阅特定主题以接收控制命令,并发布系统资源使用情况。此功能允许在远程系统上执行关机操作,并监测CPU及内存使用情况。 - 实现PCInfo结构体以存储系统资源使用信息。 - 实现Command结构体以解析接收到的控制命令。 - 编写onMessageReceived处理函数以响应接收到的消息。 - 实现executeShutdown函数以处理不同操作系统的关机逻辑。 - 主函数中初始化MQTT客户端,加载环境变量,订阅主题,并循环发布系统资源信息。
This commit is contained in:
commit
6274a2bac0
|
@ -0,0 +1,3 @@
|
|||
MQTT_BROKER=tcp://127.0.0.1:1883
|
||||
MQTT_CLIENT_ID=local
|
||||
MQTT_TOPIC=pc/0
|
|
@ -0,0 +1,8 @@
|
|||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/pc_mqtt.iml" filepath="$PROJECT_DIR$/pc_mqtt.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,3 @@
|
|||
MQTT_BROKER=tcp://127.0.0.1:1883
|
||||
MQTT_CLIENT_ID=local
|
||||
MQTT_TOPIC=pc/0
|
Binary file not shown.
|
@ -0,0 +1,33 @@
|
|||
#!/bin/bash
|
||||
|
||||
# 确保脚本以root权限运行
|
||||
if [ "$(id -u)" -ne "0" ]; then
|
||||
echo "This script must be run as root." 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 设置变量
|
||||
SERVICE_FILE="/etc/systemd/system/mqtt-info-tool.service"
|
||||
BINARY_FILE="/opt/app/pc_mqtt/client"
|
||||
|
||||
# 确保Go程序可执行
|
||||
chmod +x $BINARY_FILE
|
||||
|
||||
# 安装systemd服务文件
|
||||
echo "Copying systemd service file to /etc/systemd/system..."
|
||||
cp ./mqtt-info-tool.service $SERVICE_FILE
|
||||
|
||||
# 重新加载systemd配置
|
||||
echo "Reloading systemd daemon..."
|
||||
systemctl daemon-reload
|
||||
|
||||
# 启动服务并设置为开机自启
|
||||
echo "Starting MQTT Info Tool service..."
|
||||
systemctl start mqtt-info-tool
|
||||
systemctl enable mqtt-info-tool
|
||||
|
||||
# 打印服务状态
|
||||
echo "Service status:"
|
||||
systemctl status mqtt-info-tool
|
||||
|
||||
echo "Installation completed successfully."
|
|
@ -0,0 +1,16 @@
|
|||
[Unit]
|
||||
Description=MQTT PC Information Tool
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/opt/app/pc_mqtt/client
|
||||
WorkingDirectory=/opt/app/pc_mqtt
|
||||
EnvironmentFile=/opt/app/pc_mqtt/.env
|
||||
Restart=always
|
||||
User=root
|
||||
Group=root
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/shirou/gopsutil/cpu"
|
||||
"github.com/shirou/gopsutil/mem"
|
||||
)
|
||||
|
||||
// PCInfo 定义 PC 使用信息的结构。
|
||||
type PCInfo struct {
|
||||
CPUUsage float64 `json:"cpu_usage"` // CPU
|
||||
MemUsage float64 `json:"mem_usage"` // Memory
|
||||
}
|
||||
|
||||
// Command 定义接收命令的结构。
|
||||
type Command struct {
|
||||
Action string `json:"action"` // Action 待执行
|
||||
}
|
||||
|
||||
// loadEnv 加载 .env 文件以读取环境变量。
|
||||
func loadEnv() {
|
||||
err := godotenv.Load(".env")
|
||||
if err != nil {
|
||||
fmt.Println("Error loading .env file")
|
||||
}
|
||||
}
|
||||
|
||||
// getPCInfo 检索当前 PC 使用信息。
|
||||
func getPCInfo() (PCInfo, error) {
|
||||
cpuPercentages, err := cpu.Percent(0, false)
|
||||
if err != nil {
|
||||
return PCInfo{}, err
|
||||
}
|
||||
|
||||
memInfo, err := mem.VirtualMemory()
|
||||
if err != nil {
|
||||
return PCInfo{}, err
|
||||
}
|
||||
|
||||
// 保留2位小数
|
||||
return PCInfo{
|
||||
CPUUsage: float64(int(cpuPercentages[0]*100)) / 100,
|
||||
MemUsage: float64(int(memInfo.UsedPercent*100)) / 100,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// onMessageReceived 处理从 MQTT 接收的消息。
|
||||
func onMessageReceived(client mqtt.Client, msg mqtt.Message) {
|
||||
var cmd Command
|
||||
err := json.Unmarshal(msg.Payload(), &cmd)
|
||||
if err != nil {
|
||||
fmt.Println("Error decoding JSON command:", err)
|
||||
return
|
||||
}
|
||||
|
||||
if cmd.Action == "shutdown" {
|
||||
fmt.Println("Shutdown command received. Shutting down...")
|
||||
executeShutdown()
|
||||
}
|
||||
}
|
||||
|
||||
// executeShutdown 根据操作系统执行关机命令。
|
||||
func executeShutdown() {
|
||||
var cmd *exec.Cmd
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
cmd = exec.Command("shutdown", "/s", "/t", "0")
|
||||
case "linux", "darwin":
|
||||
cmd = exec.Command("shutdown", "-h", "now")
|
||||
default:
|
||||
fmt.Println("Shutdown not supported on this OS")
|
||||
return
|
||||
}
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
fmt.Println("Failed to execute shutdown:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
loadEnv()
|
||||
|
||||
broker := os.Getenv("MQTT_BROKER")
|
||||
clientID := os.Getenv("MQTT_CLIENT_ID")
|
||||
topic := os.Getenv("MQTT_TOPIC")
|
||||
|
||||
opts := mqtt.NewClientOptions()
|
||||
opts.AddBroker(broker)
|
||||
opts.SetClientID(clientID)
|
||||
opts.SetDefaultPublishHandler(onMessageReceived)
|
||||
|
||||
client := mqtt.NewClient(opts)
|
||||
if token := client.Connect(); token.Wait() && token.Error() != nil {
|
||||
fmt.Println(token.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 订阅主题以接收命令
|
||||
client.Subscribe(topic, 0, nil)
|
||||
|
||||
for {
|
||||
pcInfo, err := getPCInfo()
|
||||
if err != nil {
|
||||
fmt.Println("Error getting PC info:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
pcInfoJSON, err := json.Marshal(pcInfo)
|
||||
if err != nil {
|
||||
fmt.Println("Error encoding PC info to JSON:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
token := client.Publish(topic, 0, false, pcInfoJSON)
|
||||
token.Wait()
|
||||
|
||||
fmt.Println("Message published:", string(pcInfoJSON))
|
||||
|
||||
time.Sleep(10 * time.Second) // 控制发送频率,例如每10秒发送一次
|
||||
}
|
||||
|
||||
// 完成后断开 MQTT 客户端连接
|
||||
// client.Disconnect(250)
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
module pc_mqtt
|
||||
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/eclipse/paho.mqtt.golang v1.5.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
||||
github.com/tklauser/numcpus v0.8.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
golang.org/x/net v0.27.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
)
|
|
@ -0,0 +1,31 @@
|
|||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/eclipse/paho.mqtt.golang v1.5.0 h1:EH+bUVJNgttidWFkLLVKaQPGmkTUfQQqjOsyvMGvD6o=
|
||||
github.com/eclipse/paho.mqtt.golang v1.5.0/go.mod h1:du/2qNQVqJf/Sqs4MEL77kR8QTqANF7XU7Fk0aOTAgk=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
|
||||
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
|
||||
github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
|
||||
github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
Loading…
Reference in New Issue