---
title: "Rust para Microsserviços: Guia Prático | Rust Brasil"
url: "https://rustlang.com.br/artigos/rust-para-microsservicos/"
markdown_url: "https://rustlang.com.br/artigos/rust-para-microsservicos.MD"
description: "Guia de microsserviços em Rust: gRPC com Tonic, message queues, service mesh e observabilidade distribuída."
date: "2026-02-23"
author: "Equipe Rust Brasil"
---

# Rust para Microsserviços: Guia Prático | Rust Brasil

Guia de microsserviços em Rust: gRPC com Tonic, message queues, service mesh e observabilidade distribuída.


## Introdução

Microsserviços em produção exigem uma combinação rara de qualidades: **alta performance** para lidar com milhares de requisições por segundo, **baixo consumo de memória** para reduzir custos de infraestrutura, **latência previsível** para cumprir SLAs, e **confiabilidade** para operar 24/7 sem memory leaks ou crashes inesperados. Rust oferece todas essas qualidades de forma nativa, tornando-se uma escolha cada vez mais popular para serviços que precisam ser robustos em produção.

Enquanto [Go](https://golang.com.br) dominou a primeira onda de microsserviços cloud-native, Rust está se estabelecendo em cenários onde Go não é suficiente: serviços com **requisitos de latência p99 agressivos**, **orçamento de memória limitado** (serverless, edge computing), **processamento de dados em tempo real** e **componentes de infraestrutura crítica**. Neste artigo, vamos construir um microsserviço completo com gRPC, health checks, graceful shutdown e deploy otimizado.

## Ecossistema para Microsserviços

### Comunicação

| Biblioteca | Protocolo | Descrição |
|---|---|---|
| **Tonic** | gRPC | Implementação gRPC completa e de alta performance |
| **Axum** | HTTP/REST | Framework web com middleware Tower |
| **reqwest** | HTTP Client | Cliente HTTP assíncrono |
| **lapin** | AMQP | Cliente RabbitMQ |
| **rdkafka** | Kafka | Cliente Apache Kafka (bindings librdkafka) |
| **redis** | Redis | Cliente Redis com suporte a pub/sub e streams |

### Observabilidade

| Biblioteca | Função |
|---|---|
| **tracing** | Logging estruturado e distributed tracing |
| **metrics** | Métricas (counters, gauges, histograms) |
| **opentelemetry** | OpenTelemetry (traces, metrics, logs) |
| **tower-http** | Middleware para HTTP (logging, compression, CORS) |

### Resiliência

| Biblioteca | Função |
|---|---|
| **tower** | Middleware composicional (retry, timeout, rate limit) |
| **backoff** | Retry com backoff exponencial |
| **circuit-breaker** | Padrão circuit breaker |

## Exemplo Prático: Microsserviço de Pedidos com gRPC

Vamos construir um serviço de gerenciamento de pedidos que se comunica via gRPC, inclui health checks, métricas e graceful shutdown.

### Definição do Protocolo (proto/pedidos.proto)

```protobuf
syntax = "proto3";

package pedidos;

service ServicoPedidos {
    // Criar um novo pedido
    rpc CriarPedido(NovoPedidoRequest) returns (PedidoResponse);

    // Buscar pedido por ID
    rpc BuscarPedido(BuscarPedidoRequest) returns (PedidoResponse);

    // Listar pedidos de um cliente
    rpc ListarPedidos(ListarPedidosRequest) returns (ListarPedidosResponse);

    // Atualizar status do pedido
    rpc AtualizarStatus(AtualizarStatusRequest) returns (PedidoResponse);

    // Stream de atualizações de status
    rpc ObservarPedido(BuscarPedidoRequest) returns (stream StatusUpdate);
}

message NovoPedidoRequest {
    string cliente_id = 1;
    repeated ItemPedido itens = 2;
    string endereco_entrega = 3;
}

message ItemPedido {
    string produto_id = 1;
    string nome = 2;
    uint32 quantidade = 3;
    double preco_unitario = 4;
}

message BuscarPedidoRequest {
    string pedido_id = 1;
}

message ListarPedidosRequest {
    string cliente_id = 1;
    uint32 limite = 2;
    uint32 offset = 3;
}

message PedidoResponse {
    string id = 1;
    string cliente_id = 2;
    repeated ItemPedido itens = 3;
    double total = 4;
    string status = 5;
    string endereco_entrega = 6;
    string criado_em = 7;
    string atualizado_em = 8;
}

message ListarPedidosResponse {
    repeated PedidoResponse pedidos = 1;
    uint32 total = 2;
}

message AtualizarStatusRequest {
    string pedido_id = 1;
    string novo_status = 2;
}

message StatusUpdate {
    string pedido_id = 1;
    string status_anterior = 2;
    string status_novo = 3;
    string timestamp = 4;
}
```

### Cargo.toml

```toml
[package]
name = "servico-pedidos"
version = "0.1.0"
edition = "2021"

[dependencies]
tonic = "0.12"
prost = "0.13"
tokio = { version = "1", features = ["full"] }
tokio-stream = "0.1"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
uuid = { version = "1", features = ["v4"] }
chrono = "0.4"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
anyhow = "1"
dashmap = "6"

[build-dependencies]
tonic-build = "0.12"
```

### Build Script (build.rs)

```rust
fn main() -> Result<(), Box<dyn std::error::Error>> {
    tonic_build::compile_protos("proto/pedidos.proto")?;
    Ok(())
}
```

### Implementação do Serviço

```rust
use chrono::Utc;
use dashmap::DashMap;
use std::pin::Pin;
use std::sync::Arc;
use tokio::sync::broadcast;
use tokio_stream::{wrappers::BroadcastStream, Stream, StreamExt};
use tonic::{Request, Response, Status};
use uuid::Uuid;

// Módulo gerado pelo tonic-build
pub mod pedidos {
    tonic::include_proto!("pedidos");
}

use pedidos::servico_pedidos_server::ServicoPedidos;
use pedidos::*;

// === Modelo Interno ===

#[derive(Debug, Clone)]
struct Pedido {
    id: String,
    cliente_id: String,
    itens: Vec<ItemPedido>,
    total: f64,
    status: String,
    endereco_entrega: String,
    criado_em: String,
    atualizado_em: String,
}

impl From<&Pedido> for PedidoResponse {
    fn from(p: &Pedido) -> Self {
        PedidoResponse {
            id: p.id.clone(),
            cliente_id: p.cliente_id.clone(),
            itens: p.itens.clone(),
            total: p.total,
            status: p.status.clone(),
            endereco_entrega: p.endereco_entrega.clone(),
            criado_em: p.criado_em.clone(),
            atualizado_em: p.atualizado_em.clone(),
        }
    }
}

// === Implementação do Serviço ===

pub struct ServicoPedidosImpl {
    pedidos: Arc<DashMap<String, Pedido>>,
    notificacoes: broadcast::Sender<StatusUpdate>,
}

impl ServicoPedidosImpl {
    pub fn new() -> Self {
        let (tx, _) = broadcast::channel(100);
        Self {
            pedidos: Arc::new(DashMap::new()),
            notificacoes: tx,
        }
    }
}

#[tonic::async_trait]
impl ServicoPedidos for ServicoPedidosImpl {
    async fn criar_pedido(
        &self,
        request: Request<NovoPedidoRequest>,
    ) -> Result<Response<PedidoResponse>, Status> {
        let req = request.into_inner();

        // Validações
        if req.itens.is_empty() {
            return Err(Status::invalid_argument(
                "Pedido deve ter pelo menos um item",
            ));
        }
        if req.cliente_id.is_empty() {
            return Err(Status::invalid_argument(
                "ID do cliente é obrigatório",
            ));
        }

        let total: f64 = req
            .itens
            .iter()
            .map(|i| i.preco_unitario * i.quantidade as f64)
            .sum();

        let agora = Utc::now().to_rfc3339();
        let pedido = Pedido {
            id: Uuid::new_v4().to_string(),
            cliente_id: req.cliente_id,
            itens: req.itens,
            total,
            status: "criado".to_string(),
            endereco_entrega: req.endereco_entrega,
            criado_em: agora.clone(),
            atualizado_em: agora,
        };

        tracing::info!(
            pedido_id = %pedido.id,
            cliente_id = %pedido.cliente_id,
            total = %pedido.total,
            "Novo pedido criado"
        );

        let resposta = PedidoResponse::from(&pedido);
        self.pedidos.insert(pedido.id.clone(), pedido);

        Ok(Response::new(resposta))
    }

    async fn buscar_pedido(
        &self,
        request: Request<BuscarPedidoRequest>,
    ) -> Result<Response<PedidoResponse>, Status> {
        let pedido_id = &request.into_inner().pedido_id;

        let pedido = self
            .pedidos
            .get(pedido_id)
            .ok_or_else(|| Status::not_found("Pedido não encontrado"))?;

        Ok(Response::new(PedidoResponse::from(pedido.value())))
    }

    async fn listar_pedidos(
        &self,
        request: Request<ListarPedidosRequest>,
    ) -> Result<Response<ListarPedidosResponse>, Status> {
        let req = request.into_inner();
        let limite = if req.limite == 0 { 20 } else { req.limite } as usize;
        let offset = req.offset as usize;

        let todos: Vec<PedidoResponse> = self
            .pedidos
            .iter()
            .filter(|entry| entry.value().cliente_id == req.cliente_id)
            .map(|entry| PedidoResponse::from(entry.value()))
            .collect();

        let total = todos.len() as u32;
        let pedidos: Vec<PedidoResponse> = todos
            .into_iter()
            .skip(offset)
            .take(limite)
            .collect();

        Ok(Response::new(ListarPedidosResponse { pedidos, total }))
    }

    async fn atualizar_status(
        &self,
        request: Request<AtualizarStatusRequest>,
    ) -> Result<Response<PedidoResponse>, Status> {
        let req = request.into_inner();

        // Validar transição de status
        let status_validos = [
            "criado", "confirmado", "preparando",
            "enviado", "entregue", "cancelado",
        ];
        if !status_validos.contains(&req.novo_status.as_str()) {
            return Err(Status::invalid_argument(format!(
                "Status inválido: {}. Válidos: {:?}",
                req.novo_status, status_validos
            )));
        }

        let mut pedido = self
            .pedidos
            .get_mut(&req.pedido_id)
            .ok_or_else(|| Status::not_found("Pedido não encontrado"))?;

        let status_anterior = pedido.status.clone();
        pedido.status = req.novo_status.clone();
        pedido.atualizado_em = Utc::now().to_rfc3339();

        tracing::info!(
            pedido_id = %req.pedido_id,
            de = %status_anterior,
            para = %req.novo_status,
            "Status atualizado"
        );

        // Notificar observadores
        let _ = self.notificacoes.send(StatusUpdate {
            pedido_id: req.pedido_id,
            status_anterior,
            status_novo: req.novo_status,
            timestamp: Utc::now().to_rfc3339(),
        });

        Ok(Response::new(PedidoResponse::from(pedido.value())))
    }

    type ObservarPedidoStream =
        Pin<Box<dyn Stream<Item = Result<StatusUpdate, Status>> + Send>>;

    async fn observar_pedido(
        &self,
        request: Request<BuscarPedidoRequest>,
    ) -> Result<Response<Self::ObservarPedidoStream>, Status> {
        let pedido_id = request.into_inner().pedido_id;

        // Verificar se pedido existe
        if !self.pedidos.contains_key(&pedido_id) {
            return Err(Status::not_found("Pedido não encontrado"));
        }

        let rx = self.notificacoes.subscribe();
        let stream = BroadcastStream::new(rx)
            .filter_map(move |result| {
                match result {
                    Ok(update) if update.pedido_id == pedido_id => {
                        Some(Ok(update))
                    }
                    Err(_) => Some(Err(Status::internal("Erro no stream"))),
                    _ => None,
                }
            });

        Ok(Response::new(Box::pin(stream)))
    }
}
```

### Main com Health Check e Graceful Shutdown

```rust
use pedidos::servico_pedidos_server::ServicoPedidosServer;
use tokio::signal;
use tonic::transport::Server;

mod servico; // módulo com a implementação acima

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Logging estruturado em JSON para produção
    tracing_subscriber::fmt()
        .json()
        .with_env_filter(
            tracing_subscriber::EnvFilter::try_from_default_env()
                .unwrap_or_else(|_| "info".into()),
        )
        .init();

    let addr = "0.0.0.0:50051".parse()?;
    let servico = servico::ServicoPedidosImpl::new();

    tracing::info!(%addr, "Iniciando serviço de pedidos");

    // Health check para Kubernetes/Docker
    let (mut health_reporter, health_service) =
        tonic_health::server::health_reporter();
    health_reporter
        .set_serving::<ServicoPedidosServer<servico::ServicoPedidosImpl>>()
        .await;

    // Reflection para ferramentas como grpcurl
    let reflection_service = tonic_reflection::server::Builder::configure()
        .register_encoded_file_descriptor_set(
            tonic::include_file_descriptor_set!("pedidos_descriptor")
        )
        .build_v1()?;

    Server::builder()
        .add_service(health_service)
        .add_service(reflection_service)
        .add_service(ServicoPedidosServer::new(servico))
        .serve_with_shutdown(addr, async {
            shutdown_signal().await;
            tracing::info!("Sinal de shutdown recebido, finalizando...");
        })
        .await?;

    tracing::info!("Serviço encerrado com sucesso");
    Ok(())
}

/// Aguarda sinais de shutdown (SIGTERM, SIGINT).
async fn shutdown_signal() {
    let ctrl_c = async {
        signal::ctrl_c()
            .await
            .expect("Falha ao registrar handler Ctrl+C");
    };

    #[cfg(unix)]
    let terminate = async {
        signal::unix::signal(signal::unix::SignalKind::terminate())
            .expect("Falha ao registrar handler SIGTERM")
            .recv()
            .await;
    };

    #[cfg(not(unix))]
    let terminate = std::future::pending::<()>();

    tokio::select! {
        _ = ctrl_c => {},
        _ = terminate => {},
    }
}
```

### Dockerfile Multi-Stage para Produção

```dockerfile
# === Build ===
FROM rust:1.85-bookworm AS builder

# Instalar protoc para compilar .proto
RUN apt-get update && apt-get install -y protobuf-compiler && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Cache de dependências
COPY Cargo.toml Cargo.lock ./
COPY build.rs ./
COPY proto ./proto
RUN mkdir src && echo 'fn main() {}' > src/main.rs
RUN cargo build --release && rm -rf src

# Build real
COPY src ./src
RUN touch src/main.rs && cargo build --release

# === Runtime ===
FROM debian:bookworm-slim

RUN apt-get update \
    && apt-get install -y --no-install-recommends ca-certificates curl \
    && rm -rf /var/lib/apt/lists/*

RUN useradd --create-home --shell /bin/bash app
USER app

COPY --from=builder /app/target/release/servico-pedidos /usr/local/bin/

EXPOSE 50051

# Health check via grpc-health-probe
HEALTHCHECK --interval=15s --timeout=5s --retries=3 \
    CMD grpc_health_probe -addr=:50051 || exit 1

ENV RUST_LOG=info
ENTRYPOINT ["servico-pedidos"]
```

### Docker Compose com Serviços Dependentes

```yaml
version: "3.8"

services:
  pedidos:
    build: .
    ports:
      - "50051:50051"
    environment:
      RUST_LOG: "info,servico_pedidos=debug"
      DATABASE_URL: "postgres://app:senha@postgres:5432/pedidos"
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started

  postgres:
    image: postgres:16
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: senha
      POSTGRES_DB: pedidos
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  pgdata:
```

## Padrões Essenciais para Produção

### Retry com Backoff Exponencial

```rust
use std::time::Duration;
use tokio::time::sleep;

async fn com_retry<F, Fut, T, E>(
    operacao: F,
    max_tentativas: u32,
) -> Result<T, E>
where
    F: Fn() -> Fut,
    Fut: std::future::Future<Output = Result<T, E>>,
    E: std::fmt::Display,
{
    let mut tentativa = 0;
    loop {
        match operacao().await {
            Ok(resultado) => return Ok(resultado),
            Err(e) => {
                tentativa += 1;
                if tentativa >= max_tentativas {
                    tracing::error!(%e, "Máximo de tentativas excedido");
                    return Err(e);
                }
                let delay = Duration::from_millis(100 * 2u64.pow(tentativa));
                tracing::warn!(
                    %e, tentativa, delay_ms = %delay.as_millis(),
                    "Tentativa falhou, retrying..."
                );
                sleep(delay).await;
            }
        }
    }
}
```

### Middleware de Métricas com Tower

```rust
use std::time::Instant;
use tower::Layer;

#[derive(Clone)]
pub struct MetricasLayer;

impl<S> Layer<S> for MetricasLayer {
    type Service = MetricasMiddleware<S>;

    fn layer(&self, inner: S) -> Self::Service {
        MetricasMiddleware { inner }
    }
}

#[derive(Clone)]
pub struct MetricasMiddleware<S> {
    inner: S,
}

// A implementação completa registraria métricas em Prometheus/OpenTelemetry
// usando a crate `metrics` para counters e histograms.
```

## Empresas Usando Rust para Microsserviços

- **Discord**: Migrou serviços críticos de Go para Rust, reduzindo latência p99 e uso de memória
- **Cloudflare**: Centenas de microsserviços Rust no edge processando trilhões de requests
- **AWS**: Serviços internos, incluindo componentes do S3, Lambda e IAM
- **Dropbox**: Componentes de sincronização e storage
- **Figma**: Servidor multiplayer para colaboração em tempo real
- **1Password**: Backend de criptografia e sincronização
- **Fly.io**: Runtime de aplicações distribuídas
- **Linkerd**: Data plane proxy (microsserviço de infraestrutura)

## Como Começar

1. **Fundamentos de Rust**: [Tutorial de primeiros passos](/tutoriais/primeiros-passos/) e [concorrência](/tutoriais/concorrencia/)
2. **API REST**: Comece com uma [API REST simples com Axum](/tutoriais/api-rest-axum/) antes de ir para gRPC
3. **Servidor HTTP**: Pratique com a [receita de servidor HTTP](/receitas/criar-servidor-http/)
4. **Docker**: Configure seu [ambiente Docker para Rust](/instalacao/docker/)
5. **Banco de dados**: Integre com [PostgreSQL](/tutoriais/rust-postgresql/)
6. **Tratamento de erros**: Essencial para produção — [tutorial completo](/tutoriais/tratamento-de-erros/)

## Conclusão

Rust é uma escolha excelente para microsserviços que precisam entregar alta performance com baixo consumo de recursos. A combinação de gRPC com Tonic, observabilidade com tracing/OpenTelemetry e deploy otimizado com Docker multi-stage cria um stack de produção robusto. Se seus microsserviços precisam processar milhares de requisições por segundo com latência previsível e consumo mínimo de memória, Rust oferece o melhor custo-benefício entre performance e produtividade.

---

## Veja Também

- [Guia: Docker para Rust](/instalacao/docker/) — Imagens otimizadas para produção
- [Receita: Criar Servidor HTTP](/receitas/criar-servidor-http/) — Fundamentos de servidores em Rust
- [Tutorial: API REST com Axum](/tutoriais/api-rest-axum/) — REST como alternativa ao gRPC
- [Rust para Desenvolvimento Web](/artigos/rust-para-web/) — Ecossistema web completo
- [Rust para DevOps](/artigos/rust-para-devops/) — CI/CD e infraestrutura
- [Rust para APIs GraphQL](/artigos/rust-para-graphql/) — Alternativa ao REST e gRPC
- [Receita: Variáveis de Ambiente](/receitas/variaveis-ambiente/) — Configuração de serviços
- [Vagas Rust no Brasil](/vagas/) — Oportunidades em empresas que usam microsserviços
- [Go Brasil](https://golang.com.br) — Go é a outra grande linguagem para microsserviços — compare e escolha
