Files
kra-new/scripts/sw_endpoint_benchmark.py
T

77 lines
3.6 KiB
Python

"""只读请求商务接口,记录耗时、完整数据摘要及 SQL;敏感内容必须存放仓库外。"""
import argparse
import hashlib
import json
import re
import statistics
import time
import urllib.parse
import urllib.request
from pathlib import Path
def log_offsets(directory):
return {p: p.stat().st_size for p in directory.rglob("*.log")}
def read_queries(directory, offsets):
queries = []
files = list(directory.rglob("*.log"))
files = [file for file in files if file.parent.name == "sql"] or files
for file in files:
with file.open("rb") as stream:
stream.seek(offsets.get(file, 0))
for line in stream:
try:
row = json.loads(line)
except (ValueError, UnicodeDecodeError):
continue
if row.get("gorm_logger") and "sql" in row:
queries.append(row)
return queries
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--runtime", type=Path, required=True)
parser.add_argument("--base-url", default="http://127.0.0.1:19004")
parser.add_argument("--phase", required=True)
parser.add_argument("--repeat", type=int, default=2)
parser.add_argument("--cases", type=Path)
args = parser.parse_args()
if args.runtime.resolve().is_relative_to(Path(__file__).resolve().parents[1]):
raise ValueError("完整接口响应和 SQL 必须留在仓库外")
output = args.runtime / args.phase
output.mkdir(exist_ok=True)
cases = json.loads((args.cases or args.runtime / "cases.json").read_text(encoding="utf-8"))
token = (args.runtime / "new-admin.token").read_text(encoding="utf-8").strip()
report = []
for case in cases:
elapsed = []
for round_number in range(args.repeat):
offsets = log_offsets(args.runtime / "sql-logs")
url = args.base_url + case["path"] + "?" + urllib.parse.urlencode(case["params"], doseq=True)
request = urllib.request.Request(url, headers={"x-token": token, "X-Request-Id": f"perf-{args.phase}-{case['name']}-{round_number}"})
started = time.monotonic()
with urllib.request.urlopen(request, timeout=300) as response:
body = json.load(response)
elapsed.append(round((time.monotonic() - started) * 1000, 2))
if body.get("code") != 0:
raise RuntimeError("请求失败:" + case["name"])
queries = read_queries(args.runtime / "sql-logs", offsets)
data = body["data"]
capture = {"case": case, "data": data, "elapsedMs": elapsed[-1], "queries": queries}
(output / f"{case['name']}-{round_number}.json").write_text(json.dumps(capture, ensure_ascii=False, indent=2), encoding="utf-8")
digest = hashlib.sha256(json.dumps(data, sort_keys=True, ensure_ascii=False).encode()).hexdigest()
slow = sorted(queries, key=lambda row: row.get("elapsed_ms", 0), reverse=True)[:5]
item = {"case": case["name"], "round": round_number, "elapsedMs": elapsed[-1], "dataSha256": digest, "queryCount": len(queries), "slowQueries": [{"elapsedMs": q.get("elapsed_ms"), "rows": q.get("rows"), "tables": list(dict.fromkeys(re.findall(r"(?:FROM|JOIN)\s+`?([\w.]+)", q["sql"], re.I)))} for q in slow]}
report.append(item)
(output / "report.json").write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
print(json.dumps(item), flush=True)
print(json.dumps({"case": case["name"], "medianMs": statistics.median(elapsed)}), flush=True)
if __name__ == "__main__":
main()