#!/bin/bash # quic+6 vs http2+4:用**真实长轮询**对照,方法完全一致。 # # 为什么必须用长轮询测: # 之前用 /v1/health 那种"每次新建连接"的方式测, # 量到的是冷启动开销,和真实业务(复用连接的长轮询)差很多。 # 而且那种方式会看到 18 秒的尾巴 —— 长轮询完全没有。 # # 为什么这次结果可信: # 上一轮 A/B 里 quic 那组因为**两次探针用了同一个配对码**, # 第二次注册被"一个码只能一台设备"规则拒掉,token 拿到空的, # 8 次全 401 —— 那一轮数据作废。这次每个探针都用唯一的新码。 # # 为什么值得测 QUIC: # 刚才查出来:隧道连接 30 分钟 0 掉线、0 重连,很稳; # 但请求级偶发 000(10 分钟 6 次)。 # 这是 HTTP/2 over TCP 的**队头阻塞** —— # 一条 TCP 上丢一个包,那条连接上所有流一起卡。 # QUIC 每个流独立恢复,理论上正好治这个。 set -u PUB=https://relay.xxmxxs.top UA='LanRemote/1.8.1' SVC=/etc/systemd/system/lanremote-tunnel.service BACKUP=/root/lanremote-tunnel.service.bak.q.$(date +%s) cp -a "$SVC" "$BACKUP" ROUNDS=${1:-10} set_params() { local proto="$1" conns="$2" if grep -q -- '--protocol' "$SVC"; then sed -i "s/--protocol [a-z0-9]*/--protocol $proto/" "$SVC" else sed -i "s|\(ExecStart=.*cloudflared tunnel\)|\1 --protocol $proto|" "$SVC" fi if grep -q -- '--ha-connections' "$SVC"; then sed -i "s/--ha-connections [0-9]*/--ha-connections $conns/" "$SVC" else sed -i "s|\(ExecStart=.*cloudflared tunnel\)|\1 --ha-connections $conns|" "$SVC" fi systemctl daemon-reload systemctl restart lanremote-tunnel } wait_ready() { local want="$1" for i in $(seq 1 30); do sleep 5 n=$(curl -s --max-time 4 http://127.0.0.1:20241/metrics 2>/dev/null \ | grep '^cloudflared_tunnel_ha_connections' | awk '{print $2}') [ "${n:-0}" -ge "$want" ] && return 0 done return 1 } new_probe() { # 每次都用全新的 deviceId + 全新的配对码,避免被"一码一机"拒掉 local stamp stamp=$(date +%s%N | cut -c1-13) local dev="qprobe-${stamp}" local code code=$(printf "%06d" $(( (stamp / 7) % 1000000 ))) local reg reg=$(curl -s --max-time 30 -X POST "$PUB/v1/agent/register" \ -H 'Content-Type: application/json' -H "User-Agent: $UA" \ -d "{\"deviceId\":\"$dev\",\"code\":\"$code\",\"name\":\"qprobe\",\"version\":\"1.8.1\"}") local tok tok=$(echo "$reg" | python3 -c "import sys,json; print(json.load(sys.stdin).get('token',''))" 2>/dev/null) PROBE_DEV="$dev" PROBE_TOK="$tok" PROBE_CODE="$code" } cleanup_probe() { local key key=$(cat /root/lanremote-relay/data/admin_key.txt 2>/dev/null) [ -n "${PROBE_DEV:-}" ] && 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\":\"$PROBE_DEV\"}" > /dev/null 2>&1 } run_round() { local label="$1" proto="$2" conns="$3" echo "════════ $label ════════" set_params "$proto" "$conns" if wait_ready "$conns"; then echo " 连接就绪($(curl -s --max-time 4 http://127.0.0.1:20241/metrics 2>/dev/null | grep '^cloudflared_tunnel_ha_connections' | awk '{print $2}') 条)" else echo " ! 连接没建齐,仍然继续测" fi echo " 落点:" curl -s --max-time 5 http://127.0.0.1:20241/metrics 2>/dev/null \ | grep 'server_locations.*} 1' | sed 's/^/ /' sleep 15 new_probe if [ -z "${PROBE_TOK:-}" ]; then echo " [失败] 探针注册没拿到 token,这轮作废" echo "$label|REGFAIL|0|0|0" >> /tmp/qab.txt return 1 fi echo " 探针 code=$PROBE_CODE token=${#PROBE_TOK} 字符" local mark mark=$(wc -l < /root/lanremote-relay/tunnel.log 2>/dev/null || echo 0) local ok=0 fail=0 times=() fails="" for i in $(seq 1 "$ROUNDS"); do out=$(curl -s -o /dev/null -w '%{http_code} %{time_total}' --max-time 100 \ -X POST "$PUB/v1/agent/poll" -H "X-Token: $PROBE_TOK" \ -H "User-Agent: $UA") code=$(echo "$out" | awk '{print $1}') ms=$(echo "$out" | awk '{printf "%d", $2*1000}') if [ "$code" = "200" ]; then ok=$((ok+1)); times+=("$ms") printf " %2d: 200 %6dms\n" "$i" "$ms" else fail=$((fail+1)); fails="$fails ${i}:${code}" printf " %2d: %-4s **失败**\n" "$i" "$code" fi done local stats if [ "${#times[@]}" -gt 0 ]; then stats=$(printf '%s\n' "${times[@]}" | sort -n | awk '{a[NR]=$1} END { printf "中位 %d P90 %d 最大 %d", a[int(NR/2)+1], a[int(NR*0.9)+1], a[NR]}') else stats="无数据" fi local reconn reconn=$(tail -n +$((mark+1)) /root/lanremote-relay/tunnel.log 2>/dev/null \ | grep -c 'Registered tunnel connection' || echo 0) echo " == $ok/$ROUNDS 成功 耗时: $stats ms 本轮重连 $reconn 次" echo " 失败:$fails" echo "$label|$ok|$ROUNDS|$((ok*100/ROUNDS))|$stats|$reconn" >> /tmp/qab.txt cleanup_probe echo } rm -f /tmp/qab.txt # 先基线,再候选 —— 同一网络条件下先后测,尽量公平 run_round "http2+4" http2 4 || true run_round "quic+6" quic 6 || true echo "════════ 汇总 ════════" printf " %-10s %-10s %-8s %-32s %s\n" "配置" "成功" "可用率" "耗时" "本轮重连" while IFS='|' read -r label ok total pct stats reconn; do if [ "$ok" = "REGFAIL" ]; then printf " %-10s 探针注册失败,作废\n" "$label" else printf " %-10s %-10s %-8s %-32s %s\n" "$label" "$ok/$total" "${pct}%" "$stats" "$reconn" fi done < /tmp/qab.txt echo echo "════════ 决策 ════════" B_OK=$(grep '^http2+4' /tmp/qab.txt | cut -d'|' -f4) Q_OK=$(grep '^quic+6' /tmp/qab.txt | cut -d'|' -f4) B_REC=$(grep '^http2+4' /tmp/qab.txt | cut -d'|' -f6) Q_REC=$(grep '^quic+6' /tmp/qab.txt | cut -d'|' -f6) echo " 基线: 可用率 ${B_OK:-?}% 重连 ${B_REC:-?}" echo " 候选: 可用率 ${Q_OK:-?}% 重连 ${Q_REC:-?}" if [ "${Q_OK:-0}" -ge "${B_OK:-0}" ] && [ "${Q_OK:-0}" -ge 90 ]; then echo " → 采用 quic+6" else echo " → 保留 http2+4" cp -a "$BACKUP" "$SVC" systemctl daemon-reload systemctl restart lanremote-tunnel fi sleep 15 echo echo " 最终参数: $(grep -oE '\-\-protocol [a-z0-9]+|--ha-connections [0-9]+' "$SVC" | tr '\n' ' ')" echo " 备份: $BACKUP"