skip to content
Diario da Aldeia Viva

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

  1. Adicione o crate zmq ao seu Cargo.toml:

    [dependencies]
    zmq = "0.10.0"
  2. 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.