feat(telemetry): agent 心跳遥测上报+节点列表覆写(第13条)
agent 每 60s 心跳捎带遥测: agentVersion/xrayVersion(按 mtime 缓存重读)/ uptime/最近一次主控请求 RTT/cpu%/mem%/公网 v4+v6(公网优先, ULA/私网兜底); 主控心跳处理按指针语义只存上报字段, 旧 agent 无遥测行为不变; agent_nodes 加法迁移存遥测, 节点列表对 agent 行覆写真值, panelVersion 显示 "agent <版本>", NodeView 新增 ipv4/ipv6。
This commit is contained in:
+28
-4
@@ -14,6 +14,8 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/config"
|
||||
@@ -64,6 +66,15 @@ type Agent struct {
|
||||
xrayAPI xray.XrayAPI
|
||||
apiPort int
|
||||
baselineLoaded bool
|
||||
|
||||
// Heartbeat telemetry state (P13): last measured master-request RTT
|
||||
// and the mtime-keyed xray version cache.
|
||||
lastRTTms atomic.Int64
|
||||
hasRTT atomic.Bool
|
||||
xrayVerMu sync.Mutex
|
||||
xrayVer string
|
||||
xrayVerFor string // binary path the cached version was read from
|
||||
xrayVerMod time.Time
|
||||
}
|
||||
|
||||
// New creates a new agent instance
|
||||
@@ -110,7 +121,7 @@ func (a *Agent) Register() error {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+a.config.Token)
|
||||
|
||||
resp, err := a.httpClient.Do(req)
|
||||
resp, err := a.do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("register failed: %w", err)
|
||||
}
|
||||
@@ -241,12 +252,25 @@ func (a *Agent) Stop() {
|
||||
close(a.stopCh)
|
||||
}
|
||||
|
||||
// do performs one master request and records its round-trip time as the
|
||||
// node->master latency sample reported by the next heartbeat (P13).
|
||||
func (a *Agent) do(req *http.Request) (*http.Response, error) {
|
||||
start := time.Now()
|
||||
resp, err := a.httpClient.Do(req)
|
||||
if err == nil {
|
||||
a.lastRTTms.Store(time.Since(start).Milliseconds())
|
||||
a.hasRTT.Store(true)
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// sendHeartbeat sends a heartbeat to the master
|
||||
func (a *Agent) sendHeartbeat() error {
|
||||
url := fmt.Sprintf("%s/panel/api/agent/heartbeat", a.config.MasterURL)
|
||||
payload := map[string]interface{}{
|
||||
"token": a.config.Token,
|
||||
"timestamp": time.Now().Unix(),
|
||||
"telemetry": a.collectTelemetry(),
|
||||
}
|
||||
body, _ := json.Marshal(payload)
|
||||
req, err := http.NewRequest("POST", url, bytes.NewReader(body))
|
||||
@@ -256,7 +280,7 @@ func (a *Agent) sendHeartbeat() error {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+a.config.Token)
|
||||
|
||||
resp, err := a.httpClient.Do(req)
|
||||
resp, err := a.do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -276,7 +300,7 @@ func (a *Agent) fetchAndApplyConfig() error {
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+a.config.Token)
|
||||
|
||||
resp, err := a.httpClient.Do(req)
|
||||
resp, err := a.do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -509,7 +533,7 @@ func (a *Agent) postTrafficReport(report trafficReport) error {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+a.config.Token)
|
||||
|
||||
resp, err := a.httpClient.Do(req)
|
||||
resp, err := a.do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user