Introdução
Em sistemas distribuídos, operações de I/O como requisições HTTP, consultas a bancos de dados e streaming de mensagens são fundamentais para fluxos de trabalho. Embora o padrão async/await
do .NET gerencie essas operações com eficiência, integrar manipuladores assíncronos ao Brighter requer configuração explícita. Este artigo explora como habilitar manipuladores assíncronos no Brighter e explica a arquitetura por trás de seu design, baseada nos padrões Reactor e Proactor.
Manipuladores Assíncronos sem Configuração
Ao usar SqsSubscription
ou KafkaSubscription
do Brighter, tentar usar RequestHandlerAsync
sem configuração adequada resulta em erro:
Paramore.Brighter.ConfigurationException: Error when building pipeline, see inner Exception for details
---> System.InvalidCastException: Unable to cast object of type 'GreetingHandler' to type 'Paramore.Brighter.IHandleRequests'.
Isso ocorre porque o Brighter usa o padrão Reactor(I/O síncrono) por padrão e não consegue inferir se seu manipulador requer o padrão Proactor (I/O assíncrono).
Solução: Habilitar Processamento Assíncrono
Para resolver, defina isAsync: true
na configuração da inscrição:
.AddServiceActivator(opt =>
{
opt.Subscriptions = [
new SqsSubscription<Greeting>(
new SubscriptionName("greeting-subscription"),
new ChannelName("greeting-queue"),
new RoutingKey("greeting.topic".ToValidSNSTopicName()),
bufferSize: 2,
isAsync: true) // Habilita processamento assíncrono
];
opt.ChannelFactory = new ChannelFactory(connection);
})
Isso permite usar RequestHandlerAsync
para operações não bloqueantes:
public class GreetingHandler(ILogger<GreetingHandler> logger) : RequestHandlerAsync<Greeting>
{
public override async Task<Greeting> HandleAsync(Greeting command)
{
logger.LogInformation("Processando {Name}", command.Name);
await Task.Delay(1_000); // Simula I/O assíncrono (ex: chamada HTTP)
logger.LogInformation("Olá, {Name}", command.Name);
return await base.HandleAsync(command);
}
}
Message pump(A Bomba de Mensagens): Núcleo do Processamento do Brighter
O Brighter usa um message pump single-threaded para garantir ordenação e evitar condições de corrida. O processo segue três etapas:
- GetMessage: Lê mensagens da fila.
- Translate Message: Desserializa para um tipo .NET.
- Dispatch Message: Roteia para o manipulador adequado.
Por Que usar Single-Threaded pumps?
Abordagens alternativas, como BlockingCollection
ou thread-per-message, introduzem problemas críticos:
Abordagem | Problemas |
---|---|
Multithread pump | Risco de reordenar mensagens, violando garantias FIFO. |
Thread Pool | Esgota threads sob carga, causando gargalos com semáforos. |
A solução do Brighter é uma single-threaded pumps, garantindo processamento em ordem sem contenção de threads.
Padrões Reactor vs. Proactor
A message pump do Brighter opera em dois modos, determinados pela flag isAsync
:
Reactor (isAsync: false
)
- I/O Síncrono: Processa mensagens sequencialmente em uma única thread.
- Desempenho Previsível: Evita overhead de thread pool e trocas de contexto.
- Limitação: Bloqueia em I/O, reduzindo throughput para operações longas.
Proactor (isAsync: true
)
-
I/O Assíncrono: Usa
async/await
para evitar bloqueios, aumentando throughput. -
Integração com Thread Pool: Aproveita o
SynchronizationContext
do .NET para preservar a ordem. - Trade-off: Overhead leve devido ao gerenciamento de estado assíncrono.
Por Que o Brighter Exige isAsync
?
O Brighter não detecta automaticamente se seu manipulador usa I/O síncrono ou assíncrono porque:
- Alocação de Recursos: A escolha entre Reactor/Proactor impacta threads e memória.
- Prevenção de Deadlocks: Manipuladores assíncronos exigem pipelines dedicados.
- Garantias de Desempenho: Configuração explícita otimiza throughput e latência.
Conclusão
A flag isAsync
do Brighter é uma escolha de design que equilibra desempenho e escalabilidade:
- Evite Erros em Runtime: Declare manipuladores assíncronos explicitamente.
- Preserve a Ordem: A single-threaded pumps garante processamento sequencial, mesmo com I/O assíncrono.
Alinhando-se a padrões estabelecidos, o Brighter oferece mensageria eficiente e thread-safe para sistemas distribuídos.