Introdução: Por que Rust para microsserviços?
A arquitetura de microsserviços já se tornou padrão para construir sistemas distribuídos escaláveis. Geralmente, escolhe-se Go, Java ou Node.js para sua implementação. No entanto, nos últimos anos, Rust — uma linguagem de programação de sistemas que combina o desempenho do C++ com a segurança de memória — tem atraído cada vez mais atenção. Neste artigo em pythonlib.ru, vamos analisar como criar microsserviços em Rust, quais bibliotecas usar e como organizar a arquitetura.
Rust é ideal para microsserviços graças a:
- Ausência de garbage collector (baixa latência e consumo de memória previsível);
- Trabalho seguro com memória em tempo de compilação;
- Excelente suporte a assincronicidade através do ecossistema
Tokio; - Tamanho pequeno do arquivo binário final (ideal para conteinerização).
Arquitetura de um microsserviço em Rust
Um microsserviço típico em Rust consiste em várias camadas:
- Servidor HTTP (geralmente baseado em
actix-webouaxum); - Lógica de negócio (funções puras em Rust);
- Camada de dados (trabalho com BD via
sqlxoudiesel); - Runtime assíncrono (
Tokio); - Serialização (na maioria das vezes
serdepara JSON).
Vamos considerar a arquitetura básica usando como exemplo um serviço simples de gerenciamento de usuários.
1. Configuração do projeto e dependências
Crie um novo projeto Cargo e adicione as dependências no Cargo.toml:
[package]name = "user-service"version = "0.1.0"edition = "2021"
[dependencies]actix-web = "4"serde = { version = "1", features = ["derive"] }serde_json = "1"tokio = { version = "1", features = ["full"] }sqlx = { version = "0.7", features = ["runtime-tokio", "postgres"] }dotenv = "0.15"2. Criando a estrutura principal do servidor
Vamos definir os modelos de dados e iniciar o servidor HTTP:
use actix_web::{web, App, HttpServer, Responder};use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone)]struct User { id: u32, name: String, email: String,}
// Armazenamento in-memory para exemploasync fn get_users() -> impl Responder { let users = vec![ User { id: 1, name: "Alice".into(), email: "alice@example.com".into() }, User { id: 2, name: "Bob".into(), email: "bob@example.com".into() }, ]; web::Json(users)}
#[actix_web::main]async fn main() -> std::io::Result<()> { println!("Starting user-service on http://127.0.0.1:8080");
HttpServer::new(|| { App::new() .route("/users", web::get().to(get_users)) }) .bind("127.0.0.1:8080")? .run() .await}Este é um microsserviço mínimo. Observe: #[actix_web::main] é uma macro que inicia o runtime assíncrono Tokio.
3. Trabalho assíncrono com banco de dados
Um microsserviço real deve persistir dados. Usaremos sqlx para PostgreSQL. Crie um arquivo .env com a URL de conexão:
DATABASE_URL=postgres://user:password@localhost/users_dbAgora vamos escrever uma função para obter usuários do BD:
use sqlx::PgPool;
async fn get_users_from_db(pool: web::Data<PgPool>) -> impl Responder { // Executa a consulta SQL e mapeia o resultado para a estrutura User let users: Vec<User> = sqlx::query_as::<_, User>("SELECT id, name, email FROM users") .fetch_all(pool.get_ref()) .await .unwrap_or_default();
web::Json(users)}
#[actix_web::main]async fn main() -> std::io::Result<()> { dotenv::dotenv().ok(); let database_url = std::env::va