Como processar consultas de CPF de forma assíncrona em Rust com Tokio

Processe múltiplas consultas de CPF de forma assíncrona em Rust usando Tokio para máxima performance e throughput.

Redação CPFHub.io
Redação CPFHub.io
··6 min de leitura
Como processar consultas de CPF de forma assíncrona em Rust com Tokio

Processar consultas de CPF de forma assíncrona em Rust com Tokio permite executar dezenas de chamadas à API da CPFHub.io em paralelo, aproveitando o modelo de concorrência sem threads pesadas do SO. Com semáforos do Tokio você controla quantas requisições correm simultaneamente, distribuindo o volume ao longo do tempo — sem precisar se preocupar com bloqueio por quota, já que a API nunca retorna HTTP 429: consultas além do plano são simplesmente cobradas a R$0,15 cada.

Introdução

Quando é necessário processar um grande volume de consultas de CPF, a execução sequencial pode se tornar um gargalo significativo. O Tokio, runtime assíncrono mais popular de Rust, permite executar múltiplas consultas simultaneamente sem o overhead de threads do sistema operacional.


Configurando o ambiente assíncrono

Adicione as dependências necessárias para processamento assíncrono:

// Cargo.toml
[dependencies]
tokio = { version = "1", features = ["full"] }
reqwest = { version = "0.12", features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
futures = "0.3"
CratePapel no processamento
tokioRuntime assíncrono e primitivas de concorrência
reqwestCliente HTTP assíncrono
futuresUtilitários para trabalhar com futures e streams
serdeDesserialização da resposta JSON

Para a documentação completa do Tokio e seu modelo de concorrência, consulte o guia oficial do Tokio.


Criando o cliente compartilhado

Utilize um reqwest::Client compartilhado para reutilizar conexões TCP entre as consultas:

use reqwest::header::{HeaderMap, HeaderValue};
use serde::Deserialize;
use std::sync::Arc;

#[derive(Debug, Deserialize)]
pub struct CpfResponse {
    pub success: bool,
    pub data: CpfData,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CpfData {
    pub cpf: String,
    pub name: String,
    pub name_upper: String,
    pub gender: String,
    pub birth_date: String,
    pub day: String,
    pub month: String,
    pub year: String,
}

pub struct CpfClient {
    client: reqwest::Client,
    api_key: String,
}

impl CpfClient {
    pub fn new(api_key: String) -> Arc<Self> {
    let client = reqwest::Client::builder()
    .pool_max_idle_per_host(20)
    .timeout(std::time::Duration::from_secs(10))
    .build()
    .expect("Falha ao criar cliente HTTP");

    Arc::new(Self { client, api_key })
    }

    pub async fn consultar(&self, cpf: &str) -> Result<CpfResponse, String> {
    let mut headers = HeaderMap::new();
    headers.insert(
    "x-api-key",
    HeaderValue::from_str(&self.api_key).map_err(|e| e.to_string())?,
    );

    let url = format!("https://api.cpfhub.io/cpf/{}", cpf);

    let response = self.client
    .get(&url)
    .headers(headers)
    .send()
    .await
    .map_err(|e| format!("Erro na requisição: {}", e))?;

    response.json::<CpfResponse>()
    .await
    .map_err(|e| format!("Erro ao desserializar: {}", e))
    }
}

Processamento em lote com join_all

Para processar um lote de CPFs em paralelo, utilize futures::future::join_all:

use futures::future::join_all;

async fn processar_lote(
    client: &Arc<CpfClient>,
    cpfs: Vec<String>,
) -> Vec<(String, Result<CpfResponse, String>)> {
    let futures: Vec<_> = cpfs.into_iter().map(|cpf| {
    let client = Arc::clone(client);
    async move {
    let resultado = client.consultar(&cpf).await;
    (cpf, resultado)
    }
    }).collect();

    join_all(futures).await
}

Controlando concorrência com semáforo

Use um semáforo do Tokio para limitar a concorrência e distribuir o volume de consultas de forma controlada:

use tokio::sync::Semaphore;

async fn processar_com_limite(
    client: &Arc<CpfClient>,
    cpfs: Vec<String>,
    max_concurrent: usize,
) -> Vec<(String, Result<CpfResponse, String>)> {
    let semaphore = Arc::new(Semaphore::new(max_concurrent));
    let mut handles = Vec::new();

    for cpf in cpfs {
    let client = Arc::clone(client);
    let semaphore = Arc::clone(&semaphore);

    let handle = tokio::spawn(async move {
    let _permit = semaphore.acquire().await
    .expect("Semáforo fechado");

    let resultado = client.consultar(&cpf).await;
    (cpf, resultado)
    });

    handles.push(handle);
    }

    let mut resultados = Vec::new();
    for handle in handles {
    if let Ok(resultado) = handle.await {
    resultados.push(resultado);
    }
    }

    resultados
}

Exemplo completo com relatório

Monte o programa que processa um lote e gera um relatório dos resultados:

#[tokio::main]
async fn main() {
    let api_key = std::env::var("CPFHUB_API_KEY")
    .expect("Defina CPFHUB_API_KEY");

    let client = CpfClient::new(api_key);

    let cpfs: Vec<String> = vec![
    "12345678900", "98765432100", "11122233344",
    "55566677788", "99988877766",
    ].into_iter().map(String::from).collect();

    let total = cpfs.len();
    println!("Processando {} CPFs com limite de 3 simultâneos...", total);

    let inicio = std::time::Instant::now();
    let resultados = processar_com_limite(&client, cpfs, 3).await;
    let duracao = inicio.elapsed();

    let mut sucessos = 0;
    let mut falhas = 0;

    for (cpf, resultado) in &resultados {
    match resultado {
    Ok(r) if r.success => {
    println!("[OK] {} -> {}", cpf, r.data.name);
    sucessos += 1;
    }
    Ok(_) => {
    println!("[--] {} -> Não encontrado", cpf);
    falhas += 1;
    }
    Err(e) => {
    println!("[ER] {} -> {}", cpf, e);
    falhas += 1;
    }
    }
    }

    println!("\nRelatório:");
    println!(" Total: {}", total);
    println!(" Sucessos: {}", sucessos);
    println!(" Falhas: {}", falhas);
    println!(" Tempo: {:.2?}", duracao);
}

Perguntas frequentes

A API CPFHub.io retorna HTTP 429 quando muitas consultas assíncronas são disparadas ao mesmo tempo?

Não. A API da CPFHub.io nunca retorna HTTP 429 nem bloqueia requisições por volume, independentemente de quantas chamadas assíncronas você dispare em paralelo. Quando o volume mensal do plano é ultrapassado, as consultas extras são cobradas a R$0,15 cada. O semáforo do Tokio neste artigo serve para controlar o consumo de recursos locais (conexões, memória), não para evitar bloqueio da API.

Por que usar Arc<CpfClient> em vez de criar um cliente por tarefa assíncrona?

O reqwest::Client reutiliza um pool de conexões TCP, o que reduz significativamente a latência e o consumo de recursos. Criar um cliente por tarefa desperdiça esse pool e pode esgotar as portas do SO em volume alto. Com Arc, todas as tasks compartilham o mesmo cliente de forma segura entre threads.

Qual é a diferença prática entre join_all e o semáforo com tokio::spawn?

join_all dispara todas as futures simultaneamente, sem limite de concorrência — adequado para lotes pequenos. O semáforo com tokio::spawn limita quantas consultas rodam ao mesmo tempo, o que é mais adequado para lotes grandes onde você quer controlar o uso de memória e conexões. Para a maioria dos casos de produção com a CPFHub.io, use o semáforo.

Como medir o throughput real de consultas assíncronas de CPF em Rust?

Use std::time::Instant::now() antes e elapsed() depois do lote, como no exemplo completo deste artigo. Para métricas mais detalhadas em produção, considere integrar com a crate metrics ou exportar para Prometheus — veja o artigo sobre observabilidade na integração com API de CPF.


Conclusão

O processamento assíncrono com Tokio permite escalar consultas de CPF de forma eficiente, mantendo controle sobre a concorrência e o consumo de recursos. A combinação de semáforos, futures e o cliente HTTP compartilhado resulta em alto throughput com uso mínimo de memória e conexões. A API da CPFHub.io nunca bloqueia por volume — o que significa que sua lógica de retry não precisa lidar com HTTP 429, simplificando o código.

Cadastre-se em cpfhub.io — 50 consultas mensais gratuitas, sem cartão de crédito — e comece a processar consultas de CPF em escala com Rust e Tokio.

CPFHub.io

Pronto para integrar a API?

50 consultas gratuitas para testar agora. Sem cartão de crédito. Acesso imediato à documentação.

Redação CPFHub.io

Sobre a redação

Redação CPFHub.io

Time editorial especializado em APIs de CPF, identidade digital e compliance no mercado brasileiro. Produzimos guias técnicos, análises regulatórias e tutoriais sobre LGPD e KYC para desenvolvedores e líderes de produto.

WhatsAppFale conosco via WhatsApp