Files
ddhv/gtuner/tuner.go
shishi 2f79b22936 feat: 系统调优 Go 版后端完整源码
- 19 个接口全部实测验收通过 (2026-08-08)
- 结构: main.go + core/consts + gsysctl + gtuner + server
- 含 build_go.sh 官方部署结构构建脚本 (amd64/arm64 交叉编译)
- 数据无缝衔接 Python 版遗留 (快照/state/审计日志)
- 备份自 156:/root/goddhv (2026-08-09)
2026-08-09 10:12:57 +08:00

355 lines
11 KiB
Go

// Package gtuner 提供系统调优核心服务(检测/一键优化/场景预设/TCP加速/Swappiness)。
// 对应 Python 版 backend/app/services/tuner.py。
package gtuner
import (
"encoding/json"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
"github.com/user_TGPJp0KLpNsa5TAo/ddhv/core/consts"
"github.com/user_TGPJp0KLpNsa5TAo/ddhv/gsysctl"
)
// TunerService 系统调优服务
type TunerService struct {
S *gsysctl.Manager
}
// NewTuner 构造服务
func NewTuner() TunerService {
return TunerService{S: gsysctl.NewManager()}
}
// ========== 状态持久化 ==========
// statePath state.json 路径
func statePath() string {
return filepath.Join(consts.APP_DATA_PATH, "state.json")
}
// SaveState 保存应用状态(当前预设等)
func (t TunerService) SaveState(state map[string]any) {
path := statePath()
os.MkdirAll(filepath.Dir(path), 0o755)
if data, err := json.MarshalIndent(state, "", " "); err == nil {
os.WriteFile(path, data, 0o644)
}
}
// LoadState 读取应用状态
func (t TunerService) LoadState() map[string]any {
path := statePath()
if data, err := os.ReadFile(path); err == nil {
var m map[string]any
if json.Unmarshal(data, &m) == nil {
return m
}
}
return map[string]any{}
}
// GetCurrentPreset 获取当前生效的预设方案
func (t TunerService) GetCurrentPreset() (map[string]any, error) {
state := t.LoadState()
key := ""
if v, ok := state["current_preset"].(string); ok {
key = v
}
presets := t.GetPresets()
if key != "" {
if p, ok := presets[key].(map[string]any); ok {
name, _ := p["name"].(string)
appliedAt := ""
if v, ok := state["preset_applied_at"].(string); ok {
appliedAt = v
}
return map[string]any{"current": key, "name": name, "applied_at": appliedAt}, nil
}
}
return map[string]any{"current": nil, "name": nil, "applied_at": ""}, nil
}
// ========== 系统检测 ==========
// CheckRoot 检查 root 权限
func (t TunerService) CheckRoot() (map[string]any, error) {
isRoot := os.Geteuid() == 0
return map[string]any{
"is_root": isRoot, "proc_writable": isRoot, "ok": isRoot,
}, nil
}
// DetectHardware 检测硬件环境
func (t TunerService) DetectHardware() (map[string]any, error) {
info := map[string]any{
"os": runtime.GOOS + " linux", "arch": runtime.GOARCH,
"cpu_cores": runtime.NumCPU(), "memory_gb": 0.0,
"disk_type": "unknown", "is_container": false, "virtualization": "none",
}
// 内存
if data, err := os.ReadFile("/proc/meminfo"); err == nil {
for _, line := range strings.Split(string(data), "\n") {
if strings.HasPrefix(line, "MemTotal:") {
fields := strings.Fields(line)
if len(fields) >= 2 {
if kb, err := strconv.ParseInt(fields[1], 10, 64); err == nil {
gb := float64(kb) / 1024 / 1024
info["memory_gb"] = float64(int(gb*10+0.5)) / 10
}
}
break
}
}
}
// 磁盘类型
for _, dev := range []string{"sda", "vda"} {
if data, err := os.ReadFile("/sys/block/" + dev + "/queue/rotational"); err == nil {
if strings.TrimSpace(string(data)) == "1" {
info["disk_type"] = "hdd"
} else {
info["disk_type"] = "ssd"
}
break
}
}
// 容器检测
if data, err := os.ReadFile("/proc/1/cgroup"); err == nil {
c := string(data)
if strings.Contains(c, "docker") || strings.Contains(c, "kubepods") || strings.Contains(c, "lxc") {
info["is_container"] = true
info["virtualization"] = "container"
}
}
if _, err := os.Stat("/.dockerenv"); err == nil {
info["is_container"] = true
info["virtualization"] = "docker"
}
return info, nil
}
// DetectSwap 检测 swap
func (t TunerService) DetectSwap() (map[string]any, error) {
enabled := false
if data, err := os.ReadFile("/proc/swaps"); err == nil {
for _, line := range strings.Split(string(data), "\n")[1:] {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
if !strings.HasPrefix(line, "Filename") && len(strings.Fields(line)) > 0 {
enabled = true
break
}
}
}
return map[string]any{"swap_enabled": enabled}, nil
}
// DetectTCP 检测 TCP 配置
func (t TunerService) DetectTCP() (map[string]any, error) {
keys := []string{
"net.ipv4.tcp_congestion_control", "net.core.default_qdisc",
"net.ipv4.tcp_fastopen", "net.ipv4.tcp_syncookies",
"net.ipv4.tcp_rmem", "net.ipv4.tcp_wmem",
"net.core.rmem_max", "net.core.wmem_max",
"net.core.somaxconn", "net.ipv4.tcp_max_syn_backlog",
"net.ipv4.tcp_tw_reuse", "net.ipv4.tcp_slow_start_after_idle",
"net.ipv4.tcp_mtu_probing", "net.ipv4.ip_local_port_range",
}
values, _ := gsysctl.GetValues(keys)
// BBR 支持检测
result := map[string]any{}
for k, v := range values {
result[k] = v
}
bbrAvailable := false
if out, err := gsysctl.GetValue("net.ipv4.tcp_available_congestion_control"); err == nil {
if strings.Contains(strings.ToLower(out), "bbr") {
bbrAvailable = true
}
}
result["bbr_available"] = bbrAvailable
return result, nil
}
// Detect 综合检测
func (t TunerService) Detect() (map[string]any, error) {
hw, _ := t.DetectHardware()
tcp, _ := t.DetectTCP()
swap, _ := t.DetectSwap()
preset, _ := t.GetCurrentPreset()
root, _ := t.CheckRoot()
init, _ := t.InitStatus()
return map[string]any{
"hardware": hw, "tcp": tcp, "swap": swap, "preset": preset,
"root": root, "init": init,
}, nil
}
// ========== 场景预设 ==========
// GetPresets 返回可用的调优场景预设
func (t TunerService) GetPresets() map[string]any {
return map[string]any{
"balanced": map[string]any{
"name": "均衡模式",
"desc": "适合大多数服务器,平衡性能与稳定性",
"params": map[string]string{
"vm.swappiness": "10",
"net.core.default_qdisc": "fq",
"net.ipv4.tcp_congestion_control": "bbr",
"net.ipv4.tcp_fastopen": "3",
"net.ipv4.tcp_slow_start_after_idle": "0",
"net.ipv4.tcp_mtu_probing": "1",
"net.core.somaxconn": "1024",
"net.ipv4.tcp_max_syn_backlog": "4096",
},
},
"performance": map[string]any{
"name": "高性能模式",
"desc": "追求极限吞吐,适合高并发业务(内存≥4G推荐)",
"params": map[string]string{
"vm.swappiness": "5",
"vm.vfs_cache_pressure": "50",
"net.core.default_qdisc": "fq",
"net.ipv4.tcp_congestion_control": "bbr",
"net.ipv4.tcp_fastopen": "3",
"net.ipv4.tcp_slow_start_after_idle": "0",
"net.ipv4.tcp_mtu_probing": "1",
"net.core.somaxconn": "4096",
"net.ipv4.tcp_max_syn_backlog": "8192",
"net.core.rmem_max": "16777216",
"net.core.wmem_max": "16777216",
"net.ipv4.tcp_rmem": "4096 87380 16777216",
"net.ipv4.tcp_wmem": "4096 16384 16777216",
"net.ipv4.tcp_tw_reuse": "1",
},
},
"conservative": map[string]any{
"name": "保守模式",
"desc": "最小改动,仅调整最安全的参数",
"params": map[string]string{
"vm.swappiness": "10",
"net.ipv4.tcp_slow_start_after_idle": "0",
"net.ipv4.tcp_mtu_probing": "1",
},
},
}
}
// ApplyPreset 应用场景预设
func (t TunerService) ApplyPreset(key string) (map[string]any, error) {
presets := t.GetPresets()
p, ok := presets[key].(map[string]any)
if !ok {
return nil, gsysctl.NewErrSysctl("未知的预设场景: " + key)
}
name, _ := p["name"].(string)
params, _ := p["params"].(map[string]string)
results := gsysctl.SetValues(params, true)
okCount, failCount := 0, 0
for _, r := range results {
if m, ok := r.(map[string]any); ok {
if v, ok := m["ok"].(bool); ok && v {
okCount++
} else {
failCount++
}
}
}
t.SaveState(map[string]any{
"current_preset": key, "preset_applied_at": time.Now().Format("2006-01-02 15:04:05"),
})
t.S.LogAction("apply_preset", map[string]any{
"preset": key, "preset_name": name, "ok": okCount, "fail": failCount,
})
return map[string]any{
"preset": key, "preset_name": name, "results": results,
"ok_count": okCount, "fail_count": failCount,
}, nil
}
// InitStatus 检查是否已初始化
func (t TunerService) InitStatus() (map[string]any, error) {
snaps := t.S.ListSnapshots()
for _, s := range snaps {
if s.Kind == "init" {
return map[string]any{"initialized": true, "snapshot": s}, nil
}
}
return map[string]any{"initialized": false, "snapshot": nil}, nil
}
// InitSnapshot 首次使用初始化快照
func (t TunerService) InitSnapshot() (map[string]any, error) {
snaps := t.S.ListSnapshots()
for _, s := range snaps {
if s.Kind == "init" {
return map[string]any{"already": true, "snapshot": s}, nil
}
}
meta, err := t.S.CreateSnapshot("首次使用初始化", "init")
if err != nil {
return nil, err
}
t.S.LogAction("init", map[string]any{"snapshot": meta.Name, "param_count": meta.ParamCount})
return map[string]any{"already": false, "snapshot": meta}, nil
}
// ========== 单独调优项 ==========
// SetSwappiness 设置 swappiness
func (t TunerService) SetSwappiness(value int, persist bool) (map[string]any, error) {
if value < 0 || value > 100 {
return nil, gsysctl.NewErrSysctl("swappiness 必须在 0-100 之间")
}
err := gsysctl.SetValue("vm.swappiness", strconv.Itoa(value), persist)
if err != nil {
return nil, err
}
current, _ := gsysctl.GetValue("vm.swappiness")
t.S.LogAction("set_swappiness", map[string]any{"value": value, "persist": persist})
return map[string]any{"ok": true, "value": value, "current": current}, nil
}
// EnableBBR 一键开启 BBR
func (t TunerService) EnableBBR() (map[string]any, error) {
results := gsysctl.SetValues(map[string]string{
"net.core.default_qdisc": "fq",
"net.ipv4.tcp_congestion_control": "bbr",
}, true)
current, _ := gsysctl.GetValue("net.ipv4.tcp_congestion_control")
t.S.LogAction("enable_bbr", map[string]any{"current": current})
return map[string]any{"results": results, "current": current}, nil
}
// DisableBBR 关闭 BBR
func (t TunerService) DisableBBR() (map[string]any, error) {
results := gsysctl.SetValues(map[string]string{
"net.ipv4.tcp_congestion_control": "cubic",
}, true)
current, _ := gsysctl.GetValue("net.ipv4.tcp_congestion_control")
t.S.LogAction("disable_bbr", map[string]any{"current": current})
return map[string]any{"results": results, "current": current}, nil
}
// ========== 危险参数解锁 ==========
// GetDangerousParams 危险参数清单
func (t TunerService) GetDangerousParams() map[string]any {
return map[string]any{
"vm.swappiness": map[string]any{"risk": "danger", "desc": "内存交换策略,过低可能导致内存压力,过高会导致频繁交换"},
"vm.vfs_cache_pressure": map[string]any{"risk": "danger", "desc": "内核缓存回收倾向,过高可能导致缓存频繁失效"},
"net.core.rmem_max": map[string]any{"risk": "danger", "desc": "最大接收缓冲区,过大可能浪费内存"},
"net.core.wmem_max": map[string]any{"risk": "danger", "desc": "最大发送缓冲区,过大可能浪费内存"},
"net.ipv4.tcp_rmem": map[string]any{"risk": "danger", "desc": "TCP 接收缓冲动态范围,不当设置影响吞吐"},
"net.ipv4.tcp_wmem": map[string]any{"risk": "danger", "desc": "TCP 发送缓冲动态范围,不当设置影响吞吐"},
"net.ipv4.tcp_tw_reuse": map[string]any{"risk": "danger", "desc": "TIME_WAIT 复用,NAT 环境下可能引发问题"},
}
}