# -*- coding: utf-8 -*- r'''塌陷窗口分析器:专门看"连接总数 <= 3"的那些窗口里发生了什么。 == 为什么需要专门看窗口 == 总体中位数两次都会被稳态主导(都是 61 秒 / 1.2 秒),区分不出 A/B 好坏。 真正的差别只在 QUIC 塌陷的那几分钟里体现: A 组 quic8+h2 2:塌陷时只剩 2 条在扛 B 组 quic6+h2 4:塌陷时还有 4 条 所以核心指标是: * 「连接总数 <= 3」的采样占比(= 风险暴露时间) * 这些窗口里公网可用率 用法: python _build/rp_collapse.py <标签> ''' from __future__ import annotations import sys from datetime import datetime from pathlib import Path # 塌陷判定:总连接数 <= 这个值就算"容量很薄" COLLAPSE_TOTAL = 3 def parse_ts(path: str, cols: int): rows = [] for line in Path(path).read_text(encoding="utf-8", errors="replace").splitlines(): parts = line.split("\t") if len(parts) < cols: continue try: t = datetime.strptime(parts[0].strip(), "%Y-%m-%d %H:%M:%S") except ValueError: continue rows.append((t, parts)) return rows def main() -> int: if len(sys.argv) < 4: print("用法: rp_collapse.py <标签> ") return 2 label, conns_path, health_path = sys.argv[1], sys.argv[2], sys.argv[3] conns = parse_ts(conns_path, 3) health = parse_ts(health_path, 3) if not conns: print(" %s: 连接采样为空" % label) return 1 # ---- 连接数统计 ---- samples = [] for t, p in conns: try: q = int(p[1]) except ValueError: q = None try: h = int(p[2]) except ValueError: h = None samples.append((t, q, h)) valid = [(t, q, h) for t, q, h in samples if q is not None and h is not None] if not valid: print(" %s: 没有有效的连接采样" % label) return 1 span = (valid[-1][0] - valid[0][0]).total_seconds() total_list = [q + h for _t, q, h in valid] collapse = [(t, q, h) for t, q, h in valid if q + h <= COLLAPSE_TOTAL] collapse_secs = len(collapse) * 60.0 # 每 60 秒一个采样 collapse_pct = len(collapse) * 100.0 / len(valid) print("=" * 62) print(" %s" % label) print(" 采样窗口 %.0f 分钟(%d 个连接采样)" % (span / 60, len(valid))) print("=" * 62) print() print(" 连接数分布:") from collections import Counter cnt = Counter(total_list) for k in sorted(cnt, reverse=True): bar = "#" * min(cnt[k], 50) print(" 总数 %2d : %3d 次 %s" % (k, cnt[k], bar)) print() print(" 平均连接数: %.2f" % (sum(total_list) / len(total_list))) print() # ---- 塌陷窗口 ---- print(" ── 塌陷窗口(连接总数 <= %d)──" % COLLAPSE_TOTAL) print(" 出现 %d 次采样 = 约 %.0f 秒 = 占窗口的 %.1f%%" % (len(collapse), collapse_secs, collapse_pct)) if collapse: print(" 明细:") for t, q, h in collapse: print(" %s quic=%s http2=%s 合计=%d" % (t.strftime("%H:%M:%S"), q, h, q + h)) # ---- 塌陷窗口内的公网可用率 ---- print() print(" ── 塌陷窗口内的公网可用率 ──") if not collapse or not health: print(" (窗口内没有健康样本)") else: # 把塌陷时刻扩展成 ±60 秒的窗口,再取落在里面的健康样本 windows = [(t.timestamp() - 60, t.timestamp() + 60) for t, _q, _h in collapse] inside = [] for t, p in health: ts = t.timestamp() if any(a <= ts <= b for a, b in windows): inside.append((t, p[1])) if inside: ok = sum(1 for _t, c in inside if c == "200") print(" 窗口覆盖到 %d 个健康样本,成功 %d = %.1f%%" % (len(inside), ok, ok * 100.0 / len(inside))) bad = [(t, c) for t, c in inside if c != "200"] if bad: print(" 失败明细:") for t, c in bad: print(" %s 码=%s" % (t.strftime("%H:%M:%S"), c)) else: print(" (窗口内没有健康样本)") # ---- 总体可用率 ---- if health: ok = sum(1 for _t, p in health if p[1] == "200") print() print(" ── 总体公网可用率(对照)──") print(" %d/%d = %.2f%%" % (ok, len(health), ok * 100.0 / len(health))) # ---- 一句话结论 ---- print() print(" ── 一句话 ──") print(" 风险暴露(总数<=%d): %.1f%% 窗口内可用率: %s" % (COLLAPSE_TOTAL, collapse_pct, ("%.1f%%" % (sum(1 for _t, c in inside if c == "200") * 100.0 / len(inside)) if collapse and health and inside else "无数据"))) return 0 if __name__ == "__main__": sys.exit(main())