---
title: "Load Balancer em Zig — Tutorial Passo a Passo"
url: "https://ziglang.com.br/projetos/load-balancer-em-zig-tutorial-passo-a-passo/"
markdown_url: "https://ziglang.com.br/projetos/load-balancer-em-zig-tutorial-passo-a-passo.MD"
description: "Construa um load balancer round-robin em Zig que distribui requisições HTTP entre múltiplos servidores backend com health checks."
date: "2026-02-21"
author: "Zig Brasil"
---

# Load Balancer em Zig — Tutorial Passo a Passo

Construa um load balancer round-robin em Zig que distribui requisições HTTP entre múltiplos servidores backend com health checks.


# Load Balancer em Zig — Tutorial Passo a Passo

Neste tutorial, vamos construir um **load balancer round-robin** em Zig que distribui requisições HTTP entre múltiplos servidores backend, com health checks e detecção de servidores inativos.

## O Que Vamos Construir

Nosso load balancer vai:

- Distribuir requisições HTTP usando algoritmo round-robin
- Manter lista de servidores backend configuráveis
- Implementar health checks periódicos
- Detectar e remover servidores inativos automaticamente
- Reencaminhar requisições e respostas completas
- Exibir métricas de distribuição em tempo real

## Por Que Este Projeto?

Load balancers são componentes críticos em qualquer infraestrutura web. Construir um nos ensina sobre proxy reverso, distribuição de carga, monitoramento de saúde e tolerância a falhas. Em Zig, temos controle total sobre sockets e buffers.

## Pré-requisitos

- Zig 0.13+ instalado ([guia de instalação](/tutoriais/instalacao/))
- Conhecimento de [redes TCP](/receitas/http/)
- Familiaridade com [HTTP](/projetos/servidor-arquivos-http/)

## Passo 1: Estrutura do Projeto

```bash
mkdir load-balancer
cd load-balancer
zig init
```

## Passo 2: Configuração dos Backends

```zig
const std = @import("std");
const net = std.net;
const posix = std.posix;
const io = std.io;
const mem = std.mem;
const fmt = std.fmt;
const time = std.time;
const Thread = std.Thread;

const MAX_BACKENDS = 16;
const TAMANHO_BUF = 65536;
const HEALTH_CHECK_INTERVALO_MS = 5000;

/// Estado de um servidor backend.
const BackendStatus = enum {
    saudavel,
    doente,
    desconhecido,

    pub fn str(self: BackendStatus) []const u8 {
        return switch (self) {
            .saudavel => "SAUDAVEL",
            .doente => "DOENTE",
            .desconhecido => "DESCONHECIDO",
        };
    }
};

/// Representa um servidor backend.
const Backend = struct {
    host: [64]u8,
    host_len: usize,
    porta: u16,
    status: BackendStatus,
    requisicoes_total: u64,
    requisicoes_falhas: u64,
    ultimo_check: i64,
    tempo_resposta_ms: u64,

    pub fn hostStr(self: *const Backend) []const u8 {
        return self.host[0..self.host_len];
    }

    pub fn endereco(self: *const Backend) ?[4]u8 {
        var ip: [4]u8 = undefined;
        var it = mem.splitScalar(u8, self.hostStr(), '.');
        var i: usize = 0;
        while (it.next()) |parte| {
            if (i >= 4) return null;
            ip[i] = fmt.parseInt(u8, parte, 10) catch return null;
            i += 1;
        }
        if (i == 4) return ip;
        return null;
    }
};

/// Configuração do load balancer.
const LoadBalancerConfig = struct {
    porta: u16,
    backends: [MAX_BACKENDS]Backend,
    num_backends: usize,

    pub fn adicionarBackend(self: *LoadBalancerConfig, host: []const u8, porta: u16) void {
        if (self.num_backends >= MAX_BACKENDS) return;
        var backend = &self.backends[self.num_backends];
        @memcpy(backend.host[0..host.len], host);
        backend.host_len = host.len;
        backend.porta = porta;
        backend.status = .desconhecido;
        backend.requisicoes_total = 0;
        backend.requisicoes_falhas = 0;
        backend.ultimo_check = 0;
        backend.tempo_resposta_ms = 0;
        self.num_backends += 1;
    }
};
```

## Passo 3: Algoritmo Round-Robin e Health Check

