Files
server-mv/ebpf-sockmap/docker/app/server.js
T

220 lines
7.8 KiB
JavaScript

// ─────────────────────────────────────────────────────────
// server.js — Servidor Node.js de demonstração
//
// Endpoints:
// GET /health → Health check
// GET / → Página principal com info
// GET /mysql/users → Lista usuários do MySQL
// GET /postgres/products→ Lista produtos do PostgreSQL
// GET /benchmark → Teste de latência para ambos os DBs
//
// Este servidor se conecta ao MySQL e PostgreSQL.
// Com o eBPF sockmap ativo, a comunicação com os bancos
// é acelerada (dados redirecionados no nível do socket).
// ─────────────────────────────────────────────────────────
const http = require('http');
const url = require('url');
// ─── Configuração MySQL ───
const mysql = require('mysql2/promise');
const mysqlConfig = {
host: process.env.MYSQL_HOST || 'mysql',
port: parseInt(process.env.MYSQL_PORT || '3306'),
user: process.env.MYSQL_USER || 'app',
password: process.env.MYSQL_PASSWORD || 'app_password',
database: process.env.MYSQL_DATABASE || 'app_db',
waitForConnections: true,
connectionLimit: 10,
};
// ─── Configuração PostgreSQL ───
const { Pool } = require('pg');
const pgPool = new Pool({
host: process.env.POSTGRES_HOST || 'postgres',
port: parseInt(process.env.POSTGRES_PORT || '5432'),
user: process.env.POSTGRES_USER || 'app',
password: process.env.POSTGRES_PASSWORD || 'app_password',
database: process.env.POSTGRES_DATABASE || 'app_db',
max: 10,
});
let mysqlPool;
// ─── Inicializa conexão MySQL ───
async function initMySQL() {
try {
mysqlPool = mysql.createPool(mysqlConfig);
const [rows] = await mysqlPool.query('SELECT 1');
console.log('✅ MySQL conectado!');
} catch (err) {
console.error('❌ Erro ao conectar no MySQL:', err.message);
setTimeout(initMySQL, 3000);
}
}
// ─── Inicializa conexão PostgreSQL ───
async function initPostgres() {
try {
const res = await pgPool.query('SELECT 1');
console.log('✅ PostgreSQL conectado!');
} catch (err) {
console.error('❌ Erro ao conectar no PostgreSQL:', err.message);
setTimeout(initPostgres, 3000);
}
}
// ─── Mede o tempo de execução de uma query (em ms) ───
async function measureQuery(fn) {
const start = process.hrtime.bigint();
const result = await fn();
const end = process.hrtime.bigint();
const durationMs = Number(end - start) / 1_000_000;
return { result, durationMs };
}
// ─── Handlers de rotas ───
const routes = {
// Health check
'/health': async () => ({
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
}),
// Página principal
'/': async () => ({
app: 'eBPF Sockmap Demo',
message: 'Servidor Node.js funcionando! 🚀',
endpoints: {
'/health': 'Health check',
'/mysql/users': 'Listar usuários (MySQL)',
'/postgres/products': 'Listar produtos (PostgreSQL)',
'/benchmark': 'Benchmark de latência nos dois bancos',
},
info: {
description: 'Com eBPF sockmap ativo, a comunicação entre este servidor e os bancos é acelerada.',
como_funciona: 'Os dados são redirecionados diretamente entre sockets, pulando a pilha TCP/IP do kernel.',
},
}),
// Listar usuários do MySQL
'/mysql/users': async () => {
const { result, durationMs } = await measureQuery(async () => {
const [rows] = await mysqlPool.query('SELECT * FROM users');
return rows;
});
return {
source: 'MySQL',
query_time_ms: durationMs.toFixed(3),
count: result.length,
data: result,
};
},
// Listar produtos do PostgreSQL
'/postgres/products': async () => {
const { result, durationMs } = await measureQuery(async () => {
const res = await pgPool.query('SELECT * FROM products');
return res.rows;
});
return {
source: 'PostgreSQL',
query_time_ms: durationMs.toFixed(3),
count: result.length,
data: result,
};
},
// Benchmark: faz várias queries e mede a latência
'/benchmark': async () => {
const iterations = 100;
const results = {
mysql: { times: [], avg: 0, min: Infinity, max: 0 },
postgres: { times: [], avg: 0, min: Infinity, max: 0 },
};
// Benchmark MySQL
for (let i = 0; i < iterations; i++) {
const { durationMs } = await measureQuery(async () => {
const [rows] = await mysqlPool.query('SELECT 1');
return rows;
});
results.mysql.times.push(durationMs);
results.mysql.min = Math.min(results.mysql.min, durationMs);
results.mysql.max = Math.max(results.mysql.max, durationMs);
}
results.mysql.avg = results.mysql.times.reduce((a, b) => a + b, 0) / iterations;
// Benchmark PostgreSQL
for (let i = 0; i < iterations; i++) {
const { durationMs } = await measureQuery(async () => {
await pgPool.query('SELECT 1');
});
results.postgres.times.push(durationMs);
results.postgres.min = Math.min(results.postgres.min, durationMs);
results.postgres.max = Math.max(results.postgres.max, durationMs);
}
results.postgres.avg = results.postgres.times.reduce((a, b) => a + b, 0) / iterations;
// Limpa arrays de tempos individuais
delete results.mysql.times;
delete results.postgres.times;
return {
benchmark: `${iterations} queries SELECT 1 em cada banco`,
dica: 'Compare os tempos com e sem eBPF sockmap!',
mysql: {
avg_ms: results.mysql.avg.toFixed(3),
min_ms: results.mysql.min.toFixed(3),
max_ms: results.mysql.max.toFixed(3),
},
postgres: {
avg_ms: results.postgres.avg.toFixed(3),
min_ms: results.postgres.min.toFixed(3),
max_ms: results.postgres.max.toFixed(3),
},
};
},
};
// ─── Servidor HTTP ───
const server = http.createServer(async (req, res) => {
const pathname = url.parse(req.url).pathname;
const handler = routes[pathname];
res.setHeader('Content-Type', 'application/json; charset=utf-8');
if (!handler) {
res.writeHead(404);
res.end(JSON.stringify({ error: 'Rota não encontrada', path: pathname }));
return;
}
try {
const data = await handler();
res.writeHead(200);
res.end(JSON.stringify(data, null, 2));
} catch (err) {
console.error(`Erro em ${pathname}:`, err.message);
res.writeHead(500);
res.end(JSON.stringify({ error: err.message }));
}
});
// ─── Inicialização ───
const PORT = 3000;
async function main() {
console.log('🔧 Iniciando servidor Node.js...');
await initMySQL();
await initPostgres();
server.listen(PORT, '0.0.0.0', () => {
console.log(`🚀 Servidor rodando na porta ${PORT}`);
console.log(`📊 Acesse http://localhost:${PORT}/benchmark para testar latência`);
});
}
main().catch(console.error);