157 lines
4.3 KiB
JavaScript
157 lines
4.3 KiB
JavaScript
import { io } from 'socket.io-client';
|
|
|
|
class MetricsService {
|
|
constructor(baseUrl) {
|
|
this.baseUrl = baseUrl || window.location.origin;
|
|
this.socket = null;
|
|
this.subscriptions = new Map();
|
|
this.pendingRequests = new Map();
|
|
window.addEventListener('beforeunload', this.cleanupAll.bind(this));
|
|
window.addEventListener('pagehide', this.cleanupAll.bind(this));
|
|
|
|
window.addEventListener('beforeunload', () => {
|
|
this.cleanupAll();
|
|
});
|
|
}
|
|
|
|
connectWebSocket() {
|
|
if (this.socket) {
|
|
console.log('WebSocket already exists');
|
|
return;
|
|
}
|
|
|
|
console.log('Connecting WebSocket...');
|
|
this.socket = io(`${this.baseUrl.replace('http', 'ws')}/ws`, {
|
|
transports: ['websocket'],
|
|
withCredentials: true,
|
|
});
|
|
|
|
this.socket.on('connect', () => {
|
|
console.log('WebSocket connected');
|
|
// Восстанавливаем подписки при переподключении
|
|
this.subscriptions.forEach((_, metricKey) => {
|
|
const [metric, query] = metricKey.split('?');
|
|
const filters = this.parseFiltersFromKey(metricKey);
|
|
this.socket.emit('subscribe-metric', { metric, filters });
|
|
});
|
|
});
|
|
|
|
this.socket.on('disconnect', () => {
|
|
console.log('WebSocket disconnected');
|
|
this.socket = null;
|
|
});
|
|
|
|
this.socket.on('metrics-data', ({ metric, data, requestId }) => {
|
|
if (requestId && this.pendingRequests.has(requestId)) {
|
|
const { resolve } = this.pendingRequests.get(requestId);
|
|
resolve(data);
|
|
this.pendingRequests.delete(requestId);
|
|
return;
|
|
}
|
|
|
|
const callbacks = this.subscriptions.get(metric) || [];
|
|
callbacks.forEach(cb => cb(data));
|
|
});
|
|
|
|
this.socket.on('metrics-error', ({ error, requestId }) => {
|
|
if (requestId && this.pendingRequests.has(requestId)) {
|
|
const { reject } = this.pendingRequests.get(requestId);
|
|
reject(new Error(error));
|
|
this.pendingRequests.delete(requestId);
|
|
}
|
|
});
|
|
}
|
|
|
|
async fetchMetricsRange(metric, start, end, step = 15, filters = {}) {
|
|
return new Promise((resolve, reject) => {
|
|
this.connectWebSocket();
|
|
|
|
const requestId = `range-${Date.now()}`;
|
|
this.pendingRequests.set(requestId, { resolve, reject });
|
|
|
|
this.socket.emit('get-metrics', {
|
|
metric,
|
|
start,
|
|
end,
|
|
step,
|
|
filters,
|
|
isRangeQuery: true,
|
|
requestId
|
|
});
|
|
|
|
setTimeout(() => {
|
|
if (this.pendingRequests.has(requestId)) {
|
|
reject(new Error('Request timeout'));
|
|
this.pendingRequests.delete(requestId);
|
|
}
|
|
}, 30000);
|
|
});
|
|
}
|
|
|
|
subscribeToMetric(metricKey, callback, interval = 5000, filters = {}) {
|
|
this.connectWebSocket();
|
|
|
|
if (!this.subscriptions.has(metricKey)) {
|
|
this.subscriptions.set(metricKey, []);
|
|
const [metric] = metricKey.split('?');
|
|
this.socket.emit('subscribe-metric', {
|
|
metric,
|
|
interval,
|
|
filters
|
|
});
|
|
}
|
|
|
|
const callbacks = this.subscriptions.get(metricKey);
|
|
callbacks.push(callback);
|
|
|
|
return () => {
|
|
this.unsubscribeFromMetric(metricKey, callback);
|
|
};
|
|
}
|
|
|
|
unsubscribeFromMetric(metricKey, callback) {
|
|
const callbacks = this.subscriptions.get(metricKey) || [];
|
|
const filtered = callbacks.filter(cb => cb !== callback);
|
|
|
|
if (filtered.length === 0) {
|
|
this.subscriptions.delete(metricKey);
|
|
if (this.socket && this.socket.connected) {
|
|
const [metric] = metricKey.split('?');
|
|
this.socket.emit('unsubscribe-metric', { metric });
|
|
}
|
|
} else {
|
|
this.subscriptions.set(metricKey, filtered);
|
|
}
|
|
}
|
|
|
|
parseFiltersFromKey(metricKey) {
|
|
const parts = metricKey.split('?');
|
|
if (parts.length < 2) return {};
|
|
|
|
return parts[1].split('&').reduce((acc, pair) => {
|
|
const [key, value] = pair.split('=');
|
|
if (key && value) acc[key] = value;
|
|
return acc;
|
|
}, {});
|
|
}
|
|
|
|
cleanupAll() {
|
|
if (this.socket && this.socket.connected) {
|
|
this.socket.emit('unsubscribe-all');
|
|
}
|
|
this.subscriptions.clear();
|
|
this.disconnectWebSocket();
|
|
}
|
|
|
|
disconnectWebSocket() {
|
|
if (this.socket) {
|
|
this.socket.disconnect();
|
|
this.socket = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Создаем экземпляр сервиса
|
|
const metricsService = new MetricsService();
|
|
|
|
export default metricsService; |