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) => { const handleCheckAll = useLockFn(async (groupName: string) => {
console.log(`[ProxyGroups] 开始测试所有延迟,组: ${groupName}`);
const proxies = renderList const proxies = renderList
.filter( .filter(
(e) => e.group?.name === groupName && (e.type === 2 || e.type === 4), (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!) .flatMap((e) => e.proxyCol || e.proxy!)
.filter(Boolean); .filter(Boolean);
console.log(`[ProxyGroups] 找到代理数量: ${proxies.length}`);
const providers = new Set(proxies.map((p) => p!.provider!).filter(Boolean)); const providers = new Set(proxies.map((p) => p!.provider!).filter(Boolean));
if (providers.size) { if (providers.size) {
console.log(`[ProxyGroups] 发现提供者,数量: ${providers.size}`);
Promise.allSettled( Promise.allSettled(
[...providers].map((p) => providerHealthCheck(p)), [...providers].map((p) => providerHealthCheck(p)),
).then(() => onProxies()); ).then(() => {
console.log(`[ProxyGroups] 提供者健康检查完成`);
onProxies();
});
} }
const names = proxies.filter((p) => !p!.provider).map((p) => p!.name); const names = proxies.filter((p) => !p!.provider).map((p) => p!.name);
console.log(`[ProxyGroups] 过滤后需要测试的代理数量: ${names.length}`);
await Promise.race([ const url = delayManager.getUrl(groupName);
delayManager.checkListDelay(names, groupName, timeout), console.log(`[ProxyGroups] 测试URL: ${url}, 超时: ${timeout}ms`);
getGroupProxyDelays(groupName, delayManager.getUrl(groupName), timeout), // 查询group delays 将清除fixed(不关注调用结果)
]); 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(); onProxies();
}); });

View File

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

View File

@ -266,12 +266,31 @@ export const getGroupProxyDelays = async (
timeout: timeout || 10000, timeout: timeout || 10000,
url: url || "http://cp.cloudflare.com/generate_204", url: url || "http://cp.cloudflare.com/generate_204",
}; };
const instance = await getAxios();
const result = await instance.get( console.log(
`/group/${encodeURIComponent(groupName)}/delay`, `[API] 获取代理组延迟,组: ${groupName}, URL: ${params.url}, 超时: ${params.timeout}ms`,
{ params },
); );
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 // Is debug enabled

View File

@ -161,12 +161,42 @@ export async function cmdGetProxyDelay(
timeout: number, timeout: number,
url?: string, url?: string,
) { ) {
name = encodeURIComponent(name); // 确保URL不为空
return invoke<{ delay: number }>("clash_api_get_proxy_delay", { const testUrl = url || "http://cp.cloudflare.com/generate_204";
name, console.log(
url, `[API] 调用延迟测试API代理: ${name}, 超时: ${timeout}ms, URL: ${testUrl}`,
timeout, );
});
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) { export async function cmdTestDelay(url: string) {

View File

@ -13,11 +13,17 @@ class DelayManager {
private groupListenerMap = new Map<string, () => void>(); private groupListenerMap = new Map<string, () => void>();
setUrl(group: string, url: string) { setUrl(group: string, url: string) {
console.log(`[DelayManager] 设置测试URL组: ${group}, URL: ${url}`);
this.urlMap.set(group, url); this.urlMap.set(group, url);
} }
getUrl(group: string) { 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) { setListener(name: string, group: string, listener: (time: number) => void) {
@ -40,19 +46,25 @@ class DelayManager {
setDelay(name: string, group: string, delay: number) { setDelay(name: string, group: string, delay: number) {
const key = hashKey(name, group); const key = hashKey(name, group);
this.cache.set(key, [Date.now(), delay]); console.log(
this.listenerMap.get(key)?.(delay); `[DelayManager] 设置延迟,代理: ${name}, 组: ${group}, 延迟: ${delay}`,
this.groupListenerMap.get(group)?.(); );
this.cache.set(key, [delay, Date.now()]);
const listener = this.listenerMap.get(key);
if (listener) listener(delay);
} }
getDelay(name: string, group: string) { 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)); // 缓存30分钟
if (result && Date.now() - result[0] <= 18e5) { if (Date.now() - val[1] > 30 * 60 * 1000) {
return result[1]; return -1;
} }
return -1; return val[0];
} }
/// 暂时修复provider的节点延迟排序的问题 /// 暂时修复provider的节点延迟排序的问题
@ -70,13 +82,60 @@ class DelayManager {
} }
async checkDelay(name: string, group: string, timeout: number) { async checkDelay(name: string, group: string, timeout: number) {
console.log(
`[DelayManager] 开始测试延迟,代理: ${name}, 组: ${group}, 超时: ${timeout}ms`,
);
// 先将状态设置为测试中
this.setDelay(name, group, -2);
let delay = -1; let delay = -1;
try { try {
const url = this.getUrl(group); const url = this.getUrl(group);
const result = await cmdGetProxyDelay(name, timeout, url); console.log(`[DelayManager] 调用API测试延迟代理: ${name}, URL: ${url}`);
delay = result.delay;
} catch { // 记录开始时间,用于计算实际延迟
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 delay = 1e6; // error
} }
@ -88,42 +147,78 @@ class DelayManager {
nameList: string[], nameList: string[],
group: string, group: string,
timeout: number, timeout: number,
concurrency = 36 concurrency = 36,
) { ) {
console.log(
`[DelayManager] 批量测试延迟开始,组: ${group}, 数量: ${nameList.length}, 并发数: ${concurrency}`,
);
const names = nameList.filter(Boolean); const names = nameList.filter(Boolean);
// 设置正在延迟测试中 // 设置正在延迟测试中
names.forEach((name) => this.setDelay(name, group, -2)); names.forEach((name) => this.setDelay(name, group, -2));
let total = names.length; let index = 0;
let current = 0; const startTime = Date.now();
const listener = this.groupListenerMap.get(group);
return new Promise((resolve) => { const help = async (): Promise<void> => {
const help = async (): Promise<void> => { const currName = names[index++];
if (current >= concurrency) return; if (!currName) return;
const task = names.shift();
if (!task) return; try {
current += 1; // 确保API调用前状态为测试中
await this.checkDelay(task, group, timeout); this.setDelay(currName, group, -2);
current -= 1;
total -= 1; // 添加一些随机延迟,避免所有请求同时发出和返回
if (total <= 0) resolve(null); if (index > 1) {
else return help(); // 第一个不延迟,保持响应性
}; await new Promise((resolve) =>
for (let i = 0; i < concurrency; ++i) help(); 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) { formatDelay(delay: number, timeout = 10000) {
if (delay <= 0) return "Error"; if (delay === -1) return "-";
if (delay > 1e5) return "Error"; if (delay === -2) return "testing";
if (delay >= timeout) return "Timeout"; // 10s if (delay >= timeout) return "timeout";
return `${delay}`; return `${delay}`;
} }
formatDelayColor(delay: number, timeout = 10000) { formatDelayColor(delay: number, timeout = 10000) {
if (delay < 0) return "";
if (delay >= timeout) return "error.main"; if (delay >= timeout) return "error.main";
if (delay <= 0) return "error.main"; if (delay >= 10000) return "error.main";
if (delay > 500) return "warning.main"; if (delay >= 400) return "warning.main";
if (delay >= 250) return "primary.main";
return "success.main"; return "success.main";
} }
} }