# 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.

**Publicado:** 03/11/2024
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/como-processar-consultas-cpf-assincrono-rust-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:

```rust
// Cargo.toml
[dependencies]
tokio = { version = "1", features = ["full"] }
reqwest = { version = "0.12", features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
futures = "0.3"
```

| Crate | Papel no processamento |
|---|---|
| `tokio` | Runtime assíncrono e primitivas de concorrência |
| `reqwest` | Cliente HTTP assíncrono |
| `futures` | Utilitários para trabalhar com futures e streams |
| `serde` | Desserialização da resposta JSON |

Para a documentação completa do Tokio e seu modelo de concorrência, consulte o [guia oficial do Tokio](https://tokio.rs/tokio/tutorial).

---

## Criando o cliente compartilhado

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

```rust
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`:

```rust
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:

```rust
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:

```rust
#[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](https://cpfhub.io/blog/observabilidade-integracao-api-cpf).

### Leia também

- [Como validar CPF em tempo real usando Rust e APIs REST](https://cpfhub.io/blog/como-validar-cpf-tempo-real-rust-apis-rest)
- [Como consumir a API de CPF em Rust usando reqwest](https://cpfhub.io/blog/como-consumir-api-cpf-rust-reqwest)
- [Como processar consultas de CPF em paralelo usando Goroutines](https://cpfhub.io/blog/processar-consultas-cpf-paralelo-goroutines)
- [Observabilidade (logs, métricas, traces) na integração com API de CPF](https://cpfhub.io/blog/observabilidade-integracao-api-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](https://www.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.

