Initial Dever 0.0.1

This commit is contained in:
Codex committed 2026-11-03 03:32:07 +00:00
commit 81233d098c
1479 files changed
+163782

No files matched your search

+200
View File
@@ -0,0 +1,200 @@
"""同 Pod 两个隔离容器:server 执行固定矩阵,client 只负责施压。
构建产物与本文件、process.py、settings.py 放入 /work;两个角色通过 emptyDir
交接有限测试命令。测量采用闭环长连接,延迟含收发和正文验证,不含建连时间。
"""
import argparse
import hashlib
import http.client
import json
import os
from pathlib import Path
import subprocess
import time
from process import Process, summarize
ROOT = Path("/work")
def write(path, value):
temporary = path.with_suffix(".tmp")
temporary.write_text(json.dumps(value, ensure_ascii=False, indent=2) + "\n")
temporary.replace(path)
def counters():
return {name: (Path("/sys/fs/cgroup") / name).read_text()
for name in ("cpu.max", "cpu.stat", "memory.max", "memory.events")}
def client():
request = ROOT / "request.json"
while not (ROOT / "finished").exists():
if not request.exists():
time.sleep(0.02)
continue
item = json.loads(request.read_text())
request.unlink()
directory = ROOT / item["name"]
before = counters()
with Process([ROOT / "bin/rust-http", "load", item["port"], item["path"],
item["concurrency"], item["seconds"]], directory, cpus={2, 3, 4, 5}) as process:
deadline = time.monotonic() + item["seconds"] + 30
while process.child.poll() is None and time.monotonic() < deadline:
process.sample()
time.sleep(0.2)
if process.child.poll() is None:
raise TimeoutError(f"load client timed out: {item}")
result = {"exit_code": process.child.returncode, "stdout": process.text(),
"stderr": (directory / "stderr.log").read_text(),
"resources": summarize(process.samples), "samples": process.samples,
"cgroup_before": before, "cgroup_after": counters()}
write(directory / "result.json", result)
def load(process, name, port, path, concurrency, seconds):
result_path = ROOT / name / "result.json"
write(ROOT / "request.json", {"name": name, "port": port, "path": path,
"concurrency": concurrency, "seconds": seconds})
deadline = time.monotonic() + seconds + 40
while not result_path.exists() and time.monotonic() < deadline:
process.sample()
time.sleep(0.2)
result = json.loads(result_path.read_text())
if result["exit_code"] == 0:
result["measurement"] = json.loads(result["stdout"])
return result
def probe(port):
connection = http.client.HTTPConnection("127.0.0.1", port, timeout=5)
try:
connection.request("GET", "/json")
response = connection.getresponse()
return response.status == 200 and response.read() == b'{"ok":true}'
except OSError:
return False
finally:
connection.close()
def command(language, capacity=2048):
if language == "dever":
return [ROOT / "bin" / ("dever-http" if capacity == 2048 else "dever-overload")]
if language == "rust":
return [ROOT / "bin/rust-http", "serve", capacity]
binary = {"go": "go-http", "axum": "axum-http", "fiber": "fiber-http"}[language]
return [ROOT / "bin" / binary, capacity]
def measure(language, path, concurrency, seconds, name):
with Process(command(language), ROOT / name / "server", cpus={0, 1}) as process:
port = process.ready()
if not probe(port):
raise RuntimeError(f"smoke failed: {name}")
warmup = load(process, name + "/warmup", port, path, concurrency, 2)
process.samples.clear()
before = counters()
result = load(process, name + "/load", port, path, concurrency, seconds)
after = counters()
recovered = probe(port)
record = {"name": name, "language": language, "path": path,
"concurrency": concurrency, "seconds": seconds,
"warmup": warmup, "client": result,
"server": summarize(process.samples), "samples": process.samples,
"server_alive": process.child.poll() is None, "recovery_probe": recovered,
"stderr": (process.directory / "stderr.log").read_text(),
"cgroup_before": before, "cgroup_after": after}
write(ROOT / name / "result.json", record)
with (ROOT / "results.jsonl").open("a") as output:
output.write(json.dumps(record) + "\n")
print(json.dumps({"name": name, "measurement": result.get("measurement"),
"rss_mib": record["server"]["rss_high_water_bytes"] / 1048576,
"server_cores": record["server"]["cpu_cores_sampled"],
"client_cores": result["resources"]["cpu_cores_sampled"],
"recovery": recovered}), flush=True)
for phase in (warmup, result):
measurement = phase.get("measurement", {})
if phase["exit_code"] != 0 or measurement.get("errors") != 0 or not measurement.get("succeeded"):
raise RuntimeError(f"load failed; retained result: {name}")
if not record["server_alive"] or not recovered:
raise RuntimeError(f"server did not recover: {name}")
def overload(language):
name = "overload/" + language
with Process(command(language, 8192), ROOT / name / "server", cpus={0, 1}) as process:
port = process.ready()
connections = []
successful = 0
failed = 0
started = time.monotonic()
try:
for _ in range(4500):
connection = http.client.HTTPConnection("127.0.0.1", port, timeout=2)
connections.append(connection)
connection.request("GET", "/json")
response = connection.getresponse()
if response.status != 200 or response.read() != b'{"ok":true}':
failed += 1
break
successful += 1
except (OSError, http.client.HTTPException):
failed += 1
held_seconds = time.monotonic() - started
process.sample()
for connection in connections:
connection.close()
time.sleep(6)
recovered = probe(port)
record = {"language": language, "connections_target": 4500,
"successful_connections": successful, "failed": failed,
"open_seconds": held_seconds, "server_alive": process.child.poll() is None,
"recovered_after_release": recovered, "stdout": process.text(),
"stderr": (process.directory / "stderr.log").read_text(), "samples": process.samples}
write(ROOT / name / "result.json", record)
print(json.dumps(record), flush=True)
def server(mode, suite):
languages = ["dever", "axum", "fiber"] if suite == "frameworks" else ["dever", "rust", "go"]
if mode == "smoke":
for language in languages:
for path in ("/json", "/echo"):
measure(language, path, 4, 1, f"smoke/{language}/{path[1:]}")
return
manifest = {"suite": suite, "languages": languages,
"protocol": "HTTP/1.1", "client": "closed loop; one outstanding request per connection",
"server_cpus": [0, 1], "client_cpus": [2, 3, 4, 5],
"replicates": 3, "seconds": 12, "warmup_seconds": 2,
"server_capacity": 2048, "concurrency": [64, 256, 1024],
"paths": {"/json": "GET; 11-byte JSON", "/echo": "POST; 65536-byte body returned unchanged"},
"hashes": {path.name: hashlib.sha256(path.read_bytes()).hexdigest() for path in (ROOT / "bin").iterdir()}}
write(ROOT / "manifest.json", manifest)
for replicate in range(3):
for path in ("/json", "/echo"):
for concurrency in (64, 256, 1024):
for language in languages[replicate:] + languages[:replicate]:
name = f"matrix/{replicate}/{path[1:]}/{concurrency}/{language}"
measure(language, path, concurrency, 12, name)
for language in languages:
measure(language, "/json", 1024, 60, "sustained/" + language)
if suite == "core":
overload(language)
(ROOT / "finished").touch()
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("role", choices=("server", "client"))
parser.add_argument("--mode", choices=("smoke", "matrix"), default="matrix")
parser.add_argument("--suite", choices=("core", "frameworks"), default="core")
arguments = parser.parse_args()
if arguments.role == "client":
client()
else:
try:
server(arguments.mode, arguments.suite)
finally:
(ROOT / "finished").touch()