v1.3.14: CPU每核MHz显示+四页测速(CPU运行速度/内存读写/硬盘读写/网络上下行)+最多5条历史自动删旧
This commit is contained in:
1 parent
f417f8b01a
commit
1aff3138cb
4 files changed
+333
-8
No files matched your search
@@ -16,7 +16,7 @@ PORT = int(os.environ.get("PORT", "7031"))
|
||||
# ---------- CardAuth hub (免费形态: 安装统计/在线/公告/版本推送,失败开放) ----------
|
||||
HUB_URL = os.environ.get("HUB_URL", "https://auth.88531.cn/api").rstrip("/")
|
||||
HUB_PROJECT_ID = os.environ.get("HUB_PROJECT_ID", "12")
|
||||
HUB_VERSION = os.environ.get("SITE_VERSION", "1.3.13")
|
||||
HUB_VERSION = os.environ.get("SITE_VERSION", "1.3.14")
|
||||
HUB = {"ok": False, "install_id": None, "latest": "", "update_available": False,
|
||||
"update_required": False, "download_url": "", "changelog": "",
|
||||
"announcements": [], "popup": [], "ts": 0}
|
||||
@@ -374,6 +374,7 @@ def api_cpu():
|
||||
return jsonify({"cpu": cache["cpu"], "load": cache["load"],
|
||||
"active": active, "total": len(cache["procs"]),
|
||||
"info": get_cpu_info(), "percpu": get_percpu(),
|
||||
"mhz": get_percpu_mhz(),
|
||||
"items": cache["procs"][:100]})
|
||||
|
||||
_cpu_static = {}
|
||||
@@ -463,6 +464,178 @@ def get_percpu():
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
def get_percpu_mhz():
|
||||
"""每核实时频率(MHz): 优先读 cpufreq scaling_cur_freq,读不到回退 cpuinfo"""
|
||||
mhz = []
|
||||
try:
|
||||
paths = sorted(glob.glob("/sys/devices/system/cpu/cpu[0-9]*/cpufreq/scaling_cur_freq"))
|
||||
if not paths:
|
||||
for base in ("/host/sys/devices/system/cpu", "/sys/devices/system/cpu"):
|
||||
try:
|
||||
names = sorted([d for d in os.listdir(base) if d.startswith("cpu") and d[3:].isdigit()])
|
||||
except Exception:
|
||||
continue
|
||||
for n in names:
|
||||
paths.append(os.path.join(base, n, "cpufreq/scaling_cur_freq"))
|
||||
if paths:
|
||||
break
|
||||
for p in paths:
|
||||
try:
|
||||
with open(p) as f:
|
||||
v = float(f.read().strip())
|
||||
mhz.append(int(round(v / 1000.0)))
|
||||
except Exception:
|
||||
continue
|
||||
except Exception:
|
||||
pass
|
||||
if not mhz:
|
||||
try:
|
||||
for cp in ("/host/proc/cpuinfo", "/proc/cpuinfo"):
|
||||
try:
|
||||
vals = []
|
||||
with open(cp) as f:
|
||||
for line in f:
|
||||
if line.startswith("cpu MHz"):
|
||||
vals.append(int(round(float(line.split(":")[1].strip()))))
|
||||
if vals:
|
||||
mhz = vals
|
||||
break
|
||||
except Exception:
|
||||
continue
|
||||
except Exception:
|
||||
pass
|
||||
return mhz
|
||||
|
||||
# ---------- 各页测速: CPU/内存/硬盘/网络(非破坏,失败返回error) ----------
|
||||
import urllib.request as _urlreq
|
||||
|
||||
def _bench_cpu():
|
||||
import math
|
||||
N = 200000
|
||||
t0 = time.perf_counter()
|
||||
x = 0
|
||||
for i in range(N):
|
||||
x += (i * i) % 10007
|
||||
dt = max(time.perf_counter() - t0, 1e-6)
|
||||
return {"ms": int(round(dt * 1000)), "kops": int(round(N / dt / 1000)),
|
||||
"score": int(round(N / dt / 1000)), "detail": "20万次整数运算耗时"}
|
||||
|
||||
def _bench_mem():
|
||||
SZ = 32 * 1024 * 1024
|
||||
t0 = time.perf_counter()
|
||||
b = bytearray(SZ)
|
||||
b[:] = b"\x5a" * SZ
|
||||
dt_w = max(time.perf_counter() - t0, 1e-6)
|
||||
t0 = time.perf_counter()
|
||||
s = sum(b[::4096])
|
||||
dt_r = max(time.perf_counter() - t0, 1e-6)
|
||||
del b
|
||||
return {"write_mbs": round(SZ / dt_w / 1048576, 1),
|
||||
"read_mbs": round(SZ / dt_r / 1048576, 1),
|
||||
"size_mb": 32, "detail": "32MB 内存写/读", "checksum": s % 997}
|
||||
|
||||
def _bench_disk():
|
||||
SZ_MB = 32
|
||||
data = os.urandom(4 * 1024 * 1024)
|
||||
path = ""
|
||||
for cand in ("/host/vol1/.fnos_resmon_bench.tmp",
|
||||
"/host/.fnos_resmon_bench.tmp", "/tmp/.fnos_resmon_bench.tmp"):
|
||||
try:
|
||||
d = os.path.dirname(cand)
|
||||
if os.path.isdir(d) and os.access(d, os.W_OK):
|
||||
path = cand
|
||||
break
|
||||
except Exception:
|
||||
continue
|
||||
if not path:
|
||||
return {"ok": False, "error": "无可写目录"}
|
||||
try:
|
||||
t0 = time.perf_counter()
|
||||
with open(path, "wb") as f:
|
||||
for _ in range(SZ_MB // 4):
|
||||
f.write(data)
|
||||
f.flush()
|
||||
os.fsync(f.fileno())
|
||||
dt_w = max(time.perf_counter() - t0, 1e-6)
|
||||
t0 = time.perf_counter()
|
||||
with open(path, "rb") as f:
|
||||
while f.read(4 * 1024 * 1024):
|
||||
pass
|
||||
dt_r = max(time.perf_counter() - t0, 1e-6)
|
||||
return {"write_mbs": round(SZ_MB / dt_w, 1),
|
||||
"read_mbs": round(SZ_MB / dt_r, 1),
|
||||
"size_mb": SZ_MB, "file": path.replace("/host", "") or "/",
|
||||
"detail": "32MB 文件写(含落盘)/读"}
|
||||
except Exception as e:
|
||||
return {"ok": False, "error": str(e)}
|
||||
finally:
|
||||
try:
|
||||
os.unlink(path)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _bench_net():
|
||||
out = {}
|
||||
try:
|
||||
url = "https://speed.cloudflare.com/__down?bytes=5000000"
|
||||
t0 = time.perf_counter()
|
||||
req = _urlreq.Request(url, headers={"User-Agent": "fnos-resmon"})
|
||||
with _urlreq.urlopen(req, timeout=25) as r:
|
||||
n = 0
|
||||
while True:
|
||||
ch = r.read(256 * 1024)
|
||||
if not ch:
|
||||
break
|
||||
n += len(ch)
|
||||
dt = max(time.perf_counter() - t0, 1e-6)
|
||||
out["down_mbps"] = round(n * 8 / dt / 1000000, 1)
|
||||
out["down_mb"] = round(n / 1048576, 1)
|
||||
out["down_s"] = round(dt, 1)
|
||||
except Exception as e:
|
||||
return {"ok": False, "error": "下载测速失败: %s" % e}
|
||||
try:
|
||||
payload = os.urandom(2 * 1024 * 1024)
|
||||
t0 = time.perf_counter()
|
||||
req = _urlreq.Request("https://speed.cloudflare.com/__up",
|
||||
data=payload, method="POST",
|
||||
headers={"Content-Type": "application/octet-stream"})
|
||||
with _urlreq.urlopen(req, timeout=25) as r:
|
||||
r.read()
|
||||
dt = max(time.perf_counter() - t0, 1e-6)
|
||||
out["up_mbps"] = round(len(payload) * 8 / dt / 1000000, 1)
|
||||
out["up_mb"] = round(len(payload) / 1048576, 1)
|
||||
out["up_s"] = round(dt, 1)
|
||||
except Exception as e:
|
||||
out["up_mbps"] = 0
|
||||
out["up_error"] = "上传测速失败: %s" % e
|
||||
out.update({"ok": True, "detail": "Cloudflare 测速点: 下行5MB/上行2MB"})
|
||||
return out
|
||||
|
||||
@app.route("/api/bench/<kind>", methods=["POST"])
|
||||
def api_bench(kind):
|
||||
try:
|
||||
if kind == "cpu":
|
||||
r = _bench_cpu()
|
||||
p = _read_perf()
|
||||
r.update({"ok": True, "mode": p.get("mode", ""),
|
||||
"governor": p.get("governor", ""), "epp": p.get("epp", ""),
|
||||
"mhz_avg": (sum(get_percpu_mhz()) // len(get_percpu_mhz()) if get_percpu_mhz() else 0)})
|
||||
elif kind == "mem":
|
||||
r = _bench_mem()
|
||||
r["ok"] = True
|
||||
elif kind == "disk":
|
||||
r = _bench_disk()
|
||||
if "ok" not in r:
|
||||
r["ok"] = True
|
||||
elif kind == "net":
|
||||
r = _bench_net()
|
||||
else:
|
||||
return jsonify({"ok": False, "error": "未知测速类型"}), 400
|
||||
r["at"] = time.strftime("%m-%d %H:%M:%S", time.localtime())
|
||||
return jsonify(r)
|
||||
except Exception as e:
|
||||
return jsonify({"ok": False, "error": str(e)}), 500
|
||||
|
||||
# ---------- CPU 性能模式: 省电/平衡/高性能(写宿主 cpufreq,失败开放) ----------
|
||||
PERF_FILE = os.environ.get("PERF_FILE", "/run/secrets/perf_mode.json")
|
||||
PERF_FILE_FALLBACKS = [PERF_FILE, "/app/perf_mode.json"]
|
||||
|
||||
Reference in new issue
Block a user