Introdução: por que Rust para microsserviços?
A arquitetura de microsserviços tornou-se o padrão para sistemas distribuídos modernos. No entanto, a escolha da linguagem de programação para implementar microsserviços é uma decisão crítica. Rust oferece uma combinação única de desempenho de C/C++ e segurança de memória, tornando-o um candidato ideal para serviços de alta carga.
Neste artigo, vamos analisar como construir um microsserviço em Rust usando os frameworks populares Actix-web e Tokio. Você aprenderá sobre princípios arquiteturais, processamento assíncrono de requisições e obterá exemplos de código funcionais.
Arquitetura de microsserviço em Rust
Princípios básicos
Um microsserviço em Rust geralmente é construído em torno dos seguintes componentes:
- Servidor HTTP — processamento de requisições recebidas (Actix-web, Warp, Axum).
- Runtime assíncrono — gerenciamento de concorrência (Tokio, async-std).
- Lógica de negócio — módulos isolados com limites claros.
- Armazenamento de dados — interação com banco de dados via SQLx ou Diesel.
- Comunicação entre serviços — HTTP/gRPC ou filas de mensagens (RabbitMQ, Kafka).
Estrutura do projeto
Estrutura recomendada para um microsserviço em Rust:
my_microservice/├── Cargo.toml├── src/│ ├── main.rs # Ponto de entrada, inicialização do servidor│ ├── routes/ # Definição de rotas│ ├── handlers/ # Manipuladores de requisições│ ├── models/ # Estruturas de dados (DTOs, entidades)│ ├── services/ # Lógica de negócio│ ├── repositories/ # Camada de acesso a dados│ └── config/ # Configuração (env, arquivos)└── tests/ # Testes de integraçãoExemplo prático: serviço de encurtamento de links
Vamos considerar a criação de um microsserviço simples para encurtar URLs. Ele receberá links longos e retornará identificadores curtos.
Passo 1: Configuração do projeto
Crie um novo projeto Cargo e adicione as dependências no Cargo.toml:
[package]name = "url-shortener"version = "0.1.0"edition = "2021"
[dependencies]actix-web = "4"actix-rt = "2"serde = { version = "1", features = ["derive"] }serde_json = "1"tokio = { version = "1", features = ["full"] }redis = { version = "0.23", features = ["tokio-comp"] }uuid = { version = "1", features = ["v4"] }dotenv = "0.15"env_logger = "0.10"log = "0.4"Passo 2: Modelos de dados
Vamos definir as estruturas de requisição e resposta:
// models/mod.rsuse serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize)]pub struct CreateUrlRequest { pub url: String,}
#[derive(Debug, Serialize)]pub struct CreateUrlResponse { pub short_code: String, pub original_url: String,}
#[derive(Debug, Serialize)]pub struct ErrorResponse { pub error: String,}Passo 3: Camada de serviço
Vamos implementar a lógica de negócio usando Redis para armazenamento:
// services/url_service.rsuse redis::AsyncCommands;use uuid::Uuid;
pub struct UrlService { redis_client: redis::Client,}
impl UrlService { pub fn new(redis_client: redis::Client) -> Self { Self { redis_client } }
pub async fn create_short_url(&self, original_url: &str) -> Result<String, String> { let short_code = Uuid::new_v4().to_string()[..8].to_string(); let mut conn = self.redis_client .get_multiplexed_async_connection() .await .map_err(|e| format!("Redis connection error: {}", e))?;
// Salva no Redis: chave — short_code, valor — original_url conn.set_ex(&short_code, original_url, 86400) // TTL 24 horas .await .map_err(|e| format!("Redis