feat: enhance latency test logging and error handling

This commit is contained in:
wonfen 2025-03-09 04:22:34 +08:00
parent 95e8fe38f2
commit ca1f7a2b31
5 changed files with 219 additions and 51 deletions

View File

@ -365,6 +365,8 @@ export const ProxyGroups = (props: Props) => {
// 测全部延迟
const handleCheckAll = useLockFn(async (groupName: string) => {
console.log(`[ProxyGroups] 开始测试所有延迟,组: ${groupName}`);
const proxies = renderList
.filter(
(e) => e.group?.name === groupName && (e.type === 2 || e.type === 4),
@ -372,20 +374,40 @@ export const ProxyGroups = (props: Props) => {
.flatMap((e) => e.proxyCol || e.proxy!)
.filter(Boolean);
console.log(`[ProxyGroups] 找到代理数量: ${proxies.length}`);
const providers = new Set(proxies.map((p) => p!.provider!).filter(Boolean));
if (providers.size) {
console.log(`[ProxyGroups] 发现提供者,数量: ${providers.size}`);
Promise.allSettled(
[...providers].map((p) => providerHealthCheck(p)),
).then(() => onProxies());
).then(() => {
console.log(`[ProxyGroups] 提供者健康检查完成`);
onProxies();
});
}
const names = proxies.filter((p) => !p!.provider).map((p) => p!.name);
console.log(`[ProxyGroups] 过滤后需要测试的代理数量: ${names.length}`);
await Promise.race([
delayManager.checkListDelay(names, groupName, timeout),
getGroupProxyDelays(groupName, delayManager.getUrl(groupName), timeout), // 查询group delays 将清除fixed(不关注调用结果)
]);
const url = delayManager.getUrl(groupName);
console.log(`[ProxyGroups] 测试URL: ${url}, 超时: ${timeout}ms`);
try {
await Promise.race([
delayManager.checkListDelay(names, groupName, timeout),
getGroupProxyDelays(groupName, url, timeout).then((result) => {
console.log(
`[ProxyGroups] getGroupProxyDelays返回结果数量:`,
Object.keys(result || {}).length,
);
}), // 查询group delays 将清除fixed(不关注调用结果)
]);
console.log(`[ProxyGroups] 延迟测试完成,组: ${groupName}`);
} catch (error) {
console.error(`[ProxyGroups] 延迟测试出错,组: ${groupName}`, error);
}
onProxies();
});

View File

@ -48,7 +48,7 @@ export const ProxyHead = (props: Props) => {
useEffect(() => {
delayManager.setUrl(
groupName,
testUrl || url || verge?.default_latency_test!
testUrl || url || verge?.default_latency_test!,
);
}, [groupName, testUrl, verge?.default_latency_test]);
@ -68,8 +68,10 @@ export const ProxyHead = (props: Props) => {
color="inherit"
title={t("Delay check")}
onClick={() => {
console.log(`[ProxyHead] 点击延迟测试按钮,组: ${groupName}`);
// Remind the user that it is custom test url
if (testUrl?.trim() && textState !== "filter") {
console.log(`[ProxyHead] 使用自定义测试URL: ${testUrl}`);
onHeadState({ textState: "url" });
}
props.onCheckDelay();

View File

@ -266,12 +266,31 @@ export const getGroupProxyDelays = async (
timeout: timeout || 10000,
url: url || "http://cp.cloudflare.com/generate_204",
};
const instance = await getAxios();
const result = await instance.get(
`/group/${encodeURIComponent(groupName)}/delay`,
{ params },
console.log(
`[API] 获取代理组延迟,组: ${groupName}, URL: ${params.url}, 超时: ${params.timeout}ms`,
);
return result as any as Record<string, number>;
try {
const instance = await getAxios();
console.log(
`[API] 发送HTTP请求: GET /group/${encodeURIComponent(groupName)}/delay`,
);
const result = await instance.get(
`/group/${encodeURIComponent(groupName)}/delay`,
{ params },
);
console.log(
`[API] 获取代理组延迟成功,组: ${groupName}, 结果数量:`,
Object.keys(result || {}).length,
);
return result as any as Record<string, number>;
} catch (error) {
console.error(`[API] 获取代理组延迟失败,组: ${groupName}`, error);
throw error;
}
};
// Is debug enabled

View File

@ -161,12 +161,42 @@ export async function cmdGetProxyDelay(
timeout: number,
url?: string,
) {
name = encodeURIComponent(name);
return invoke<{ delay: number }>("clash_api_get_proxy_delay", {
name,
url,
timeout,
});
// 确保URL不为空
const testUrl = url || "http://cp.cloudflare.com/generate_204";
console.log(
`[API] 调用延迟测试API代理: ${name}, 超时: ${timeout}ms, URL: ${testUrl}`,
);
try {
name = encodeURIComponent(name);
const result = await invoke<{ delay: number }>(
"clash_api_get_proxy_delay",
{
name,
url: testUrl, // 传递经过验证的URL
timeout,
},
);
// 验证返回结果中是否有delay字段并且值是一个有效的数字
if (result && typeof result.delay === "number") {
console.log(
`[API] 延迟测试API调用成功代理: ${name}, 延迟: ${result.delay}ms`,
);
return result;
} else {
console.error(
`[API] 延迟测试API返回无效结果代理: ${name}, 结果:`,
result,
);
// 返回一个有效的结果对象,但标记为超时
return { delay: 1e6 };
}
} catch (error) {
console.error(`[API] 延迟测试API调用失败代理: ${name}`, error);
// 返回一个有效的结果对象,但标记为错误
return { delay: 1e6 };
}
}
export async function cmdTestDelay(url: string) {

View File

@ -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";
}
}