feat: 重构为多类型 checker 通用框架,支持 HTTP 与命令检查
- 引入 typed target 判别联合,支持 http 与 command 两种 checker - expect 重构为有序规则数组,按配置顺序快速失败并生成结构化 failure - 新增 command runner,支持 exec + args 本地命令执行 - 引入全局并发限制 maxConcurrentChecks 和 size 解析 (KB/MB/GB) - HTTP/command 各自独立 expect pipeline,应用领域默认成功语义 - SQLite schema、API、Dashboard 全链路调整为 checker 通用契约 - 补充完整测试覆盖(192 tests),更新 README 与示例配置
This commit is contained in:
@@ -1,15 +1,21 @@
|
||||
import type { CheckResult, ResolvedTarget } from "./types";
|
||||
import type { ProbeStore } from "./store";
|
||||
import { fetchTarget } from "./fetcher";
|
||||
import { runHttpCheck } from "./fetcher";
|
||||
import { runCommandCheck } from "./command-runner";
|
||||
|
||||
export class ProbeEngine {
|
||||
private timers: ReturnType<typeof setInterval>[] = [];
|
||||
private store: ProbeStore;
|
||||
private targets: ResolvedTarget[];
|
||||
private targetNameToId: Map<string, number> = new Map();
|
||||
private maxConcurrentChecks: number;
|
||||
private running = 0;
|
||||
private queue: Array<() => void> = [];
|
||||
|
||||
constructor(store: ProbeStore, targets: ResolvedTarget[]) {
|
||||
constructor(store: ProbeStore, targets: ResolvedTarget[], maxConcurrentChecks?: number) {
|
||||
this.store = store;
|
||||
this.targets = targets;
|
||||
this.maxConcurrentChecks = maxConcurrentChecks ?? 10;
|
||||
this.refreshCache();
|
||||
}
|
||||
|
||||
@@ -46,8 +52,36 @@ export class ProbeEngine {
|
||||
return groups;
|
||||
}
|
||||
|
||||
private async acquire(): Promise<void> {
|
||||
if (this.running < this.maxConcurrentChecks) {
|
||||
this.running++;
|
||||
return;
|
||||
}
|
||||
return new Promise<void>((resolve) => {
|
||||
this.queue.push(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
private release(): void {
|
||||
const next = this.queue.shift();
|
||||
if (next) {
|
||||
next();
|
||||
} else {
|
||||
this.running--;
|
||||
}
|
||||
}
|
||||
|
||||
private async probeGroup(targets: ResolvedTarget[]): Promise<void> {
|
||||
const results = await Promise.allSettled(targets.map((t) => this.probeOne(t)));
|
||||
const results = await Promise.allSettled(
|
||||
targets.map(async (target) => {
|
||||
await this.acquire();
|
||||
try {
|
||||
return await this.runCheck(target);
|
||||
} finally {
|
||||
this.release();
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
for (const result of results) {
|
||||
if (result.status === "fulfilled") {
|
||||
@@ -56,23 +90,27 @@ export class ProbeEngine {
|
||||
}
|
||||
}
|
||||
|
||||
private async probeOne(target: ResolvedTarget): Promise<CheckResult> {
|
||||
return fetchTarget(target);
|
||||
private async runCheck(target: ResolvedTarget): Promise<CheckResult> {
|
||||
switch (target.type) {
|
||||
case "http":
|
||||
return runHttpCheck(target);
|
||||
case "command":
|
||||
return runCommandCheck(target);
|
||||
}
|
||||
}
|
||||
|
||||
private writeResult(result: CheckResult): void {
|
||||
const targetId = this.targetNameToId.get(result.targetName);
|
||||
|
||||
if (!targetId) return;
|
||||
|
||||
this.store.insertCheckResult({
|
||||
targetId,
|
||||
timestamp: result.timestamp,
|
||||
success: result.success,
|
||||
statusCode: result.statusCode,
|
||||
latencyMs: result.latencyMs,
|
||||
error: result.error,
|
||||
matched: result.matched,
|
||||
durationMs: result.durationMs,
|
||||
statusDetail: result.statusDetail,
|
||||
failure: result.failure,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -82,6 +120,4 @@ export class ProbeEngine {
|
||||
this.targetNameToId.set(target.name, target.id);
|
||||
}
|
||||
}
|
||||
|
||||
private targets: ResolvedTarget[];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user