first commit

This commit is contained in:
Rodrigo Bertoldo de Sousa Martins committed 2026-09-13 20:49:33 -03:00
commit 01d7cb1a80
34 files changed
+5723

No files matched your search

+8
View File
@@ -0,0 +1,8 @@
FROM node:20-alpine
WORKDIR /app
COPY package.json ./
RUN npm install --production
COPY server.js ./
RUN apk add --no-cache wget
EXPOSE 3000
CMD ["node", "server.js"]
+13
View File
@@ -0,0 +1,13 @@
{
"name": "sockmap-demo-app",
"version": "1.0.0",
"description": "App Node.js de demonstração para eBPF sockmap",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"mysql2": "^3.6.0",
"pg": "^8.11.0"
}
}
+219
View File
@@ -0,0 +1,219 @@
// ─────────────────────────────────────────────────────────
// 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);
+25
View File
@@ -0,0 +1,25 @@
-- ─────────────────────────────────────────────────────────
-- MySQL: Script de inicialização do banco de dados
-- Cria tabelas de exemplo para teste
-- ─────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(150) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS requests_log (
id INT AUTO_INCREMENT PRIMARY KEY,
endpoint VARCHAR(255) NOT NULL,
method VARCHAR(10) NOT NULL,
response_time_ms FLOAT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Dados de exemplo
INSERT INTO users (name, email) VALUES
('Alice Silva', 'alice@exemplo.com'),
('Bob Santos', 'bob@exemplo.com'),
('Carol Oliveira', 'carol@exemplo.com');
+28
View File
@@ -0,0 +1,28 @@
-- ─────────────────────────────────────────────────────────
-- PostgreSQL: Script de inicialização do banco de dados
-- Cria tabelas de exemplo para teste
-- ─────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS products (
id SERIAL PRIMARY KEY,
name VARCHAR(200) NOT NULL,
price NUMERIC(10, 2) NOT NULL,
stock INT DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS orders (
id SERIAL PRIMARY KEY,
user_name VARCHAR(100) NOT NULL,
product_id INT REFERENCES products(id),
quantity INT NOT NULL DEFAULT 1,
total NUMERIC(10, 2),
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Dados de exemplo
INSERT INTO products (name, price, stock) VALUES
('Notebook Dell', 4500.00, 10),
('Mouse Logitech', 150.00, 50),
('Teclado Mecânico', 350.00, 30),
('Monitor 27"', 2200.00, 15);
+113
View File
@@ -0,0 +1,113 @@
# ─────────────────────────────────────────────────────────
# Docker Compose - Stack completa para testar eBPF Sockmap
#
# Serviços:
# nginx → Proxy reverso (porta 80 → Node.js)
# app → Servidor Node.js (porta 3000)
# mysql → Banco de dados MySQL (porta 3306)
# postgres → Banco de dados PostgreSQL (porta 5432)
#
# Todos os containers ficam na mesma rede bridge,
# e o eBPF sockmap acelera a comunicação entre eles.
# ─────────────────────────────────────────────────────────
version: "3.8"
services:
# ─── NGINX Proxy Reverso ───
nginx:
image: nginx:alpine
container_name: sockmap-nginx
ports:
- "8080:80" # Acesse http://localhost:8080
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
app:
condition: service_healthy
networks:
- sockmap-net
restart: unless-stopped
# ─── Aplicação Node.js ───
app:
build:
context: ./app
dockerfile: Dockerfile
container_name: sockmap-app
environment:
NODE_ENV: production
MYSQL_HOST: mysql
MYSQL_PORT: 3306
MYSQL_USER: app
MYSQL_PASSWORD: app_password
MYSQL_DATABASE: app_db
POSTGRES_HOST: postgres
POSTGRES_PORT: 5432
POSTGRES_USER: app
POSTGRES_PASSWORD: app_password
POSTGRES_DATABASE: app_db
depends_on:
mysql:
condition: service_healthy
postgres:
condition: service_healthy
networks:
- sockmap-net
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
interval: 5s
timeout: 3s
retries: 10
restart: unless-stopped
# ─── MySQL ───
mysql:
image: mysql:8.0
container_name: sockmap-mysql
environment:
MYSQL_ROOT_PASSWORD: root_password
MYSQL_DATABASE: app_db
MYSQL_USER: app
MYSQL_PASSWORD: app_password
volumes:
- mysql-data:/var/lib/mysql
- ./db/mysql-init.sql:/docker-entrypoint-initdb.d/init.sql:ro
networks:
- sockmap-net
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-proot_password"]
interval: 5s
timeout: 3s
retries: 30
restart: unless-stopped
# ─── PostgreSQL ───
postgres:
image: postgres:16-alpine
container_name: sockmap-postgres
environment:
POSTGRES_DB: app_db
POSTGRES_USER: app
POSTGRES_PASSWORD: app_password
volumes:
- postgres-data:/var/lib/postgresql/data
- ./db/postgres-init.sql:/docker-entrypoint-initdb.d/init.sql:ro
networks:
- sockmap-net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app_db"]
interval: 5s
timeout: 3s
retries: 30
restart: unless-stopped
volumes:
mysql-data:
postgres-data:
networks:
sockmap-net:
driver: bridge
ipam:
config:
- subnet: 172.28.0.0/16
+78
View File
@@ -0,0 +1,78 @@
# ─────────────────────────────────────────────────────────
# nginx.conf — Proxy reverso para o servidor Node.js
#
# Nginx recebe requisições na porta 80 e encaminha para
# o container do Node.js na porta 3000.
#
# Com o eBPF sockmap ativo, a comunicação nginx→app
# é acelerada (dados pulam a pilha de rede do kernel).
# ─────────────────────────────────────────────────────────
worker_processes auto;
events {
worker_connections 1024;
use epoll;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Formato de log com tempo de resposta
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
keepalive_requests 100;
# Compressão gzip
gzip on;
gzip_types text/plain application/json application/javascript text/css;
# ─── Upstream: servidor Node.js ───
upstream nodejs_app {
server sockmap-app:3000;
keepalive 32; # Conexões persistentes (importante para sockmap!)
}
server {
listen 80;
server_name localhost;
# Página de status do Nginx
location /nginx-status {
stub_status on;
access_log off;
}
# Todas as outras requisições vão para o Node.js
location / {
proxy_pass http://nodejs_app;
proxy_http_version 1.1;
# Headers para proxy reverso
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Manter conexão viva (keepalive)
proxy_set_header Connection "";
# Timeouts
proxy_connect_timeout 5s;
proxy_read_timeout 30s;
proxy_send_timeout 30s;
}
}
}