Usando Filas para Processar Transações PIX com ZMQ e Rust
/ 3 min read
Um bom exemplo de uso de filas é em sistemas de pagamentos, como o PIX, para gerenciar requisições de forma assíncrona e garantir resiliência e escalabilidade.
Filas e Assincronismo: Processando Requisições PIX com ZMQ em Rust
Quando pensamos em pagamentos instantâneos, como o PIX, garantir baixa latência e alta disponibilidade é essencial. Porém, o que acontece nos bastidores? Uma abordagem eficiente é usar filas para gerenciar as requisições de pagamento.
Neste post, vamos explorar como utilizar o ZeroMQ (ZMQ) com Rust para criar um sistema que simula o envio e o processamento de transações PIX.
O Cenário
Imagine um servidor que recebe pedidos de transações PIX. Esses pedidos precisam ser processados em ordem, mas o processamento pode levar algum tempo, como validações de segurança, consulta ao saldo do cliente ou comunicação com outros serviços. Usando filas, podemos desacoplar a recepção do pedido do processamento.
A Implementação
Setup do ZMQ no Rust
-
Adicione o crate
zmq
ao seuCargo.toml
:[dependencies]zmq = "0.10.0" -
Crie um servidor que age como produtor (recebe as requisições) e um consumidor (processa as requisições).
Servidor Produtor (Request Queue)
Este servidor irá receber os pedidos de PIX e adicioná-los à fila:
use zmq;
pub fn run_pusher() { let context = zmq::Context::new(); let pusher = context.socket(zmq::PUSH).unwrap();
// Bind na porta 5555 pusher.bind("tcp://*:5555").expect("Não foi possível iniciar o pusher");
println!("pusher iniciado na porta 5555");
// Aguarda 1 segundo para permitir que os pullers se conectem std::thread::sleep(std::time::Duration::from_secs(1));
let items = vec![ ("Transação 1: João para Maria", 50), ("Transação 2: Pedro para Ana", 100), ("Transação 3: Carla para Lucas", 75), ];
for (item, price) in items { let message = format!("{} {}", item, price); println!("Enviando: {}", message); pusher.send(&message, 0).unwrap(); std::thread::sleep(std::time::Duration::from_secs(1)); // Simula atualizações em tempo real }}
Servidor Consumidor (Worker)
Este consumidor irá processar as requisições em ordem:
pub fn run_puller() { let context = zmq::Context::new(); let puller = context.socket(zmq::PULL).unwrap();
// Conectando ao pusher puller.connect("tcp://localhost:5555").expect("Não foi possível conectar ao pusher");
println!("puller conectado e aguardando mensagens...");
loop { let msg = puller.recv_string(0).unwrap().unwrap(); println!("Recebido: {}", msg); }}
No arquivo principal para ficar mais fácil de executar
mod pix;
use std::env;
fn main() { let args: Vec<String> = env::args().collect();
if args.len() < 2 { eprintln!("Uso: cargo run -- (pusher|puller)"); return; }
match args[1].as_str() { "pusher" => pix::run_pusher(), "puller" => pix::run_puller(), _ => eprintln!("Comando desconhecido: use 'pusher' ou 'puller'"), }}
Por que ZMQ?
- Alto desempenho: Comunicação eficiente entre processos.
- Flexibilidade: Suporte a padrões como PUSH/PULL, PUB/SUB.
- Escalabilidade: Fácil integração com múltiplos consumidores.
Expansões Possíveis
- Logs: Adicionar logs para monitorar as mensagens processadas.
- Escalabilidade: Conectar múltiplos consumidores para processar mensagens em paralelo.
- Persistência: Armazenar mensagens em um banco de dados caso o consumidor falhe.
Com isto entendemos a base do funcionamento de uma fila do Pix. Mas bem básico mesmo.