```zig
/// Load Balancer com round-robin e health checks.
const LoadBalancer = struct {
    config: LoadBalancerConfig,
    indice_atual: usize, // para round-robin
    total_requisicoes: u64,

    const Self = @This();

    pub fn init(config: LoadBalancerConfig) Self {
        return .{
            .config = config,
            .indice_atual = 0,
            .total_requisicoes = 0,
        };
    }

    /// Seleciona o próximo backend saudável (round-robin).
    pub fn proximoBackend(self: *Self) ?*Backend {
        if (self.config.num_backends == 0) return null;

        // Tentar encontrar um backend saudável
        var tentativas: usize = 0;
        while (tentativas < self.config.num_backends) : (tentativas += 1) {
            const idx = self.indice_atual % self.config.num_backends;
            self.indice_atual += 1;

            const backend = &self.config.backends[idx];
            if (backend.status != .doente) {
                return backend;
            }
        }

        // Nenhum saudável: tentar qualquer um
        const idx = self.indice_atual % self.config.num_backends;
        self.indice_atual += 1;
        return &self.config.backends[idx];
    }

    /// Verifica a saúde de um backend tentando conectar.
    pub fn healthCheck(backend: *Backend) void {
        const ip = backend.endereco() orelse {
            backend.status = .doente;
            return;
        };

        const addr = net.Address.initIp4(ip, backend.porta);
        const socket = posix.socket(posix.AF.INET, posix.SOCK.STREAM, 0) catch {
            backend.status = .doente;
            return;
        };
        defer posix.close(socket);

        const inicio = time.milliTimestamp();

        posix.connect(socket, &addr.any, addr.getOsSockLen()) catch {
            backend.status = .doente;
            return;
        };

        backend.tempo_resposta_ms = @intCast(time.milliTimestamp() - inicio);
        backend.status = .saudavel;
        backend.ultimo_check = time.milliTimestamp();
    }

    /// Executa health checks em todos os backends.
    pub fn healthCheckTodos(self: *Self) void {
        for (0..self.config.num_backends) |i| {
            healthCheck(&self.config.backends[i]);
        }
    }

    /// Encaminha uma requisição para o backend selecionado.
    pub fn encaminhar(
        self: *Self,
        dados: []const u8,
        socket_cliente: posix.socket_t,
        stdout: anytype,
    ) !void {
        const backend = self.proximoBackend() orelse {
            const resp = "HTTP/1.1 503 Service Unavailable\r\n" ++
                "Content-Type: text/plain\r\nConnection: close\r\n\r\n" ++
                "Nenhum backend disponivel";
            _ = posix.write(socket_cliente, resp) catch {};
            return;
        };

        self.total_requisicoes += 1;
        backend.requisicoes_total += 1;

        const ip = backend.endereco() orelse {
            backend.status = .doente;
            return;
        };

        // Conectar ao backend
        const addr = net.Address.initIp4(ip, backend.porta);
        const socket_backend = posix.socket(posix.AF.INET, posix.SOCK.STREAM, 0) catch {
            backend.requisicoes_falhas += 1;
            backend.status = .doente;
            const resp = "HTTP/1.1 502 Bad Gateway\r\nConnection: close\r\n\r\n";
            _ = posix.write(socket_cliente, resp) catch {};
            return;
        };
        defer posix.close(socket_backend);

        posix.connect(socket_backend, &addr.any, addr.getOsSockLen()) catch {
            backend.requisicoes_falhas += 1;
            backend.status = .doente;
            const resp = "HTTP/1.1 502 Bad Gateway\r\nConnection: close\r\n\r\n";
            _ = posix.write(socket_cliente, resp) catch {};
            return;
        };

        // Enviar requisição ao backend
        _ = posix.write(socket_backend, dados) catch return;

        // Relay resposta de volta ao cliente
        var buf_resp: [TAMANHO_BUF]u8 = undefined;
        while (true) {
            const n = posix.read(socket_backend, &buf_resp) catch break;
            if (n == 0) break;
            _ = posix.write(socket_cliente, buf_resp[0..n]) catch break;
        }

        try stdout.print("  -> {s}:{d} (req #{d})\n", .{
            backend.hostStr(), backend.porta, backend.requisicoes_total,
        });
    }

    /// Imprime o status de todos os backends.
    pub fn imprimirStatus(self: *const Self, writer: anytype) !void {
        try writer.print("\n  --- Status dos Backends ---\n", .{});
        try writer.print("  {s:<20} {s:<10} {s:<10} {s:<10} {s}\n", .{
            "BACKEND", "STATUS", "REQS", "FALHAS", "RESP(ms)",
        });
        try writer.print("  {s:-<60}\n", .{""});

        for (0..self.config.num_backends) |i| {
            const b = &self.config.backends[i];
            try writer.print("  {s}:{d:<14} {s:<10} {d:<10} {d:<10} {d}\n", .{
                b.hostStr(), b.porta, b.status.str(),
                b.requisicoes_total, b.requisicoes_falhas, b.tempo_resposta_ms,
            });
        }
        try writer.print("  Total requisicoes: {d}\n\n", .{self.total_requisicoes});
    }
};
```

## Passo 4: Servidor Principal

