mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 12:30:44 +00:00
单次请求失败不再立刻判离线,改成连续 3 次
实测单个请求有 2~4% 的偶发失败(4G 丢包打在一条隧道连接上, 或者那条连接正在重连)。原逻辑是一失败就 online = false, 于是界面上会闪一下已断开 —— 用户点按钮时正好撞上那一闪, 看起来就像又连不上了。 而链路其实 1.5~15 秒就自己恢复(客户端本来就有退避重试)。 改成连续失败 3 次才判离线(手表至少 4.5 秒、电脑至少 6 秒): * 单次抖动完全不影响界面 * 真的断了仍然如实显示 这不是掩盖问题,而是把是否可用的判断从一次采样改成连续采样 —— 单点采样在有丢包的链路上噪声太大。 两边同步改: android .../RelayTransport.java failStreak + OFFLINE_AFTER windows/lanremote/relay.py _fail_streak + OFFLINE_AFTER 顺带记录 PC 端基准(真实用户视角,之前都是在设备上量的, 那等于把设备 4G 算了两遍): 公网可用率 98% 耗时 中位 911 P90 1889 P99 23868 ms 长轮询 5/6 耗时 60.8~62.1 秒 对比设备端(中位 1344 / P90 2563)—— 用户实际体验比设备端量出来的好。
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
r'''单次请求失败不再立刻判"离线",改成连续失败 3 次才算。
|
||||
|
||||
== 为什么 ==
|
||||
|
||||
实测单个请求会有 2~4% 的偶发失败(4G 丢包打在一条隧道连接上,
|
||||
或者那条连接正在重连)。原来的逻辑是**一失败就 online = false**,
|
||||
于是:
|
||||
|
||||
手表/电脑的界面上会闪一下"已断开"
|
||||
用户点按钮时正好撞上那一闪 -> 看起来像"又连不上了"
|
||||
|
||||
而实际上链路 1.5~15 秒后就自己恢复了(客户端本来就有退避重试)。
|
||||
|
||||
改成"连续失败 3 次才判离线",效果是:
|
||||
* 单次抖动完全不影响界面显示
|
||||
* 真的断了(连续 3 次,至少 4.5 秒都没通)仍然会如实显示离线
|
||||
|
||||
这不是掩盖问题,而是把"是否可用"的判断从"一次采样"改成
|
||||
"连续采样"—— 单点采样在有丢包的链路上噪声太大。
|
||||
|
||||
== 两边都要改 ==
|
||||
|
||||
1. android/src/com/lanremote/watch/RelayTransport.java (手表轮询)
|
||||
2. windows/lanremote/relay.py (电脑轮询)
|
||||
'''
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote")
|
||||
BOM = b"\xef\xbb\xbf"
|
||||
|
||||
# ---------------------------------------------------------------- 手表
|
||||
WATCH = ROOT / "android/src/com/lanremote/watch/RelayTransport.java"
|
||||
|
||||
WATCH_CONST_OLD = " private static final int RETRY_MAX_MS = 15000;"
|
||||
WATCH_CONST_NEW = """ private static final int RETRY_MAX_MS = 15000;
|
||||
/**
|
||||
* 连续失败几次才判定"离线"。
|
||||
*
|
||||
* <p>单次失败就报离线太敏感:4G 链路上随手就有 2~4% 的偶发失败,
|
||||
* 而链路通常一两秒就自己恢复,界面却已经闪了一下"已断开"。
|
||||
* 改成连续 3 次(至少 4.5 秒都不通)才判离线 ——
|
||||
* 真的断了仍然会如实显示。
|
||||
*/
|
||||
private static final int OFFLINE_AFTER = 3;"""
|
||||
|
||||
WATCH_FIELD_OLD = " private Thread pollThread;"
|
||||
WATCH_FIELD_NEW = """ private Thread pollThread;
|
||||
/** 连续失败次数(成功一次就清零)。 */
|
||||
private volatile int failStreak;"""
|
||||
|
||||
# 401/403 分支
|
||||
W_401_OLD = """ if (result.code == 401 || result.code == 403) {
|
||||
// 会话过期(中转重启等)—— 这种可以自动重连
|
||||
online = false;
|
||||
lastError = "会话已失效,正在重新接入";"""
|
||||
W_401_NEW = """ if (result.code == 401 || result.code == 403) {
|
||||
// 会话过期(中转重启等)—— 这种可以自动重连。
|
||||
// 会话失效是明确的状态,直接标离线。
|
||||
online = false;
|
||||
lastError = "会话已失效,正在重新接入";"""
|
||||
|
||||
# 非 200 分支
|
||||
W_OTHER_OLD = """ if (result.code != 200) {
|
||||
online = false;
|
||||
lastError = "轮询返回 " + result.code;
|
||||
sleep(backoff);
|
||||
backoff = Math.min(RETRY_MAX_MS, backoff * 2);
|
||||
continue;
|
||||
}
|
||||
online = true;
|
||||
lastError = "";
|
||||
backoff = RETRY_MIN_MS;"""
|
||||
W_OTHER_NEW = """ if (result.code != 200) {
|
||||
// 偶发的 502/504 不算断线,连续几次才算
|
||||
failStreak++;
|
||||
if (failStreak >= OFFLINE_AFTER) {
|
||||
online = false;
|
||||
}
|
||||
lastError = "轮询返回 " + result.code;
|
||||
sleep(backoff);
|
||||
backoff = Math.min(RETRY_MAX_MS, backoff * 2);
|
||||
continue;
|
||||
}
|
||||
online = true;
|
||||
failStreak = 0;
|
||||
lastError = "";
|
||||
backoff = RETRY_MIN_MS;"""
|
||||
|
||||
# 异常分支
|
||||
W_EXC_OLD = """ } catch (Exception exc) {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
online = false;
|
||||
lastError = "轮询出错:" + exc.getClass().getSimpleName();
|
||||
sleep(backoff);
|
||||
backoff = Math.min(RETRY_MAX_MS, backoff * 2);
|
||||
}"""
|
||||
W_EXC_NEW = """ } catch (Exception exc) {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
// 连接超时/重置很常见(隧道在重连),连续几次才算真断
|
||||
failStreak++;
|
||||
if (failStreak >= OFFLINE_AFTER) {
|
||||
online = false;
|
||||
}
|
||||
lastError = "轮询出错:" + exc.getClass().getSimpleName();
|
||||
sleep(backoff);
|
||||
backoff = Math.min(RETRY_MAX_MS, backoff * 2);
|
||||
}"""
|
||||
|
||||
# ---------------------------------------------------------------- 电脑
|
||||
AGENT = ROOT / "windows/lanremote/relay.py"
|
||||
|
||||
A_CONST_OLD = "RETRY_MAX = 20.0"
|
||||
A_CONST_NEW = """RETRY_MAX = 20.0
|
||||
# 连续失败几次才判定"离线"。
|
||||
# 单次失败就报离线太敏感:4G 链路上有 2~4% 的偶发失败,
|
||||
# 而链路通常一两秒就自己恢复,界面却已经闪了一下"已断开"。
|
||||
# 连续 3 次(至少 6 秒都不通)才判离线,真的断了仍然如实显示。
|
||||
OFFLINE_AFTER = 3"""
|
||||
|
||||
A_FAIL_OLD = """ except Exception as exc: # noqa: BLE001
|
||||
self.online = False
|
||||
self.last_error = f"{type(exc).__name__}: {exc}"
|
||||
self._log(f"中转连接出问题:{self.last_error}")"""
|
||||
A_FAIL_NEW = """ except Exception as exc: # noqa: BLE001
|
||||
# 偶发的连接超时/重置不算断线,连续几次才算
|
||||
self._fail_streak = getattr(self, "_fail_streak", 0) + 1
|
||||
if self._fail_streak >= OFFLINE_AFTER:
|
||||
self.online = False
|
||||
self.last_error = f"{type(exc).__name__}: {exc}"
|
||||
self._log(f"中转连接出问题(第 {self._fail_streak} 次):"
|
||||
f"{self.last_error}")"""
|
||||
|
||||
A_OK_OLD = """ self.online = True
|
||||
self.last_ok = time.time()
|
||||
self.last_error = \"\""""
|
||||
A_OK_NEW = """ self.online = True
|
||||
self._fail_streak = 0
|
||||
self.last_ok = time.time()
|
||||
self.last_error = \"\""""
|
||||
|
||||
|
||||
def patch(text: str, old: str, new: str, label: str) -> tuple:
|
||||
if old not in text:
|
||||
if new.strip() and new.splitlines()[-1].strip() in text:
|
||||
print(" • %s 已经是新值" % label)
|
||||
return text, True
|
||||
print(" ✗ 没找到:%s" % label)
|
||||
return text, False
|
||||
if text.count(old) != 1:
|
||||
print(" ! 出现 %d 次:%s" % (text.count(old), label))
|
||||
return text, False
|
||||
print(" ✓ %s" % label)
|
||||
return text.replace(old, new, 1), True
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ok_all = True
|
||||
|
||||
print(" 手表 RelayTransport.java:")
|
||||
raw = WATCH.read_bytes()
|
||||
text = raw.decode("utf-8-sig")
|
||||
for old, new, label in (
|
||||
(WATCH_CONST_OLD, WATCH_CONST_NEW, "加 OFFLINE_AFTER 常量"),
|
||||
(WATCH_FIELD_OLD, WATCH_FIELD_NEW, "加 failStreak 字段"),
|
||||
(W_OTHER_OLD, W_OTHER_NEW, "非 200 用连续计数"),
|
||||
(W_EXC_OLD, W_EXC_NEW, "异常用连续计数"),
|
||||
):
|
||||
text, ok = patch(text, old, new, label)
|
||||
ok_all = ok_all and ok
|
||||
|
||||
if ok_all:
|
||||
WATCH.write_text(text, encoding="utf-8", newline="\n")
|
||||
|
||||
print()
|
||||
print(" 电脑 relay.py:")
|
||||
araw = AGENT.read_bytes()
|
||||
atext = araw.decode("utf-8-sig")
|
||||
for old, new, label in (
|
||||
(A_CONST_OLD, A_CONST_NEW, "加 OFFLINE_AFTER 常量"),
|
||||
(A_FAIL_OLD, A_FAIL_NEW, "异常用连续计数"),
|
||||
(A_OK_OLD, A_OK_NEW, "成功时清零"),
|
||||
):
|
||||
atext, ok = patch(atext, old, new, label)
|
||||
ok_all = ok_all and ok
|
||||
|
||||
if not ok_all:
|
||||
print()
|
||||
print(" 有失败项,没写回电脑端")
|
||||
return 1
|
||||
|
||||
AGENT.write_text(atext, encoding="utf-8", newline="\n")
|
||||
print()
|
||||
print(" 两边都改好了")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user