Files
ocr_data/tools/查端点存活.py
2026-09-28 16:09:31 +08:00

148 lines
5.3 KiB
Python

# -*- coding: utf-8 -*-
"""
逐个探测 config 里的 OCR 端点, 看有多少台【真的在服务】。
用来确认"120台只活了20台"这类问题——死端点越多, 活端点被压得越狠 → 超时。
用法:
python3 tools/查端点存活.py # 自动找一张真实帧图做OCR探测
python3 tools/查端点存活.py --tcp # 只测TCP端口连通(更快, 不发OCR)
python3 tools/查端点存活.py --timeout 5
python3 tools/查端点存活.py --list-dead # 额外列出死端点IP
"""
import argparse
import base64
import glob
import os
import socket
import sys
import time
from collections import Counter
from concurrent.futures import ThreadPoolExecutor, as_completed
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import requests
from config.config_utils import config
OCR_PATH = "/youtu/ocrapi/hpgeneralocr_v3"
DEFAULT_PORT = 18081
def norm(e):
e = str(e).strip().rstrip("/")
if not e:
return None
if not e.startswith("http"):
e = "http://" + e
host = e.split("://", 1)[1]
if ":" not in host:
e = e + ":" + str(DEFAULT_PORT)
return e
def host_port(ep):
h = ep.split("://", 1)[1]
host, _, port = h.partition(":")
return host, int(port or DEFAULT_PORT)
def find_image_b64():
"""找一张真实帧图转 base64; 找不到返回 None(改用TCP探测)。"""
for pat in ("./split/**/*.jpg", "./composite/**/*.jpg", "./split/*/*.jpg"):
for p in glob.glob(pat, recursive=True):
try:
with open(p, "rb") as f:
return base64.b64encode(f.read()).decode()
except OSError:
continue
return None
def probe_ocr(ep, img_b64, timeout):
url = ep + OCR_PATH
payload = {"app_id": str(int(time.time() * 1000)), "image": img_b64,
"ocr_template": "OCR", "options": {"pdf_page_index": 0, "detect_text": True}}
t0 = time.time()
try:
r = requests.post(url, json=payload, timeout=timeout,
headers={"Content-Type": "application/json"})
dt = time.time() - t0
return (ep, "alive", dt, "") if r.status_code == 200 else (ep, "http_err", dt, f"HTTP {r.status_code}")
except requests.exceptions.ConnectTimeout:
return (ep, "dead", time.time() - t0, "连接超时")
except requests.exceptions.ReadTimeout:
return (ep, "slow", time.time() - t0, "读超时(连得上不回)")
except requests.exceptions.ConnectionError:
return (ep, "dead", time.time() - t0, "拒绝/不可达")
except Exception as e:
return (ep, "dead", time.time() - t0, str(e)[:40])
def probe_tcp(ep, timeout):
host, port = host_port(ep)
t0 = time.time()
try:
with socket.create_connection((host, port), timeout=timeout):
return (ep, "alive", time.time() - t0, "TCP通")
except socket.timeout:
return (ep, "dead", time.time() - t0, "TCP超时")
except Exception as e:
return (ep, "dead", time.time() - t0, str(e)[:40])
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--timeout", type=float, default=5.0)
ap.add_argument("--tcp", action="store_true", help="只测TCP端口连通(不发OCR)")
ap.add_argument("--list-dead", action="store_true")
args = ap.parse_args()
eps = config.get_tencent_ocr_config().get("endpoints") or []
eps = [u for u in (norm(e) for e in eps) if u]
img = None
if not args.tcp:
img = find_image_b64()
if img is None:
print("没找到帧图, 改用 TCP 连通探测(--tcp)。")
mode = "TCP连通" if (args.tcp or img is None) else "真实OCR请求"
print(f"探测 {len(eps)} 个端点 (方式: {mode}, 超时 {args.timeout}s)...")
def work(e):
if args.tcp or img is None:
return probe_tcp(e, args.timeout)
return probe_ocr(e, img, args.timeout)
res = []
with ThreadPoolExecutor(max_workers=64) as ex:
for f in as_completed([ex.submit(work, e) for e in eps]):
res.append(f.result())
c = Counter(r[1] for r in res)
alive = [r for r in res if r[1] in ("alive", "http_err")]
lats = sorted(r[2] for r in alive)
total = len(eps)
good = c["alive"] + c["http_err"]
print("=" * 52)
print(f" 可用(alive/http_err): {good}")
print(f" 慢(读超时): {c['slow']}")
print(f" 死(连不上/拒绝): {c['dead']}")
if lats:
print(f" 可用端点延迟: 中位 {lats[len(lats)//2]*1000:.0f}ms 最慢 {lats[-1]*1000:.0f}ms")
print("=" * 52)
print(f"结论: {total} 个端点里 {good} 个可用 ({good/total*100:.0f}%)")
if good < total * 0.8:
print(f"⚠️ {total-good} 个端点不可用! 负载全压在 {good} 个上 → 这极可能就是OCR超时根因。")
print(f" 把死端点服务拉起 / 或从config删掉死的, OCR容量最高可提升 {total/max(good,1):.1f} 倍。")
else:
print("端点基本都活着 → OCR超时不是'端点少', 是'整体容量/CPU抢占'问题。")
if args.list_dead:
print("\n死/慢端点:")
for ep, st, dt, msg in sorted(res, key=lambda x: x[1]):
if st in ("dead", "slow"):
print(f" {st:6s} {ep:32s} {msg}")
if __name__ == "__main__":
main()