220 lines
7.8 KiB
JavaScript
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);
|