- 19 个接口全部实测验收通过 (2026-08-08) - 结构: main.go + core/consts + gsysctl + gtuner + server - 含 build_go.sh 官方部署结构构建脚本 (amd64/arm64 交叉编译) - 数据无缝衔接 Python 版遗留 (快照/state/审计日志) - 备份自 156:/root/goddhv (2026-08-09)
355 lines
11 KiB
Go
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 环境下可能引发问题"},
|
|
}
|
|
}
|