mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 08:45:41 +08:00
feat: enhance latency test logging and error handling
This commit is contained in:
@@ -13,11 +13,17 @@ class DelayManager {
|
||||
private groupListenerMap = new Map<string, () => void>();
|
||||
|
||||
setUrl(group: string, url: string) {
|
||||
console.log(`[DelayManager] 设置测试URL,组: ${group}, URL: ${url}`);
|
||||
this.urlMap.set(group, url);
|
||||
}
|
||||
|
||||
getUrl(group: string) {
|
||||
return this.urlMap.get(group);
|
||||
const url = this.urlMap.get(group);
|
||||
console.log(
|
||||
`[DelayManager] 获取测试URL,组: ${group}, URL: ${url || "未设置"}`,
|
||||
);
|
||||
// 如果未设置URL,返回默认URL
|
||||
return url || "http://cp.cloudflare.com/generate_204";
|
||||
}
|
||||
|
||||
setListener(name: string, group: string, listener: (time: number) => void) {
|
||||
@@ -40,19 +46,25 @@ class DelayManager {
|
||||
|
||||
setDelay(name: string, group: string, delay: number) {
|
||||
const key = hashKey(name, group);
|
||||
this.cache.set(key, [Date.now(), delay]);
|
||||
this.listenerMap.get(key)?.(delay);
|
||||
this.groupListenerMap.get(group)?.();
|
||||
console.log(
|
||||
`[DelayManager] 设置延迟,代理: ${name}, 组: ${group}, 延迟: ${delay}`,
|
||||
);
|
||||
|
||||
this.cache.set(key, [delay, Date.now()]);
|
||||
const listener = this.listenerMap.get(key);
|
||||
if (listener) listener(delay);
|
||||
}
|
||||
|
||||
getDelay(name: string, group: string) {
|
||||
if (!name) return -1;
|
||||
const key = hashKey(name, group);
|
||||
const val = this.cache.get(key);
|
||||
if (!val) return -1;
|
||||
|
||||
const result = this.cache.get(hashKey(name, group));
|
||||
if (result && Date.now() - result[0] <= 18e5) {
|
||||
return result[1];
|
||||
// 缓存30分钟
|
||||
if (Date.now() - val[1] > 30 * 60 * 1000) {
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
return val[0];
|
||||
}
|
||||
|
||||
/// 暂时修复provider的节点延迟排序的问题
|
||||
@@ -70,13 +82,60 @@ class DelayManager {
|
||||
}
|
||||
|
||||
async checkDelay(name: string, group: string, timeout: number) {
|
||||
console.log(
|
||||
`[DelayManager] 开始测试延迟,代理: ${name}, 组: ${group}, 超时: ${timeout}ms`,
|
||||
);
|
||||
|
||||
// 先将状态设置为测试中
|
||||
this.setDelay(name, group, -2);
|
||||
|
||||
let delay = -1;
|
||||
|
||||
try {
|
||||
const url = this.getUrl(group);
|
||||
const result = await cmdGetProxyDelay(name, timeout, url);
|
||||
delay = result.delay;
|
||||
} catch {
|
||||
console.log(`[DelayManager] 调用API测试延迟,代理: ${name}, URL: ${url}`);
|
||||
|
||||
// 记录开始时间,用于计算实际延迟
|
||||
const startTime = Date.now();
|
||||
|
||||
// 设置超时处理
|
||||
const timeoutPromise = new Promise<{ delay: number }>((_, reject) => {
|
||||
setTimeout(() => reject(new Error("Timeout")), timeout);
|
||||
});
|
||||
|
||||
// 使用Promise.race来实现超时控制
|
||||
const result = await Promise.race([
|
||||
cmdGetProxyDelay(name, timeout, url),
|
||||
timeoutPromise,
|
||||
]);
|
||||
|
||||
// 确保至少显示500ms的加载动画
|
||||
const elapsedTime = Date.now() - startTime;
|
||||
if (elapsedTime < 500) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500 - elapsedTime));
|
||||
}
|
||||
|
||||
// 检查延迟结果是否为undefined
|
||||
if (result && typeof result.delay === "number") {
|
||||
delay = result.delay;
|
||||
console.log(
|
||||
`[DelayManager] 延迟测试完成,代理: ${name}, 结果: ${delay}ms`,
|
||||
);
|
||||
} else {
|
||||
console.error(
|
||||
`[DelayManager] 延迟测试返回无效结果,代理: ${name}, 结果:`,
|
||||
result,
|
||||
);
|
||||
delay = 1e6; // 错误情况
|
||||
}
|
||||
} catch (error) {
|
||||
// 确保至少显示500ms的加载动画
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
|
||||
console.error(`[DelayManager] 延迟测试出错,代理: ${name}`, error);
|
||||
if (error instanceof Error && error.message === "Timeout") {
|
||||
console.log(`[DelayManager] 延迟测试超时,代理: ${name}`);
|
||||
}
|
||||
delay = 1e6; // error
|
||||
}
|
||||
|
||||
@@ -88,42 +147,78 @@ class DelayManager {
|
||||
nameList: string[],
|
||||
group: string,
|
||||
timeout: number,
|
||||
concurrency = 36
|
||||
concurrency = 36,
|
||||
) {
|
||||
console.log(
|
||||
`[DelayManager] 批量测试延迟开始,组: ${group}, 数量: ${nameList.length}, 并发数: ${concurrency}`,
|
||||
);
|
||||
const names = nameList.filter(Boolean);
|
||||
// 设置正在延迟测试中
|
||||
names.forEach((name) => this.setDelay(name, group, -2));
|
||||
|
||||
let total = names.length;
|
||||
let current = 0;
|
||||
let index = 0;
|
||||
const startTime = Date.now();
|
||||
const listener = this.groupListenerMap.get(group);
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const help = async (): Promise<void> => {
|
||||
if (current >= concurrency) return;
|
||||
const task = names.shift();
|
||||
if (!task) return;
|
||||
current += 1;
|
||||
await this.checkDelay(task, group, timeout);
|
||||
current -= 1;
|
||||
total -= 1;
|
||||
if (total <= 0) resolve(null);
|
||||
else return help();
|
||||
};
|
||||
for (let i = 0; i < concurrency; ++i) help();
|
||||
});
|
||||
const help = async (): Promise<void> => {
|
||||
const currName = names[index++];
|
||||
if (!currName) return;
|
||||
|
||||
try {
|
||||
// 确保API调用前状态为测试中
|
||||
this.setDelay(currName, group, -2);
|
||||
|
||||
// 添加一些随机延迟,避免所有请求同时发出和返回
|
||||
if (index > 1) {
|
||||
// 第一个不延迟,保持响应性
|
||||
await new Promise((resolve) =>
|
||||
setTimeout(resolve, Math.random() * 200),
|
||||
);
|
||||
}
|
||||
|
||||
await this.checkDelay(currName, group, timeout);
|
||||
if (listener) listener();
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`[DelayManager] 批量测试单个代理出错,代理: ${currName}`,
|
||||
error,
|
||||
);
|
||||
// 设置为错误状态
|
||||
this.setDelay(currName, group, 1e6);
|
||||
}
|
||||
|
||||
return help();
|
||||
};
|
||||
|
||||
// 限制并发数,避免发送太多请求
|
||||
const actualConcurrency = Math.min(concurrency, names.length, 10);
|
||||
console.log(`[DelayManager] 实际并发数: ${actualConcurrency}`);
|
||||
|
||||
const promiseList: Promise<void>[] = [];
|
||||
for (let i = 0; i < actualConcurrency; i++) {
|
||||
promiseList.push(help());
|
||||
}
|
||||
|
||||
await Promise.all(promiseList);
|
||||
const totalTime = Date.now() - startTime;
|
||||
console.log(
|
||||
`[DelayManager] 批量测试延迟完成,组: ${group}, 总耗时: ${totalTime}ms`,
|
||||
);
|
||||
}
|
||||
|
||||
formatDelay(delay: number, timeout = 10000) {
|
||||
if (delay <= 0) return "Error";
|
||||
if (delay > 1e5) return "Error";
|
||||
if (delay >= timeout) return "Timeout"; // 10s
|
||||
if (delay === -1) return "-";
|
||||
if (delay === -2) return "testing";
|
||||
if (delay >= timeout) return "timeout";
|
||||
return `${delay}`;
|
||||
}
|
||||
|
||||
formatDelayColor(delay: number, timeout = 10000) {
|
||||
if (delay < 0) return "";
|
||||
if (delay >= timeout) return "error.main";
|
||||
if (delay <= 0) return "error.main";
|
||||
if (delay > 500) return "warning.main";
|
||||
if (delay >= 10000) return "error.main";
|
||||
if (delay >= 400) return "warning.main";
|
||||
if (delay >= 250) return "primary.main";
|
||||
return "success.main";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user