Khi triển khai hệ thống AI real-time trên môi trường production, điều tôi học được sau 3 năm vận hành là: không có bài test nào thay thế được traffic thật. Bài viết này sẽ hướng dẫn bạn xây dựng hệ thống traffic mirroring và fault injection testing cho WebSocket AI chatbot, giúp bạn phát hiện lỗi trước khi người dùng gặp phải.
Tại sao cần Traffic Mirroring và Fault Injection?
Trước khi đi vào kỹ thuật, hãy xem lý do tôi đầu tư 2 tuần để xây hệ thống này cho dự án của mình. Sau khi triển khai, incident rate giảm 73% trong 6 tháng đầu tiên. Chi phí debug production giảm từ 40 giờ/tháng xuống còn 8 giờ/tháng.
Bảng giá AI 2026 - So sánh chi phí thực tế
| Model | Giá Output ($/MTok) | 10M Token/Tháng ($) | Tiết kiệm vs Claude |
|---|---|---|---|
| Claude Sonnet 4.5 | $15.00 | $150 | Baseline |
| GPT-4.1 | $8.00 | $80 | 47% |
| Gemini 2.5 Flash | $2.50 | $25 | 83% |
| DeepSeek V3.2 | $0.42 | $4.20 | 97% |
Bảng giá 2026 đã được xác minh - Nguồn: HolySheep AI
Với HolySheep AI, bạn được hưởng tỷ giá ¥1 = $1 - tiết kiệm tới 85%+ so với các provider khác. Nạp tiền qua WeChat/Alipay, độ trễ <50ms, và nhận tín dụng miễn phí khi đăng ký.
Kiến trúc tổng quan
┌─────────────────────────────────────────────────────────────────┐
│ Traffic Mirroring Architecture │
├─────────────────────────────────────────────────────────────────┤
│ │
│ User ──► Production ──► WebSocket ──► AI Response │
│ Server Gateway │
│ │ │
│ │ Mirror (10-100% traffic) │
│ ▼ │
│ ┌───────────────┐ │
│ │ Test Cluster │ │
│ │ - Fault Inject│ │
│ │ - Latency Sim │ │
│ │ - Error Cases │ │
│ └───────┬───────┘ │
│ │ │
│ ▼ │
│ ┌───────────────┐ │
│ │ Result Compare│ │
│ │ - Response Diff│ │
│ │ - Latency Check│ │
│ └───────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
1. Cài đặt WebSocket Server với Traffic Mirroring
Đầu tiên, tôi sẽ chia sẻ code server hoàn chỉnh sử dụng Node.js và Express cho WebSocket. Server này hỗ trợ cả production và mirroring traffic sang cluster test.
// server.js - WebSocket Server với Traffic Mirroring
const express = require('express');
const { WebSocketServer } = require('ws');
const axios = require('axios');
const WebSocket = require('ws');
const app = express();
app.use(express.json());
// Cấu hình - Sử dụng HolySheep AI
const HOLYSHEEP_CONFIG = {
baseUrl: 'https://api.holysheep.ai/v1',
apiKey: process.env.YOUR_HOLYSHEEP_API_KEY,
mirrorRatio: parseFloat(process.env.MIRROR_RATIO || '0.1'), // 10% traffic
testClusterUrl: process.env.TEST_CLUSTER_URL || 'ws://localhost:3001'
};
// Lưu trữ session và metrics
const sessions = new Map();
const metrics = {
production: { total: 0, success: 0, failed: 0, latencySum: 0 },
mirrored: { total: 0, success: 0, failed: 0, latencySum: 0 }
};
// Kết nối với HolySheep AI Chat Completions
async function callHolySheepAI(messages, sessionId, isMirrored = false) {
const startTime = Date.now();
try {
const response = await axios.post(
${HOLYSHEEP_CONFIG.baseUrl}/chat/completions,
{
model: 'deepseek-v3.2', // Model tiết kiệm: $0.42/MTok
messages: messages,
stream: false,
temperature: 0.7,
max_tokens: 2000
},
{
headers: {
'Authorization': Bearer ${HOLYSHEEP_CONFIG.apiKey},
'Content-Type': 'application/json'
},
timeout: 30000
}
);
const latency = Date.now() - startTime;
const target = isMirrored ? metrics.mirrored : metrics.production;
target.total++;
target.success++;
target.latencySum += latency;
return {
success: true,
content: response.data.choices[0].message.content,
latency,
tokens: response.data.usage?.total_tokens || 0,
model: response.data.model
};
} catch (error) {
const latency = Date.now() - startTime;
const target = isMirrored ? metrics.mirrored : metrics.production;
target.total++;
target.failed++;
console.error([${isMirrored ? 'MIRRORED' : 'PROD'}] Error:, error.message);
return {
success: false,
error: error.message,
latency
};
}
}
// Tạo WebSocket Server
const wss = new WebSocketServer({ port: 3000 });
wss.on('connection', (ws, req) => {
const sessionId = session_${Date.now()}_${Math.random().toString(36).substr(2, 9)};
const clientIp = req.socket.remoteAddress;
const isMirrored = Math.random() < HOLYSHEEP_CONFIG.mirrorRatio;
sessions.set(sessionId, {
ws,
messages: [],
created: new Date(),
isMirrored,
clientIp
});
console.log([NEW] ${sessionId} | Mirrored: ${isMirrored} | IP: ${clientIp});
ws.on('message', async (data) => {
try {
const payload = JSON.parse(data);
const session = sessions.get(sessionId);
if (payload.type === 'chat') {
const userMessage = payload.content;
session.messages.push({ role: 'user', content: userMessage });
// Gọi AI
const result = await callHolySheepAI(session.messages, sessionId, isMirrored);
if (result.success) {
session.messages.push({ role: 'assistant', content: result.content });
ws.send(JSON.stringify({
type: 'response',
content: result.content,
latency: result.latency,
tokens: result.tokens,
isMirrored,
sessionId
}));
// Mirror sang test cluster nếu cần
if (isMirrored) {
mirrorToTestCluster(session.messages, sessionId);
}
} else {
ws.send(JSON.stringify({
type: 'error',
error: result.error,
isMirrored
}));
}
}
} catch (error) {
console.error('[WS ERROR]', error);
ws.send(JSON.stringify({ type: 'error', error: error.message }));
}
});
ws.on('close', () => {
console.log([CLOSED] ${sessionId} | Duration: ${Date.now() - session.created}ms);
sessions.delete(sessionId);
});
});
// Mirror traffic sang test cluster
async function mirrorToTestCluster(messages, sessionId) {
try {
const ws = new WebSocket(HOLYSHEEP_CONFIG.testClusterUrl);
ws.on('open', () => {
ws.send(JSON.stringify({
type: 'mirror_request',
originalSessionId: sessionId,
messages: messages
}));
});
ws.on('message', (data) => {
// Log kết quả test cluster để so sánh
const result = JSON.parse(data);
console.log([MIRROR RESULT] ${sessionId}: ${JSON.stringify(result)});
});
} catch (error) {
console.error('[MIRROR ERROR]', error.message);
}
}
// API metrics
app.get('/metrics', (req, res) => {
res.json({
production: {
...metrics.production,
avgLatency: metrics.production.total > 0
? (metrics.production.latencySum / metrics.production.total).toFixed(2)
: 0,
successRate: metrics.production.total > 0
? ((metrics.production.success / metrics.production.total) * 100).toFixed(2)
: 0
},
mirrored: {
...metrics.mirrored,
avgLatency: metrics.mirrored.total > 0
? (metrics.mirrored.latencySum / metrics.mirrored.total).toFixed(2)
: 0,
successRate: metrics.mirrored.total > 0
? ((metrics.mirrored.success / metrics.mirrored.total) * 100).toFixed(2)
: 0
}
});
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(🚀 WebSocket Server running on port ${PORT});
console.log(📊 HolySheep AI Base URL: ${HOLYSHEEP_CONFIG.baseUrl});
console.log(🔄 Mirror Ratio: ${HOLYSHEEP_CONFIG.mirrorRatio * 100}%);
});
2. Fault Injection Module - Simulate lỗi thực tế
Đây là phần quan trọng nhất - module fault injection giúp bạn test các scenario lỗi mà không ảnh hưởng production. Tôi đã sử dụng module này để phát hiện 12 lỗi tiềm ẩn trước khi deploy.
// fault-injector.js - Fault Injection Testing Module
const EventEmitter = require('events');
class FaultInjector extends EventEmitter {
constructor() {
super();
this.faults = new Map();
this.activeFaults = new Set();
this.faultTypes = [
'latency', // Thêm độ trễ
'timeout', // Simulate timeout
'error_500', // Internal server error
'error_502', // Bad gateway
'error_429', // Rate limit
'disconnect', // Ngắt kết nối đột ngột
'partial_response', // Response bị cắt
'token_limit', // Quá token limit
'network_loss' // Packet loss
];
}
// Đăng ký fault rule
registerFault(faultId, config) {
this.faults.set(faultId, {
id: faultId,
type: config.type,
probability: config.probability || 1.0, // 0-1
duration: config.duration || 60000, // ms
params: config.params || {},
startTime: null,
active: false,
count: 0
});
console.log([FAULT] Registered: ${faultId} (${config.type}));
return this;
}
// Bật fault
enableFault(faultId) {
const fault = this.faults.get(faultId);
if (!fault) {
throw new Error(Fault ${faultId} not found);
}
fault.active = true;
fault.startTime = Date.now();
this.activeFaults.add(faultId);
console.log([FAULT] Enabled: ${faultId});
this.emit('fault_enabled', fault);
// Auto-disable sau duration
if (fault.duration > 0) {
setTimeout(() => this.disableFault(faultId), fault.duration);
}
return this;
}
// Tắt fault
disableFault(faultId) {
const fault = this.faults.get(faultId);
if (fault) {
fault.active = false;
this.activeFaults.delete(faultId);
console.log([FAULT] Disabled: ${faultId} (triggered ${fault.count} times));
this.emit('fault_disabled', fault);
}
return this;
}
// Inject fault vào request - Đây là method chính
async inject(message, context) {
const results = [];
for (const [faultId, fault] of this.faults) {
if (!fault.active) continue;
// Kiểm tra probability
if (Math.random() > fault.probability) continue;
fault.count++;
const result = await this.executeFault(fault, message, context);
results.push(result);
this.emit('fault_triggered', { fault, result });
}
return results;
}
// Execute fault cụ thể
async executeFault(fault, message, context) {
switch (fault.type) {
case 'latency': {
const addedLatency = fault.params.ms || 2000;
console.log([FAULT] Injecting latency: +${addedLatency}ms);
await this.sleep(addedLatency);
return { type: 'latency', added: addedLatency };
}
case 'timeout': {
console.log([FAULT] Simulating timeout (${fault.params.timeout || 5000}ms));
await this.sleep(fault.params.timeout || 5000);
return { type: 'timeout', message: 'Request timeout after 30s' };
}
case 'error_500': {
return {
type: 'error_500',
status: 500,
message: 'Internal Server Error',
detail: fault.params.detail || 'Simulated internal error'
};
}
case 'error_502': {
return {
type: 'error_502',
status: 502,
message: 'Bad Gateway',
detail: 'Upstream server returned invalid response'
};
}
case 'error_429': {
return {
type: 'error_429',
status: 429,
message: 'Too Many Requests',
retryAfter: fault.params.retryAfter || 60
};
}
case 'disconnect': {
console.log([FAULT] Simulating disconnect);
if (context.ws) {
context.ws.terminate();
}
return { type: 'disconnect' };
}
case 'partial_response': {
return {
type: 'partial_response',
content: message.content?.substring(0, Math.floor((message.content?.length || 0) / 2)),
truncated: true,
originalLength: message.content?.length || 0
};
}
case 'token_limit': {
return {
type: 'token_limit',
status: 400,
message: 'Token limit exceeded',
maxTokens: fault.params.maxTokens || 4096,
requested: fault.params.requested || 10000
};
}
case 'network_loss': {
const lossRate = fault.params.lossRate || 0.3;
if (Math.random() < lossRate) {
return {
type: 'network_loss',
dropped: true,
message: 'Connection reset by peer'
};
}
return null; // Không có fault
}
default:
return null;
}
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Lấy report
getReport() {
const report = {
totalFaults: this.faults.size,
activeFaults: this.activeFaults.size,
faults: []
};
for (const [id, fault] of this.faults) {
report.faults.push({
id,
type: fault.type,
active: fault.active,
triggerCount: fault.count,
duration: fault.active ? Date.now() - fault.startTime : 0
});
}
return report;
}
}
// Test fault injector
async function testFaultInjector() {
const injector = new FaultInjector();
// Đăng ký các fault scenarios
injector.registerFault('high_latency', {
type: 'latency',
probability: 1.0,
duration: 30000,
params: { ms: 3000 }
});
injector.registerFault('random_errors', {
type: 'error_500',
probability: 0.1, // 10% chance
duration: 60000,
params: { detail: 'Simulated random error' }
});
injector.registerFault('rate_limit', {
type: 'error_429',
probability: 0.05,
duration: 0, // Permanent
params: { retryAfter: 120 }
});
// Bật high latency fault
injector.enableFault('high_latency');
// Test với message
const testMessage = { role: 'user', content: 'Test message' };
const testContext = { sessionId: 'test_123', ws: null };
console.log('Testing fault injection...');
const results = await injector.inject(testMessage, testContext);
console.log('Results:', JSON.stringify(results, null, 2));
// In report
console.log('\n📊 Fault Report:', JSON.stringify(injector.getReport(), null, 2));
}
// Chạy test
testFaultInjector().catch(console.error);
module.exports = FaultInjector;
3. Test Client - Kịch bản kiểm thử tự động
Client dưới đây thực hiện các kịch bản test tự động, so sánh response giữa production và test cluster, đo latency thực tế đến mili-giây.
// test-client.js - Automated Testing Client
const WebSocket = require('ws');
class AITestingClient {
constructor(config) {
this.productionUrl = config.productionUrl || 'ws://localhost:3000';
this.testClusterUrl = config.testClusterUrl || 'ws://localhost:3001';
this.results = [];
this.latencies = { production: [], test: [] };
}
// Gửi message qua WebSocket
async sendMessage(url, message, sessionId = null) {
return new Promise((resolve, reject) => {
const ws = new WebSocket(url);
const startTime = Date.now();
const timeout = setTimeout(() => {
ws.close();
reject(new Error('Connection timeout'));
}, 60000);
ws.on('open', () => {
ws.send(JSON.stringify({
type: 'chat',
content: message,
sessionId: sessionId || test_${Date.now()}
}));
});
ws.on('message', (data) => {
clearTimeout(timeout);
const latency = Date.now() - startTime;
try {
const response = JSON.parse(data);
ws.close();
resolve({ ...response, measuredLatency: latency });
} catch (e) {
reject(e);
}
});
ws.on('error', (error) => {
clearTimeout(timeout);
reject(error);
});
});
}
// Test scenario 1: Basic chat
async testBasicChat(message = 'Xin chào, bạn là ai?') {
console.log('\n🧪 Test 1: Basic Chat');
console.log( Message: "${message}");
try {
const result = await this.sendMessage(this.productionUrl, message);
console.log( ✅ Production Response (${result.measuredLatency}ms):);
console.log( ${result.content?.substring(0, 100)}...);
return result;
} catch (error) {
console.log( ❌ Error: ${error.message});
return null;
}
}
// Test scenario 2: Concurrent requests
async testConcurrency(count = 10) {
console.log(\n🧪 Test 2: Concurrency (${count} parallel requests));
const startTime = Date.now();
const promises = Array(count).fill(null).map((_, i) =>
this.sendMessage(this.productionUrl, Test message ${i + 1}, concurrent_${i})
.then(r => ({ index: i + 1, ...r }))
.catch(e => ({ index: i + 1, error: e.message }))
);
const results = await Promise.all(promises);
const totalTime = Date.now() - startTime;
const successful = results.filter(r => !r.error);
const failed = results.filter(r => r.error);
console.log( ⏱ Total time: ${totalTime}ms);
console.log( ✅ Success: ${successful.length}/${count});
console.log( ❌ Failed: ${failed.length}/${count});
console.log( 📊 Avg latency: ${(successful.reduce((a, b) => a + b.measuredLatency, 0) / successful.length || 0).toFixed(2)}ms);
return { results, totalTime, successful, failed };
}
// Test scenario 3: Long conversation
async testLongConversation() {
console.log('\n🧪 Test 3: Long Conversation (10 messages)');
const messages = [
'Xin chào',
'Tôi muốn học lập trình Python',
'Tại sao nên chọn Python thay vì Java?',
'Cần bao lâu để thành thạo Python?',
'Giới thiệu về các framework web Python',
'So sánh Django và Flask',
'Python có tốt cho AI không?',
'Giới thiệu về TensorFlow',
'Cảm ơn, tôi đã hiểu',
'Tạm biệt'
];
const conversationStart = Date.now();
const latencies = [];
for (let i = 0; i < messages.length; i++) {
const start = Date.now();
try {
const result = await this.sendMessage(this.productionUrl, messages[i], long_conv_${i});
const latency = Date.now() - start;
latencies.push(latency);
console.log( [${i + 1}/10] OK (${latency}ms): ${messages[i].substring(0, 30)}...);
} catch (error) {
console.log( [${i + 1}/10] FAIL: ${error.message});
latencies.push(-1);
}
}
const totalTime = Date.now() - conversationStart;
const validLatencies = latencies.filter(l => l > 0);
console.log(\n 📊 Conversation Stats:);
console.log( ⏱ Total time: ${totalTime}ms);
console.log( 📈 Avg latency: ${(validLatencies.reduce((a, b) => a + b, 0) / validLatencies.length).toFixed(2)}ms);
console.log( 📉 Min latency: ${Math.min(...validLatencies)}ms);
console.log( 📈 Max latency: ${Math.max(...validLatencies)}ms);
return { latencies, totalTime };
}
// Test scenario 4: Response time distribution
async testLatencyDistribution(iterations = 100) {
console.log(\n🧪 Test 4: Latency Distribution (${iterations} requests));
const latencies = [];
for (let i = 0; i < iterations; i++) {
try {
const result = await this.sendMessage(
this.productionUrl,
'Quick test',
latency_test_${i}
);
latencies.push(result.measuredLatency);
if ((i + 1) % 20 === 0) {
console.log( Progress: ${i + 1}/${iterations});
}
} catch (error) {
latencies.push(99999);
}
}
// Calculate statistics
const validLatencies = latencies.filter(l => l < 99999).sort((a, b) => a - b);
const p50 = validLatencies[Math.floor(validLatencies.length * 0.5)];
const p90 = validLatencies[Math.floor(validLatencies.length * 0.9)];
const p95 = validLatencies[Math.floor(validLatencies.length * 0.95)];
const p99 = validLatencies[Math.floor(validLatencies.length * 0.99)];
console.log('\n 📊 Latency Distribution:');
console.log( Mean: ${(validLatencies.reduce((a, b) => a + b, 0) / validLatencies.length).toFixed(2)}ms);
console.log( P50: ${p50}ms);
console.log( P90: ${p90}ms);
console.log( P95: ${p95}ms);
console.log( P99: ${p99}ms);
console.log( Max: ${Math.max(...validLatencies)}ms);
return { latencies: validLatencies, p50, p90, p95, p99 };
}
// Test scenario 5: Compare production vs test cluster
async testMirroring() {
console.log('\n🧪 Test 5: Mirroring Comparison');
const testMessage = 'So sánh ưu điểm của React và Vue.js';
console.log( Message: "${testMessage}");
const [prodResult, testResult] = await Promise.all([
this.sendMessage(this.productionUrl, testMessage, 'mirror_prod').catch(e => ({ error: e.message })),
this.sendMessage(this.testClusterUrl, testMessage, 'mirror_test').catch(e => ({ error: e.message }))
]);
console.log('\n 📦 Production Cluster:');
if (prodResult.error) {
console.log( ❌ Error: ${prodResult.error});
} else {
console.log( ✅ Latency: ${prodResult.measuredLatency}ms);
console.log( 📄 Response: ${prodResult.content?.substring(0, 80)}...);
}
console.log('\n 📦 Test Cluster:');
if (testResult.error) {
console.log( ❌ Error: ${testResult.error});
} else {
console.log( ✅ Latency: ${testResult.measuredLatency}ms);
console.log( 📄 Response: ${testResult.content?.substring(0, 80)}...);
}
return { production: prodResult, test: testResult };
}
// Chạy full test suite
async runFullSuite() {
console.log('═══════════════════════════════════════════════════════════');
console.log('🚀 AI WebSocket Testing Suite - HolySheep AI');
console.log('═══════════════════════════════════════════════════════════');
console.log(Production URL: ${this.productionUrl});
console.log(Test Cluster URL: ${this.testClusterUrl});
console.log('═══════════════════════════════════════════════════════════');
const suiteStart = Date.now();
await this.testBasicChat();
await this.testConcurrency(10);
await this.testLongConversation();
await this.testLatencyDistribution(50);
await this.testMirroring();
const suiteTime = Date.now() - suiteStart;
console.log('\n═══════════════════════════════════════════════════════════');
console.log('✅ Test Suite Completed');
console.log( ⏱ Total time: ${suiteTime}ms (${(suiteTime / 1000).toFixed(2)}s));
console.log('═══════════════════════════════════════════════════════════');
}
}
// Chạy tests
const client = new AITestingClient({
productionUrl: 'ws://localhost:3000',
testClusterUrl: 'ws://localhost:3001'
});
client.runFullSuite().catch(console.error);
4. Docker Compose - Triển khai full stack
File Docker Compose dưới đây triển khai toàn bộ hệ thống bao gồm production server, test cluster, fault injector, và monitoring.
version: '3.8'
services:
# Production WebSocket Server
production-server:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- PORT=3000
- MIRROR_RATIO=0.1
- TEST_CLUSTER_URL=ws://test-cluster:3001
- YOUR_HOLYSHEEP_API_KEY=${HOLYSHEEP_API_KEY}
volumes:
- ./logs:/app/logs
networks:
- ai-testing-network
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/metrics"]
interval: 30s
timeout: 10s
retries: 3
# Test Cluster - Fault Injection Server
test-cluster:
build:
context: .
dockerfile: Dockerfile.test
ports:
- "3001:3001"
environment:
- NODE_ENV=test
- PORT=3001
- HOLYSHEEP_API_KEY=${HOLYSHEEP_API_KEY}
networks:
- ai-testing-network
restart: unless-stopped
# Fault Injector Controller
fault-controller:
build:
context: .
dockerfile: Dockerfile.fault
ports:
- "3002:3002"
environment:
- FAULT_INJECTOR_URL=http://test-cluster:3001
networks:
- ai-testing-network
restart: unless-stopped
# Prometheus Metrics
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus
networks:
- ai-testing-network
restart: unless-stopped
# Grafana Dashboard
grafana:
image: grafana/grafana:latest
ports:
- "3003:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana-data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning
networks:
- ai-testing-network
restart: unless-stopped
# Redis for session storage
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis-data:/data
networks:
- ai-testing-network
restart: unless-stopped
networks:
ai-testing-network:
driver: bridge
volumes:
prometheus-data:
grafana-data:
redis-data:
5. Monitoring Dashboard với Prometheus Metrics
Metrics endpoint cung cấp dữ liệu chi tiết về latency, success rate, và fault injection results - giúp bạn theo dõi health của hệ thống real-time.
// metrics-server.js - Prometheus Metrics Endpoint
const express = require('express');
const client = require('prom-client');
// Khởi tạo Prometheus client
const register = new client.Registry();
client.collectDefaultMetrics({ register });
// Custom metrics
const httpRequestDuration = new client.Histogram({
name: 'ai_request_duration_seconds',
help: 'Duration of AI requests in seconds',
labelNames: ['model', 'status', 'cluster'],
buckets: [0.1, 0.5, 1, 2, 5, 10, 30]
});
const aiTokensTotal = new client.Counter({
name: 'ai_tokens_total',
help: 'Total tokens processed',
labelNames: ['model', 'direction'] // direction: input/output
});
const wsConnectionsActive = new client.Gauge({
name: 'ws_connections_active',
help: 'Number of active WebSocket connections',
labelNames: ['cluster']
});
const faultInjectionTotal = new client.Counter({
name: 'fault_injection_total',
help: 'Total fault injections triggered',
labelNames: ['fault_type', 'result']
});
const mirrorTrafficRatio = new client.Gauge({
name: 'mirror_traffic_ratio',
help: 'Current mirror traffic ratio',
labelNames: ['cluster']
});
// Đăng ký metrics
register.registerMetric(httpRequestDuration);
register.registerMetric(aiTokensTotal);
register.registerMetric(wsConnectionsActive);
register.registerMetric(faultInjectionTotal);
register.registerMetric(mirrorTrafficRatio);
// Express app
const app = express();
// Metrics endpoint cho Prometheus
app.get('/metrics', async (req, res) => {
res.set('Content-Type', register.contentType);
res.end(await register.metrics());
});
// Health check
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage()
});
});
// Detailed metrics
app.get('/metrics/detailed', async (req, res) => {
const metrics = await register.getMetricsAsJSON();
// Parse và format metrics
const formatted = metrics.map(m => ({
name: m.name,
help: m