Disciplina: Sistemas Distribuídos Curso: Bacharelado em Sistemas de Informação — IFBA Professor: Leonardo Barreto Campos
Um cliente solicita o download de um arquivo. O sistema busca esse arquivo em uma réplica (servidor de arquivos). Se a conexão com essa réplica cair durante o download, o sistema detecta a falha automaticamente e continua o download de onde parou, usando outra réplica — sem que o cliente precise fazer nada. Isso é a transparência nas falhas.
┌──────────────────────────────────────────────────────────────┐
│ MÁQUINA 1 — Coordinator (:8080) │
│ │
│ /admin → painel do operador (monitora réplicas e downloads)│
│ /user → painel do cliente (lista arquivos e faz download) │
│ │
│ Lógica: health check, proxy do download, failover │
└───────────────────┬──────────────────────────────────────────┘
│ HTTP Range Requests
┌─────────┴──────────┐
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ MÁQUINA 2 │ │ MÁQUINA 3 │
│ Réplica A :9001 │ │ Réplica B :9001 │
│ (tem os arquivos│ │ (mesmos arquivos│
│ em shared/files│ │ em shared/files│
└──────────────────┘ └──────────────────┘
MÁQUINAS 4 e 5 → apenas abrem o browser
não precisam de Go instalado
não precisam dos arquivos
O sistema expõe as seguintes rotas da API para comunicação interna e interfaces web:
| Rota | Método | Descrição |
|---|---|---|
/user |
GET | Interface do cliente (HTML) |
/admin |
GET | Interface do operador (HTML) |
/api/files |
GET | Lista os arquivos disponíveis (proxy para uma réplica) |
/api/replicas |
GET | Status atual de todas as réplicas |
/api/download/{arquivo} |
GET | Download com failover automático |
/events |
GET | Stream SSE de eventos em tempo real |
| Rota | Método | Descrição |
|---|---|---|
/health |
GET | Retorna {"status":"ok"} — usado pelo health check |
/files |
GET | Lista os arquivos em shared/files/ |
/download/{arquivo} |
GET/HEAD | Serve o arquivo, com suporte a Range Requests |
Sempre o IP da Máquina 1 (Coordinator).
O coordinator é o único que serve o frontend. As réplicas não têm interface web — elas só servem os bytes dos arquivos para o coordinator.
| Quem acessa | URL |
|---|---|
| Operador (Máq 1) | http://localhost:8080/admin |
| Clientes (Máq 4 e 5) | http://<IP_DA_MÁQUINA_1>:8080/user |
Como descobrir o IP da Máquina 1:
# No Windows:
ipconfig
# No Linux/Mac:
ip a
# ou
hostname -IProcure o IP na linha IPv4 (Windows) ou inet (Linux) da interface de rede da máquina (geralmente começa com 192.168.x.x ou 10.x.x.x). O coordinator também imprime o endereço no terminal quando inicia.
failover-trasnparency/
├── main.go ← Coordinator: proxy, failover, health check, SSE
├── go.mod
├── replica/
│ └── main.go ← Servidor de réplica: serve arquivos com Range Request
├── static/
│ ├── admin.html ← Frontend do operador (/admin)
│ └── user.html ← Frontend do cliente (/user)
└── shared/
└── files/ ← Coloque aqui os arquivos nas máquinas RÉPLICA
Onde os arquivos precisam estar:
| Máquina | Precisa dos arquivos em shared/files/? |
|---|---|
| Máquina 1 (Coordinator) | NÃO — só faz proxy |
| Máquina 2 (Réplica A) | SIM |
| Máquina 3 (Réplica B) | SIM (cópia idêntica) |
| Máquinas 4 e 5 (Clientes) | NÃO — só usam o browser |
- Go 1.22+ instalado nas máquinas 1, 2 e 3
- Todas as máquinas na mesma rede (mesmo Wi-Fi ou cabo do laboratório)
- Browser nas máquinas 4 e 5 (qualquer um)
Copie a pasta do projeto para cada uma das três máquinas. A forma mais fácil em laboratório é via pendrive ou git clone.
Nas Máquinas 2 e 3, coloque os arquivos que serão disponibilizados para download dentro de shared/files/. Ambas precisam ter os mesmos arquivos para o failover funcionar.
# Exemplo: gerar um arquivo de teste de 50MB (Linux/Mac)
dd if=/dev/urandom of=shared/files/teste-50mb.bin bs=1M count=50
# Windows (PowerShell)
$bytes = New-Object byte[] 52428800
[System.IO.File]::WriteAllBytes("shared\files\teste-50mb.bin", $bytes)Dica: use um arquivo grande o suficiente para ter tempo de simular a falha durante o download. 20–100 MB é ideal.
Na Máquina 2, abra o terminal na pasta do projeto e execute:
go run ./replica/ -port 9001 -name "Replica-A" -delay 150msNa Máquina 3, execute:
go run ./replica/ -port 9001 -name "Replica-B" -delay 150msO terminal vai mostrar o IP e a porta de cada réplica, por exemplo:
╔══════════════════════════════════════════╗
║ REPLICA SERVER: Replica-A ║
║ Address: 192.168.1.11:9001 ║
║ Files: ./shared/files ║
║ Delay: 150ms ║
╚══════════════════════════════════════════╝
O flag
-delayadiciona um atraso proposital a cada bloco de 32KB enviado. Isso torna o download mais lento e dá tempo de desligar a réplica durante a demo.
Abra o terminal na pasta do projeto e execute, substituindo pelos IPs reais das Máquinas 2 e 3:
go run . -replicas "192.168.1.11:9001,192.168.1.12:9001"O terminal vai mostrar:
╔══════════════════════════════════════════════╗
║ COORDINATOR SERVER ║
║ Address: 192.168.1.10:8080 ║
║ Admin: http://192.168.1.10:8080/admin ║
║ User: http://192.168.1.10:8080/user ║
║ Replicas: 2 configured ║
╚══════════════════════════════════════════════╝
→ Replica: 192.168.1.11:9001
→ Replica: 192.168.1.12:9001
Na Máquina 1 (operador):
http://localhost:8080/admin
Nas Máquinas 4 e 5 (clientes) — apenas abrir o browser:
http://192.168.1.10:8080/user
(use o IP real que apareceu no terminal do coordinator)
- Abra
/adminno operador e/usernos clientes - Confirme que as réplicas aparecem como ONLINE (verde) no admin
- Um cliente clica em Download em um arquivo grande
- Observe a barra de progresso avançando no
/usere o monitor no/admin - Encerre a Réplica A com
Ctrl+Cno terminal da Máquina 2 (ou puxe o cabo) - Observe nos logs:
CONEXÃO PERDIDA com 192.168.1.11:9001FAILOVER: Reconectando à 192.168.1.12:9001 byte XXXXX
- A barra de progresso fica laranja e o símbolo ⚡ aparece
- O download continua e conclui normalmente
- No
/admina Réplica A aparece como OFFLINE (vermelho piscando)
O coordinator é o ponto central da arquitetura — ele é o único que serve o frontend e coordena os downloads. Se ele cair:
- O
/usere o/adminficam inacessíveis (não tem como acessar pelo browser) - Qualquer download em andamento é interrompido
- As réplicas continuam rodando normalmente, mas ninguém consegue usá-las
Isso é esperado. O tema 08 demonstra transparência nas falhas de réplicas de arquivo. A falha do coordinator é um problema diferente (coordenador tolerante a falhas requer eleição de líder, Raft, Paxos — muito além do escopo). Na prática do seminário, o coordinator fica na máquina do professor ou em uma máquina estável. O que pode e deve cair durante a demo são as réplicas.
"Máquina" = qualquer coisa com browser ou terminal. Pode ser um PC real, um terminal diferente no mesmo PC, um celular, um tablet. O sistema não sabe a diferença.
| Papel | O que precisa | O que roda |
|---|---|---|
| Máquina 1 — Coordinator | PC com Go instalado | go run . no terminal + browser no /admin |
| Máquina 2 — Réplica A | PC/terminal com Go + os arquivos | go run ./replica/ -port 9001 |
| Máquina 3 — Réplica B | PC/terminal com Go + os arquivos | go run ./replica/ -port 9002 |
| Clientes (4, 5...) | Só um browser | Abrir http://<ip-coordinator>:8080/user |
Sim, tudo pode rodar em terminais do mesmo PC. Isso é o "teste local" da seção abaixo.
Sim, o celular funciona como cliente — basta estar no mesmo WiFi e abrir o /user no browser do celular.
Todo o servidor roda no seu laptop. O celular ou outro dispositivo só abre o browser.
Abra o Prompt de Comando (Win + R → cmd → Enter) e execute:
ipconfig | findstr "IPv4"A saída vai ser algo assim:
Endereço IPv4. . . . . . . . . . . . . : 192.168.1.105
Esse IP é o endereço do seu laptop na rede WiFi. É o que o celular/namorada vai usar no browser.
O coordinator também imprime esse IP automaticamente quando inicia — você pode copiar direto de lá.
O Windows bloqueia conexões externas por padrão. Abra o PowerShell como Administrador e execute:
netsh advfirewall firewall add rule name="SD-Coordinator" dir=in action=allow protocol=TCP localport=8080Só precisa fazer uma vez. Para desfazer depois:
netsh advfirewall firewall delete rule name="SD-Coordinator"No PowerShell (na pasta do projeto), gere um arquivo grande o suficiente para ter tempo de simular a falha:
# Cria um arquivo de 30MB
$bytes = New-Object byte[] (30 * 1024 * 1024)
(New-Object Random).NextBytes($bytes)
[System.IO.File]::WriteAllBytes("$PWD\shared\files\teste-30mb.bin", $bytes)Na pasta do projeto, abra 3 terminais separados (pode usar o terminal do VSCode com + ou janelas separadas):
Terminal 1 — Réplica A:
go run ./replica/ -port 9001 -name "Replica-A" -delay 200msTerminal 2 — Réplica B:
go run ./replica/ -port 9002 -name "Replica-B" -delay 200msTerminal 3 — Coordinator (inicie por último):
go run . -replicas "127.0.0.1:9001,127.0.0.1:9002"O Terminal 3 vai imprimir o IP da sua máquina:
║ Admin: http://192.168.1.105:8080/admin ║
║ User: http://192.168.1.105:8080/user ║
| Dispositivo | URL |
|---|---|
| Seu laptop (admin) | http://localhost:8080/admin |
| Celular / namorada | http://192.168.1.105:8080/user ← o IP do Terminal 3 |
No celular: abrir o browser (Chrome, Safari) e digitar a URL. Precisa estar no mesmo WiFi.
- No celular/namorada: clicar em Download
- No seu laptop: observar o progresso no
/admin - Enquanto o download avança: Ctrl+C no Terminal 1 (mata a Réplica A)
- O sistema detecta a falha e automaticamente troca para a Réplica B
- A barra fica laranja com ⚡ — o download continua e termina normalmente
Para testar tudo em um só computador, abra 3 terminais:
# Terminal 1 — Réplica A
go run ./replica/ -port 9001 -name "Replica-A" -delay 200ms
# Terminal 2 — Réplica B
go run ./replica/ -port 9002 -name "Replica-B" -delay 200ms
# Terminal 3 — Coordinator
go run . -replicas "127.0.0.1:9001,127.0.0.1:9002"Acesse:
- Admin:
http://localhost:8080/admin - Cliente:
http://localhost:8080/user
Para simular a falha: feche o Terminal 1 (Ctrl+C) durante um download.
| Flag | Padrão | Descrição |
|---|---|---|
-port |
8080 |
Porta do servidor |
-static |
./static |
Pasta com os HTMLs |
-replicas |
— | Obrigatório. IPs das réplicas separados por vírgula |
-health-interval |
2s |
Intervalo do health check |
| Flag | Padrão | Descrição |
|---|---|---|
-port |
9001 |
Porta do servidor |
-name |
replica-{port} |
Nome exibido nos logs |
-dir |
./shared/files |
Pasta com os arquivos |
-delay |
100ms |
Atraso por chunk de 32KB (para demo) |
- Cliente abre
/usere clica em Download - Coordinator recebe a requisição em
/api/download/{arquivo} - Coordinator faz
HEADna Réplica A para obter o tamanho total do arquivo - Coordinator faz
GETcomRange: bytes=0-{total}na Réplica A - Coordinator transmite os bytes recebidos direto para o browser do cliente
- A cada chunk, o coordinator envia o progresso via SSE para todos os browsers conectados
- Se a Réplica A cai (timeout ou erro de rede):
- O coordinator registra quantos bytes já foram enviados
- Busca outra réplica saudável na lista
- Faz
GETcomRange: bytes={bytesRecebidos}-{total}na Réplica B - Continua o streaming exatamente de onde parou
- O arquivo chega completo ao browser — a troca de réplica é transparente para o usuário
- Backend: Go 1.22+ (stdlib pura, zero dependências)
- Frontend: HTML + CSS + JavaScript vanilla
- Protocolo de failover: HTTP Range Requests (
Range: bytes=X-Y) - Tempo real: Server-Sent Events (SSE)