mirror of
https://github.com/clash-verge-rev/clash-verge-rev
synced 2025-05-05 05:23:44 +08:00
feat: enhance latency test logging and error handling
This commit is contained in:
parent
95e8fe38f2
commit
ca1f7a2b31
@ -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();
|
||||||
});
|
});
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
||||||
|
@ -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) {
|
||||||
|
@ -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";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user