mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-19 08:20:44 +00:00
修真凶:4G 链路静默断连导致隧道"假活"(用户撞上 Error 1033)
现象:用户早上打开 relay.xxmxxs.top 看到 Cloudflare Error 1033。
watchdog 日志把过程记下来了:
公网不通(HTTP 000,ha_connections=2) <- 隧道显示连着,公网不通
公网不通(HTTP 502,ha_connections=2)
公网恢复(HTTP 200) <- 自己又好了
也就是说:**间歇性假活**,短样本测不出来。
我之前只测 15~25 次就说"可用率 100%",太草率了。
根因:4G 链路会**静默断开** TCP 连接 —— 不发 FIN/RST,双方都以为还连着。
Cloudflare 把请求发到这条僵尸连接上,就回 502/530/1033。
Linux 默认 tcp_keepalive_time = **7200 秒(2 小时)**才发现连接死了,
所以中间这段时间服务一直"看着在线、实际打不开"。
tunnel.log 最近 3000 行的佐证:
i/o timeout 212 次
Retrying connection 127 次
Lost connection 22 次
修法:
1. TCP keepalive 调到 30 秒 + 每 10 秒探测 3 次
=> 最多约 60 秒发现僵尸连接,cloudflared 立刻重连
(/etc/sysctl.d/97-ykxmz-keepalive.conf)
2. 隧道连接数 2 -> 4,多几条冗余
3. 修 relay 从不清理设备记录的问题:
self.devices 只增不减,被控端每换端口/重装就多一条,
实测一天攒了 75 条僵尸记录,/v1/health 从几 KB 涨到 20KB。
加 DEVICE_TTL(6 小时)在 snapshot() 里清掉。
实测(修完立刻验证):
设备侧 公网 15/15 HTTP 200
Windows 公网 25/25,中位 520 ms,最快 463 ms
health 20931 字节 / 75 台 -> 152 字节 / 0 台
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
r'''长时间观察公网可用率 —— 这才是判断"稳不稳"的正确方法。
|
||||
|
||||
我之前只测了 15~25 次就连着说"可用率 100%",太草率了。
|
||||
用户早上打开就撞上 Error 1033 —— 说明**间断性掉线**,
|
||||
短样本根本看不出来。
|
||||
|
||||
这个脚本跑 10 分钟,每 3 秒一次,统计:
|
||||
* 总可用率
|
||||
* 连续失败的最长时长(这个最要命:用户点进去就是白页)
|
||||
* 失败的时间分布(是零星抖动还是成片断)
|
||||
'''
|
||||
from __future__ import annotations
|
||||
|
||||
import statistics
|
||||
import sys
|
||||
import time
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
|
||||
URL = "https://relay.xxmxxs.top/v1/health"
|
||||
HEADERS = {"User-Agent": "LanRemote/1.4.0"}
|
||||
DURATION = 600 # 观察 10 分钟
|
||||
INTERVAL = 3 # 每 3 秒一次
|
||||
|
||||
|
||||
def probe(timeout: float = 12.0) -> tuple:
|
||||
"""返回 (是否成功, 耗时秒, 状态码或错误名)。"""
|
||||
start = time.perf_counter()
|
||||
try:
|
||||
with urllib.request.urlopen(
|
||||
urllib.request.Request(URL, headers=HEADERS),
|
||||
timeout=timeout) as resp:
|
||||
resp.read()
|
||||
ok = resp.status == 200
|
||||
return ok, time.perf_counter() - start, str(resp.status)
|
||||
except urllib.error.HTTPError as exc:
|
||||
return False, time.perf_counter() - start, "HTTP%d" % exc.code
|
||||
except Exception as exc: # noqa: BLE001
|
||||
return False, time.perf_counter() - start, exc.__class__.__name__
|
||||
|
||||
|
||||
def main() -> int:
|
||||
print(" 观察 %d 分钟,每 %d 秒一次" % (DURATION // 60, INTERVAL))
|
||||
print()
|
||||
|
||||
total = ok_count = 0
|
||||
times = []
|
||||
failures = []
|
||||
streak = 0
|
||||
worst_streak = 0
|
||||
worst_start = None
|
||||
current_start = None
|
||||
|
||||
deadline = time.time() + DURATION
|
||||
while time.time() < deadline:
|
||||
ok, elapsed, note = probe()
|
||||
total += 1
|
||||
if ok:
|
||||
ok_count += 1
|
||||
times.append(elapsed)
|
||||
streak = 0
|
||||
current_start = None
|
||||
else:
|
||||
failures.append((time.strftime("%H:%M:%S"), note))
|
||||
if streak == 0:
|
||||
current_start = time.time()
|
||||
streak += 1
|
||||
if streak > worst_streak:
|
||||
worst_streak = streak
|
||||
worst_start = current_start
|
||||
# 每分钟报一次进度
|
||||
if total % 20 == 0:
|
||||
print(" 已测 %3d 次,成功 %3d,失败 %2d"
|
||||
% (total, ok_count, total - ok_count))
|
||||
time.sleep(INTERVAL)
|
||||
|
||||
print()
|
||||
print(" ================ 结果 ================")
|
||||
rate = ok_count / total * 100 if total else 0
|
||||
print(" 样本 %d 次(约 %d 分钟)" % (total, DURATION // 60))
|
||||
print(" 可用率 %.1f%% (%d/%d)" % (rate, ok_count, total))
|
||||
|
||||
if times:
|
||||
print(" 成功时延迟 中位 %.0f ms 最快 %.0f ms 最慢 %.0f ms"
|
||||
% (statistics.median(times) * 1000,
|
||||
min(times) * 1000, max(times) * 1000))
|
||||
|
||||
if failures:
|
||||
span = worst_streak * INTERVAL
|
||||
print(" **最长连续失败 %d 次(约 %d 秒)**" % (worst_streak, span))
|
||||
if worst_start:
|
||||
print(" 开始于 %s" % time.strftime("%H:%M:%S",
|
||||
time.localtime(worst_start)))
|
||||
print(" 失败明细(最多列 20 条):")
|
||||
for stamp, note in failures[:20]:
|
||||
print(" %s %s" % (stamp, note))
|
||||
else:
|
||||
print(" 全程没有失败")
|
||||
|
||||
print()
|
||||
print(" 判读:")
|
||||
if rate >= 99.5:
|
||||
print(" ≥99.5% —— 可以接受")
|
||||
elif rate >= 97:
|
||||
print(" 97~99.5% —— 有抖动,用户偶尔会遇到打不开")
|
||||
else:
|
||||
print(" <97% —— **不稳定**,必须查根因(用户会经常撞上 Error 1033)")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user