Channel (IModel) | RabbitMQ | CSharp
- Channel (IModel) | RabbitMQ | CSharp
O que é?
Representa uma sessão com o servidor RabbitMQ e é onde ocorre a maioria das operações (como declarar filas, publicar mensagens e assinar)
Ciclo de vida | Channel (IModel) | RabbitMQ
- Recomenda ser
scopedporque não éthread-safee se tiver vários consumers usando ao mesmo tempo pode dar erros inesperados como:- race condition
- ack errado
- channel fechado inesperadamente
- mensagens duplicadas
Visão Geral
No client do RabbitMQ para .NET, existem dois conceitos principais:
- Connection (
IConnection) - Channel (
IChannel/IModel)
Cada um possui características diferentes de concorrência:
- Connection é thread-safe
- Channel NÃO é thread-safe
Isso significa que múltiplas threads acessando o mesmo channel simultaneamente podem causar comportamento indefinido.
Como o Channel Funciona Internamente
Quando você executa um publish:
await channel.BasicPublishAsync(...);
Isso não envia apenas um bloco de dados. Internamente, o client envia múltiplos frames no protocolo AMQP:
[Frame: Method - BasicPublish]
[Frame: Header - propriedades]
[Frame: Body - payload]
Esses frames precisam ser enviados:
- em ordem
- sem interrupção
- respeitando o contexto da mensagem
Problema de Concorrência
Considere duas threads usando o mesmo channel:
Thread A:
Frame A1
Frame A2
Frame A3
Thread B:
Frame B1
Frame B2
Frame B3
Sem controle de concorrência, o envio pode virar:
Frame A1
Frame B1
Frame A2
Frame B2
Frame A3
Frame B3
Isso quebra o protocolo AMQP, pois o servidor não consegue reconstruir corretamente as mensagens.
Analogia com NetworkStream
O comportamento é equivalente a múltiplas threads escrevendo no mesmo stream TCP:
// Thread 1
stream.Write("HELLO");
// Thread 2
stream.Write("WORLD");
Resultado possível:
HEWOLLRLOD
No caso do RabbitMQ, o problema é mais grave, pois envolve protocolo estruturado e não apenas texto.
Natureza Stateful do Channel
O channel mantém estado interno durante a comunicação:
- sequência de frames
- controle de envio
- confirmações (ack/nack)
- buffers internos
Esse estado assume acesso sequencial.
Quando múltiplas threads escrevem ao mesmo tempo:
- o estado interno fica inconsistente
- a sequência de frames é corrompida
- o servidor pode fechar a conexão
Problemas Observados em Produção
Uso concorrente de channel pode causar:
- mensagens corrompidas
- exceções intermitentes
- fechamento inesperado de conexão
- perda de mensagens
- comportamento não determinístico
Exemplo Incorreto
app.MapPost("/resposta", async (HttpRequest request, IGerenciadorConexaoMensageria gerenciador) =>
{
var body = await ReadAllBytesAsync(request.Body);
await gerenciador.Channel.BasicPublishAsync(
exchange: "",
routingKey: "fila",
mandatory: false,
basicProperties: null,
body: body);
return Results.Ok();
});
Problema:
gerenciador.Channelé compartilhado- múltiplas requisições usam o mesmo channel simultaneamente
Abordagens Corretas
Channel por requisição
app.MapPost("/resposta", async (HttpRequest request, IGerenciadorConexaoMensageria gerenciador) =>
{
var body = await ReadAllBytesAsync(request.Body);
var channel = await gerenciador.Connection.CreateChannelAsync();
await channel.BasicPublishAsync(
exchange: "",
routingKey: "fila",
mandatory: false,
basicProperties: null,
body: body);
await channel.CloseAsync();
return Results.Ok();
});
Pool de Channels
- reutiliza múltiplos channels
- evita criação excessiva
- mantém isolamento entre threads
Regra Prática
-
Nunca compartilhe um channel entre múltiplas threads sem sincronização
-
Prefira:
- um channel por operação
- ou pool de channels