mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-12 22:31:00 +00:00
feat(xray): add tunnel health monitor (#5480)
* feat(xray): add tunnel health monitor * fix(tunnelmonitor): reuse netproxy client and init logger in tests Replace the duplicated newHTTPClient/dialContextWithProxy with netproxy.NewHTTPClient, which centralises the http/https/socks5 handling and avoids the dial-goroutine connection leak on context cancellation. Cap failures at the threshold during cooldown so the counter stays a true consecutive-failure count. Add TestMain to initialise the logger and fix the nil-pointer panic in the success-after-failure path. * fix(tunnelmonitor): observable recovery, signal headroom, and hardening Address the remaining review findings on the tunnel health monitor: - Recovery is now synchronous and observable: the callback calls server.RestartXray() directly and returns its error instead of just enqueuing SIGUSR1, so a failed restart no longer masks as success and arms the cooldown while the tunnel is still down. - Give the OS signal channel headroom (buffer 8) so producers cannot starve a SIGTERM/SIGINT out of the single slot. - Warn at startup when the monitor is enabled without a proxy, since the probe then measures host connectivity rather than the xray tunnel. - Cap failures at the threshold in the nil-recover branch too, matching the cooldown cap. - Document the XUI_TUNNEL_HEALTH_* vars in .env.example and the README. - Add tests for status-code classification, Normalize bounds, New proxy scheme errors, the recovery-error and nil-recover paths, the cooldown cap, and Run context cancellation (coverage 90%). --------- Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -18,6 +19,7 @@ import (
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/database"
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/logger"
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/sub"
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/tunnelmonitor"
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/util/crypto"
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/util/sys"
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/web"
|
||||
@@ -91,7 +93,7 @@ func runWebServer() {
|
||||
return
|
||||
}
|
||||
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
sigCh := make(chan os.Signal, 8)
|
||||
// Trap shutdown signals
|
||||
signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGTERM, sys.SIGUSR1, os.Interrupt)
|
||||
global.SetRestartHook(func() {
|
||||
@@ -100,6 +102,27 @@ func runWebServer() {
|
||||
default:
|
||||
}
|
||||
})
|
||||
|
||||
var stopTunnelHealthMonitor context.CancelFunc
|
||||
monitorCfg := tunnelmonitor.ConfigFromEnv()
|
||||
if monitorCfg.Enabled {
|
||||
if monitorCfg.ProxyURL == "" {
|
||||
logger.Warning("Tunnel health monitor enabled without XUI_TUNNEL_HEALTH_PROXY: the probe measures host connectivity, not the xray tunnel, so failures will restart xray without fixing host network issues")
|
||||
}
|
||||
|
||||
monitorCtx, cancel := context.WithCancel(context.Background())
|
||||
stopTunnelHealthMonitor = cancel
|
||||
|
||||
monitor, err := tunnelmonitor.New(monitorCfg, func(_ context.Context) error {
|
||||
logger.Warning("Tunnel health monitor threshold reached, restarting xray-core")
|
||||
return server.RestartXray()
|
||||
})
|
||||
if err != nil {
|
||||
logger.Warning("Tunnel health monitor disabled: ", err)
|
||||
} else {
|
||||
go monitor.Run(monitorCtx)
|
||||
}
|
||||
}
|
||||
for {
|
||||
sig := <-sigCh
|
||||
|
||||
@@ -142,6 +165,10 @@ func runWebServer() {
|
||||
}
|
||||
|
||||
default:
|
||||
if stopTunnelHealthMonitor != nil {
|
||||
stopTunnelHealthMonitor()
|
||||
}
|
||||
|
||||
// --- FIX FOR TELEGRAM BOT CONFLICT (409) on full shutdown ---
|
||||
tgbot.StopBot()
|
||||
// ------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user