Files
ykxmz/_build/fix_watchdog_loop.sh
T
xmz7891 2b204cae07 修看门狗重启死循环(我上一版改过头了)
用户报 502 Bad Gateway,Cloudflare 的图显示
「Host: relay.xxmxxs.top - Error」,也就是源站/隧道这一跳挂了。

查 watchdog 日志,发现它在**每 50 秒重启一次隧道**:
    [10:31:57] 只剩 1 条连接 -> 重启隧道
    [10:32:22] 重启完
    [10:32:24] 又只剩 1 条 -> 距上次才 27 秒,先不重启
    [10:32:47] 距上次 50 秒 -> 又重启
    ... 循环不停

根因(我上一版的错):
  隧道在**质量差的 4G 上重建 4 条连接要 30~60 秒**。
  而我上一版把 MIN_RESTART_GAP 设成 45 秒,并且
  "公网不通 + 连接数<=1"就立刻重启 ——
  结果每次隧道还没把连接建齐,就被下一次重启打断,
  **永远停在"只剩 1 条"的状态**。
  等于我自己制造了不稳定。

修法:
  1. 重启后给 GRACE=150 秒宽限期,这期间绝不重启,
     让 cloudflared 专心把连接建齐
  2. MIN_RESTART_GAP 从 45 提到 150(和宽限期一致)
  3. "立刻重启"的判定加一条:距上次重启必须超过宽限期

实测(停掉 watchdog -> 手动重启隧道 -> 观察):
    10 秒内建齐 4 条连接,公网立刻 200
    宽限期生效后公网 20/20 = 100%,延迟 ~690ms

顺带一个教训:之前测出 80%、20% 的 000,
是我自己后台还跑着一个观察任务在并发打接口造成的干扰。
**测稳定性的时候,测量工具本身不能成为干扰源。**
2026-10-07 10:37:26 +08:00

111 lines
4.6 KiB
Bash

#!/bin/bash
# 修 watchdog 的重启死循环(这是我上一版改过头造成的)
#
# 现象(watchdog 日志):
# [10:31:57] 只剩 1 条连接 -> 重启隧道
# [10:32:22] 重启完
# [10:32:24] 又只剩 1 条 -> 距上次才 27 秒,先不重启
# [10:32:47] 距上次 50 秒 -> 又重启
# ... 每 50 秒一次,永远不消停
#
# 根因:
# 隧道在**质量差的 4G 上重建 4 条连接要 30~60 秒**。
# 而我上一版把 MIN_RESTART_GAP 设成 45 秒,
# 并且"公网不通 + 连接数<=1"就立刻重启 ——
# 结果每次隧道还没把连接建齐,就被下一次重启打断,
# **永远停在"只剩 1 条"的状态**。
#
# 等于我自己制造了不稳定。
#
# 修法:
# 1. 重启后给**宽限期**(GRACE=150 秒):这段时间内绝不重启,
# 让 cloudflared 专心把连接建齐。
# 2. MIN_RESTART_GAP 从 45 提到 150 秒(和宽限期一致)。
# 3. "立刻重启"的判定加一条:距上次重启必须超过宽限期。
# 确认故障还是立刻重启,但**不能在上次重启还没缓过来的时候**。
set -u
echo "════════ 1. 先停掉 watchdog,别一边改一边被它重启 ════════"
systemctl stop lanremote-watchdog.timer 2>&1 | sed 's/^/ /'
echo " timer: $(systemctl is-active lanremote-watchdog.timer)"
echo
echo "════════ 2. 让隧道安静地重建一次(别再打断它)════════"
systemctl restart lanremote-tunnel
echo " 已重启,等它把连接建齐…"
for i in $(seq 1 24); do
sleep 10
n=$(curl -s --max-time 4 http://127.0.0.1:20241/metrics 2>/dev/null \
| grep '^cloudflared_tunnel_ha_connections' | awk '{print $2}')
pub=$(curl -s -o /dev/null -w '%{http_code}' --max-time 8 \
-H 'User-Agent: LanRemote/1.4.0' https://relay.xxmxxs.top/v1/health 2>/dev/null)
echo " ${i}0s: 连接 ${n:-?} 公网 $pub"
[ "$n" = "4" ] && [ "$pub" = "200" ] && { echo " ✓ 已经建齐且公网通"; break; }
done
echo
echo "════════ 3. 写入修好的 watchdog ════════"
# 只在几处关键参数上打补丁,其余逻辑不动
python3 - <<'PY'
from pathlib import Path
path = Path("/root/lanremote-relay/watchdog.sh")
text = path.read_text(encoding="utf-8")
# 间隔 45 -> 150
text = text.replace(
"MIN_RESTART_GAP=45",
"# 两次重启至少隔这么久。\n"
"# 150 秒不是拍脑袋:隧道在差的 4G 上重建 4 条连接要 30~60 秒,\n"
"# 原来设 45 秒会导致**隧道还没建齐就被下一次重启打断**,\n"
"# 永远停在只剩 1 条连接的状态(实测死循环,每 50 秒重启一次)。\n"
"# 给足宽限期,让它有机会稳定下来。\n"
"MIN_RESTART_GAP=150\n"
"GRACE=150")
# "确认故障立刻重启" 加上宽限期判断
old = '''if [ "$DEGRADED" = "1" ]; then
restart_tunnel "公网不通且只剩 ${CONNS} 条隧道连接(确认故障,立即重启)"
fi'''
new = '''if [ "$DEGRADED" = "1" ]; then
# 宽限期内不重启 —— 让上一次重启有机会把连接建齐。
# (不加这条会死循环:隧道还没建齐就被再次打断。)
LAST_R=$(cat "$LAST_RESTART_FILE" 2>/dev/null || echo 0)
SINCE=$(( $(date +%s) - LAST_R ))
if [ "$SINCE" -lt "$GRACE" ]; then
log "公网不通且只剩 ${CONNS} 条连接,但距上次重启才 ${SINCE} 秒(宽限 ${GRACE} 秒)—— 先让它建完"
else
restart_tunnel "公网不通且只剩 ${CONNS} 条隧道连接(确认故障,立即重启)"
fi
fi'''
if old in text:
text = text.replace(old, new, 1)
print(" 已加宽限期判断")
else:
print(" [警告] 找不到'立即重启'那段,没改")
path.write_text(text, encoding="utf-8", newline="\n")
print(" 参数: MIN_RESTART_GAP=150 GRACE=150")
PY
echo
echo " 语法检查: $(bash -n /root/lanremote-relay/watchdog.sh 2>&1 && echo OK || echo 失败)"
grep -E '^MIN_RESTART_GAP|^GRACE|^THRESHOLD' /root/lanremote-relay/watchdog.sh | sed 's/^/ /'
echo
echo "════════ 4. 重新开 watchdog ════════"
systemctl start lanremote-watchdog.timer
echo " timer: $(systemctl is-active lanremote-watchdog.timer)"
echo
echo "════════ 5. 公网连测 15 次 ════════"
ok=0
for i in $(seq 1 15); do
c=$(curl -s -o /dev/null -w '%{http_code}' --max-time 15 \
-H 'User-Agent: LanRemote/1.4.0' https://relay.xxmxxs.top/)
[ "$c" = "200" ] && ok=$((ok+1))
printf " %2d: %s\n" "$i" "$c"
sleep 2
done
echo " 可用 $ok/15"