修复延迟控制
This commit is contained in:
@@ -30,22 +30,28 @@ async function pollLatency() {
|
|||||||
const probeUrl = `${route.url.replace(/\/+$/, '')}/probe?module=${module}&target=${encodeURIComponent(target)}`;
|
const probeUrl = `${route.url.replace(/\/+$/, '')}/probe?module=${module}&target=${encodeURIComponent(target)}`;
|
||||||
|
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
const response = await axios.get(probeUrl, { timeout: 5000 });
|
const response = await axios.get(probeUrl, {
|
||||||
|
timeout: 5000,
|
||||||
|
responseType: 'text',
|
||||||
|
validateStatus: false
|
||||||
|
});
|
||||||
|
|
||||||
|
if (typeof response.data !== 'string') {
|
||||||
|
throw new Error('Response data is not a string');
|
||||||
|
}
|
||||||
|
|
||||||
// 1. Parse prometheus text format for success and specific metrics
|
|
||||||
let latency = null;
|
|
||||||
const lines = response.data.split('\n').map(l => l.trim()).filter(l => l && !l.startsWith('#'));
|
const lines = response.data.split('\n').map(l => l.trim()).filter(l => l && !l.startsWith('#'));
|
||||||
|
|
||||||
// Track success but also try to parse latency anyway as a backup
|
// 1. Check if the probe was successful
|
||||||
let isProbeSuccess = false;
|
let isProbeSuccess = false;
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
if (line.match(/^probe_success(\{.*\})?\s+1/)) {
|
if (/^probe_success(\{.*\})?\s+1/.test(line)) {
|
||||||
isProbeSuccess = true;
|
isProbeSuccess = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try a wider set of potential latency metrics
|
// 2. Extract latency from priority metrics
|
||||||
const targetMetrics = [
|
const targetMetrics = [
|
||||||
'probe_icmp_duration_seconds',
|
'probe_icmp_duration_seconds',
|
||||||
'probe_http_duration_seconds',
|
'probe_http_duration_seconds',
|
||||||
@@ -53,34 +59,50 @@ async function pollLatency() {
|
|||||||
'probe_duration_seconds'
|
'probe_duration_seconds'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
let foundLatency = null;
|
||||||
|
const encodedTarget = target.toLowerCase();
|
||||||
|
|
||||||
for (const metricName of targetMetrics) {
|
for (const metricName of targetMetrics) {
|
||||||
// Match: metric_name{labels} value
|
|
||||||
// Regex handles optional labels and scientific notation
|
|
||||||
const regex = new RegExp(`^${metricName}(?:\\{[^}]*\\})?\\s+([\\d.eE+-]+)`);
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
|
if (!line.startsWith(metricName)) continue;
|
||||||
|
|
||||||
|
// Precise matching for the specific target if multiple targets exist in data
|
||||||
|
// Handles: metric{instance="target"} value OR metric value
|
||||||
|
const regex = new RegExp(`^${metricName}(?:\\{[^}]*\\})?\\s+([\\d.eE+-]+)`);
|
||||||
const match = line.match(regex);
|
const match = line.match(regex);
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
|
// If there are labels, verify they relate to our target (if target label exists)
|
||||||
|
if (line.includes('{')) {
|
||||||
|
const labelsPart = line.substring(line.indexOf('{') + 1, line.lastIndexOf('}')).toLowerCase();
|
||||||
|
// Only check if the labels contain our target to be safe,
|
||||||
|
// though /probe usually only returns one target anyway.
|
||||||
|
if (labelsPart.includes(encodedTarget) || labelsPart.includes('instance') || labelsPart.includes('target')) {
|
||||||
const val = parseFloat(match[1]);
|
const val = parseFloat(match[1]);
|
||||||
if (!isNaN(val)) {
|
if (!isNaN(val)) foundLatency = val * 1000;
|
||||||
latency = val * 1000; // to ms
|
}
|
||||||
break;
|
} else {
|
||||||
|
const val = parseFloat(match[1]);
|
||||||
|
if (!isNaN(val)) foundLatency = val * 1000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (foundLatency !== null) break;
|
||||||
}
|
}
|
||||||
if (latency !== null) break;
|
if (foundLatency !== null) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If probe reported failure but we found a duration, we might still want to show null/error
|
// 3. Final decision
|
||||||
if (!isProbeSuccess && latency !== null) {
|
// If it's a success, use found latency. If success=0 or missing, handle carefully.
|
||||||
// If probe failed, force null to indicate error/offline on UI
|
if (isProbeSuccess && foundLatency !== null) {
|
||||||
|
latency = foundLatency;
|
||||||
|
} else {
|
||||||
|
// If probe failed or metrics missing, do not show 0, show null (Measurement in progress/Error)
|
||||||
latency = null;
|
latency = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save to Valkey
|
// Save to Valkey
|
||||||
await cache.set(`latency:route:${route.id}`, latency, 60);
|
await cache.set(`latency:route:${route.id}`, latency, 60);
|
||||||
// console.log(`[Latency] Route ${route.id} (${target}): ${latency.toFixed(2)}ms`);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// console.error(`[Latency] Error polling route ${route.id}:`, err.message);
|
|
||||||
await cache.set(`latency:route:${route.id}`, null, 60);
|
await cache.set(`latency:route:${route.id}`, null, 60);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user