#!/bin/bash # 2 小时综合长测(task-3 第 1、3 项)。 # # 同时测四件事,并且都带**时间戳**,这样才能回答 # "失败是不是集中在某个时段"(运营商时段性抖动): # # 1. 公网可用率 每 15 秒一次 /v1/health,连续 2 小时 # 2. 长轮询成功率 30 次真实 poll(每次挂 60 秒),后台跑 # 3. 连接数波动 每 60 秒采样两个 metrics 端口 # 4. 重连/拨号失败 从两个 tunnel 日志的事件增量算 # # 输出: # /root/long2h_health.tsv 时间戳 状态码 耗时ms # /root/long2h_poll.tsv 序号 状态码 耗时ms 起始时间 # /root/long2h_conns.tsv 时间戳 quic连接 http2连接 # /root/long2h_summary.txt 汇总 set -u BASE=https://relay.xxmxxs.top UA='LanRemote/1.8.2' DUR=${1:-7200} # 默认 2 小时 HEALTH_EVERY=15 CONN_EVERY=60 POLLS=30 H=/root/long2h_health.tsv P=/root/long2h_poll.tsv C=/root/long2h_conns.tsv S=/root/long2h_summary.txt MAINLOG=/root/lanremote-relay/tunnel.log H2LOG=/root/lanremote-relay/tunnel-h2.log rm -f "$H" "$P" "$C" "$S" conns_of() { curl -s --max-time 4 "http://127.0.0.1:$1/metrics" 2>/dev/null \ | grep '^cloudflared_tunnel_ha_connections' | awk '{print $2}' } # ---------- 后台任务 1:长轮询 ---------- poll_worker() { local stamp dev code reg token stamp=$(date +%s) dev="l2h-${stamp}" code=$(printf "%06d" $(( stamp % 1000000 ))) reg=$(curl -s --max-time 30 -X POST "$BASE/v1/agent/register" \ -H 'Content-Type: application/json' -H "User-Agent: $UA" \ -d "{\"deviceId\":\"$dev\",\"code\":\"$code\",\"name\":\"l2h\",\"version\":\"1.8.2\"}") token=$(echo "$reg" | python3 -c "import sys,json; print(json.load(sys.stdin).get('token',''))" 2>/dev/null) if [ -z "$token" ]; then echo "0 0 0 REGFAIL" >> "$P" return fi local i for i in $(seq 1 "$POLLS"); do local t0 out c ms t0=$(date '+%H:%M:%S') out=$(curl -s -o /dev/null -w '%{http_code} %{time_total}' --max-time 130 \ -X POST "$BASE/v1/agent/poll" -H "X-Token: $token" -H "User-Agent: $UA") c=$(echo "$out" | awk '{print $1}') ms=$(echo "$out" | awk '{printf "%d", $2*1000}') echo "$i $c $ms $t0" >> "$P" done # 清理探针 local key key=$(cat /root/lanremote-relay/data/admin_key.txt 2>/dev/null) curl -s --max-time 20 -X POST "http://127.0.0.1:8080/v1/admin/delete" \ -H "X-Admin: $key" -H 'Content-Type: application/json' \ -d "{\"deviceId\":\"$dev\"}" > /dev/null 2>&1 } poll_worker & POLL_PID=$! # ---------- 记录起始的日志行号,用于算增量 ---------- M0=$(wc -l < "$MAINLOG" 2>/dev/null || echo 0) H0=$(wc -l < "$H2LOG" 2>/dev/null || echo 0) T0=$(date +%s) NEXT_CONN=$T0 echo " 开始: $(date '+%Y-%m-%d %H:%M:%S') 持续 ${DUR} 秒" # ---------- 主循环:公网健康 ---------- while true; do NOW=$(date +%s) [ $((NOW - T0)) -ge "$DUR" ] && break out=$(curl -s -o /dev/null -w '%{http_code} %{time_total}' --max-time 25 \ -H "User-Agent: $UA" "$BASE/v1/health") code=$(echo "$out" | awk '{print $1}') ms=$(echo "$out" | awk '{printf "%d", $2*1000}') echo "$(date '+%Y-%m-%d %H:%M:%S') $code $ms" >> "$H" # 连接数采样 if [ "$NOW" -ge "$NEXT_CONN" ]; then a=$(conns_of 20241); b=$(conns_of 20242) echo "$(date '+%Y-%m-%d %H:%M:%S') ${a:-X} ${b:-X}" >> "$C" NEXT_CONN=$((NOW + CONN_EVERY)) fi sleep "$HEALTH_EVERY" done wait "$POLL_PID" 2>/dev/null || true # ---------- 汇总 ---------- T1=$(date +%s) M1=$(wc -l < "$MAINLOG" 2>/dev/null || echo 0) H1=$(wc -l < "$H2LOG" 2>/dev/null || echo 0) { echo "================================================================" echo " 2 小时综合长测 · $(date '+%Y-%m-%d %H:%M:%S')" echo " 实际时长: $((T1 - T0)) 秒" echo "================================================================" echo echo "── 1. 公网可用率 ──" total=$(wc -l < "$H") ok=$(awk -F'\t' '$2=="200"' "$H" | wc -l) echo " 样本 $total 次,成功 $ok 次 = $((ok * 100 / (total > 0 ? total : 1)))%" echo " 各状态码分布:" awk -F'\t' '{print $2}' "$H" | sort | uniq -c | sort -rn | sed 's/^/ /' echo " 耗时分布(成功的):" awk -F'\t' '$2=="200" {print $3}' "$H" | sort -n | awk ' {a[NR]=$1} END { if (NR == 0) { print " 无数据"; exit } printf " 最小 %d 中位 %d P90 %d P99 %d 最大 %d ms\n", a[1], a[int(NR/2)+1], a[int(NR*0.9)+1], a[int(NR*0.99)+1], a[NR] }' | sed 's/^/ /' echo echo " ★ 失败时刻(时间线,用来判断是不是集中在某时段):" awk -F'\t' '$2!="200" {print " " $1 " 码=" $2 " 耗时=" $3 "ms"}' "$H" echo echo "── 2. 长轮询成功率 ──" ptotal=$(grep -c . "$P" 2>/dev/null || echo 0) pok=$(awk -F'\t' '$2=="200"' "$P" 2>/dev/null | wc -l) echo " $pok/$ptotal = $((pok * 100 / (ptotal > 0 ? ptotal : 1)))%" awk -F'\t' '$2=="200" {print $3}' "$P" | sort -n | awk ' {a[NR]=$1} END { if (NR == 0) { print " 无数据"; exit } printf " 最小 %d P50 %d P90 %d P99 %d 最大 %d ms(理想 ≈ 61000)\n", a[1], a[int(NR/2)+1], a[int(NR*0.9)+1], a[int(NR*0.99)+1], a[NR] }' echo " 失败的:" awk -F'\t' '$2!="200" {print " #" $1 " 码=" $2 " 起始 " $4}' "$P" echo echo "── 3. 连接数波动 ──" echo " quic 实例: 最小 $(awk -F'\t' 'NR>1{print $2}' "$C" | grep -E '^[0-9]+$' | sort -n | head -1) 最大 $(awk -F'\t' '{print $2}' "$C" | grep -E '^[0-9]+$' | sort -n | tail -1)" echo " http2 实例: 最小 $(awk -F'\t' '{print $3}' "$C" | grep -E '^[0-9]+$' | sort -n | head -1) 最大 $(awk -F'\t' '{print $3}' "$C" | grep -E '^[0-9]+$' | sort -n | tail -1)" echo " 出现过的组合(次数):" awk -F'\t' 'NR>1{print "quic="$2" http2="$3}' "$C" | sort | uniq -c | sort -rn | head -8 | sed 's/^/ /' echo echo "── 4. 隧道事件(本轮增量)──" echo " QUIC 实例:" for kw in 'Registered tunnel connection' 'Failed to dial' 'Lost connection' 'i/o timeout'; do n=$(tail -n +$((M0 + 1)) "$MAINLOG" 2>/dev/null | grep -c "$kw") printf " %-34s %s\n" "$kw" "$n" done echo " HTTP/2 实例:" for kw in 'Registered tunnel connection' 'Failed to dial' 'Lost connection' 'i/o timeout'; do n=$(tail -n +$((H0 + 1)) "$H2LOG" 2>/dev/null | grep -c "$kw") printf " %-34s %s\n" "$kw" "$n" done echo echo " 失败事件的时间线(QUIC):" tail -n +$((M0 + 1)) "$MAINLOG" 2>/dev/null \ | grep -E 'Failed to dial|Lost connection' | tail -20 \ | sed -E 's/^(....-..-..T..:..):.*(Failed to dial|Lost connection).*/ \1 \2/' | sed 's/^/ /' } > "$S" 2>&1 cat "$S"