```zig
pub fn main() !void {
    const stdout = io.getStdOut().writer();
    const porta: u16 = 8000;

    // Configurar backends
    var config = LoadBalancerConfig{
        .porta = porta,
        .backends = undefined,
        .num_backends = 0,
    };
    config.adicionarBackend("127.0.0.1", 8081);
    config.adicionarBackend("127.0.0.1", 8082);
    config.adicionarBackend("127.0.0.1", 8083);

    var lb = LoadBalancer.init(config);

    // Health check inicial
    try stdout.print("  Verificando saude dos backends...\n", .{});
    lb.healthCheckTodos();
    try lb.imprimirStatus(stdout);

    // Criar socket do servidor
    const endereco = net.Address.initIp4(.{ 0, 0, 0, 0 }, porta);
    const socket_servidor = try posix.socket(posix.AF.INET, posix.SOCK.STREAM, 0);
    defer posix.close(socket_servidor);

    const optval: i32 = 1;
    try posix.setsockopt(socket_servidor, posix.SOL.SOCKET, posix.SO.REUSEADDR, mem.asBytes(&optval));
    try posix.bind(socket_servidor, &endereco.any, endereco.getOsSockLen());
    try posix.listen(socket_servidor, 128);

    try stdout.print(
        \\
        \\  ==========================================
        \\     LOAD BALANCER - Round Robin - Zig
        \\  ==========================================
        \\  Escutando na porta {d}
        \\  Backends configurados: {d}
        \\
        \\  Teste: curl http://localhost:{d}/
        \\  ==========================================
        \\
    , .{ porta, lb.config.num_backends, porta });

    var ultimo_health_check = time.milliTimestamp();

    // Loop principal
    while (true) {
        // Health check periódico
        const agora = time.milliTimestamp();
        if (agora - ultimo_health_check > HEALTH_CHECK_INTERVALO_MS) {
            lb.healthCheckTodos();
            ultimo_health_check = agora;
        }

        const socket_cliente = posix.accept(socket_servidor, null, null) catch continue;

        var buf: [TAMANHO_BUF]u8 = undefined;
        const n = posix.read(socket_cliente, &buf) catch {
            posix.close(socket_cliente);
            continue;
        };

        if (n == 0) {
            posix.close(socket_cliente);
            continue;
        }

        lb.encaminhar(buf[0..n], socket_cliente, stdout) catch {};
        posix.close(socket_cliente);

        // Imprimir status a cada 10 requisições
        if (lb.total_requisicoes % 10 == 0) {
            lb.imprimirStatus(stdout) catch {};
        }
    }
}
```

## Testes

```zig
test "config - adicionar backends" {
    var config = LoadBalancerConfig{
        .porta = 8000,
        .backends = undefined,
        .num_backends = 0,
    };
    config.adicionarBackend("127.0.0.1", 8081);
    config.adicionarBackend("127.0.0.1", 8082);
    try std.testing.expectEqual(@as(usize, 2), config.num_backends);
}

test "round-robin distribui igualmente" {
    var config = LoadBalancerConfig{
        .porta = 8000,
        .backends = undefined,
        .num_backends = 0,
    };
    config.adicionarBackend("127.0.0.1", 8081);
    config.adicionarBackend("127.0.0.1", 8082);

    // Marcar como saudáveis
    config.backends[0].status = .saudavel;
    config.backends[1].status = .saudavel;

    var lb = LoadBalancer.init(config);

    const b1 = lb.proximoBackend().?;
    const b2 = lb.proximoBackend().?;
    try std.testing.expect(b1.porta != b2.porta);
}

test "backend doente e pulado" {
    var config = LoadBalancerConfig{
        .porta = 8000,
        .backends = undefined,
        .num_backends = 0,
    };
    config.adicionarBackend("127.0.0.1", 8081);
    config.adicionarBackend("127.0.0.1", 8082);

    config.backends[0].status = .doente;
    config.backends[1].status = .saudavel;

    var lb = LoadBalancer.init(config);

    const b = lb.proximoBackend().?;
    try std.testing.expectEqual(@as(u16, 8082), b.porta);
}
```

## Compilando e Executando

```bash
# Iniciar backends de teste (em terminais separados):
# python3 -m http.server 8081
# python3 -m http.server 8082
# python3 -m http.server 8083

# Iniciar o load balancer:
zig build run

# Testar distribuição:
for i in $(seq 1 10); do curl -s http://localhost:8000/; done
```

## Conceitos Aprendidos

- **Round-robin** para distribuição de carga
- **Health checks** para detecção de falhas
- **Proxy reverso** com relay de dados
- **Gerenciamento de estado** de servidores
- **Tolerância a falhas** com remoção automática

## Próximos Passos

- Explore a [stdlib de redes](/stdlib/net/)
- Veja o [Proxy HTTP](/projetos/proxy-http/) para funcionalidades extras
- Construa a [Thread Pool](/projetos/thread-pool-impl/) para conexões paralelas
- Consulte o [Rate Limiter](/projetos/rate-limiter/) para proteção dos backends
