Luis Tavarez · Noticias

C#: Kafka frente a RabbitMQ, qué significa confirmar progreso

Un modelo local distingue offsets por partición y acknowledgements por canal. Falla antes de confirmar y comprueba el reintento.

Luis Tavarez ·

Empezar por la semántica

Un consumidor no debería confirmar trabajo que falló. Este programa C# modela esa regla con dos adaptadores de progreso. No abre conexiones, no incluye clientes de brokers y no compara rendimiento: es un laboratorio del contrato de procesamiento.

Kafka: posición en una partición

El modelo representa un consumidor clásico de una sola partición, procesada secuencialmente. Tras procesar el offset 4, guarda 5 como siguiente posición. Confirmar una posición posterior puede saltar trabajo previo si introduces procesamiento desordenado sin controlar los huecos. Debes diseñar también rebalances y recuperación. Kafka incluye otras capacidades, como share groups; esta comparación no pretende cubrirlas.

RabbitMQ: una entrega en un canal

El segundo adaptador modela el acknowledgement individual de un delivery tag en un canal. No es un offset global. Los acknowledgements del consumidor y los publisher confirms resuelven tramos diferentes del recorrido; uno no reemplaza al otro. Streams y tipos de cola también cambian las opciones disponibles.

El fallo que se comprueba

La primera entrega termina; la segunda lanza un error antes de confirmar. El estado permanece en el primer progreso y solo avanza tras el reintento exitoso. No hay efectos de negocio duraderos en este modelo ni una promesa de exactly-once.

Decidir y completar la integración

Para un historial retenido con consumidores independientes, evalúa retención y grupos de Kafka. Para distribución y enrutamiento de tareas, evalúa exchanges, colas, reintentos y límites de RabbitMQ. Valida las capacidades concretas de cada versión. Antes de producción añade clientes reales, idempotencia persistente, límites de reintento y pruebas de caída entre guardar el efecto y confirmar.

C# · Laboratorio para desarrolladores

Arquitectura, código y práctica

LT Architecture StudioDiagrama creado con nuestra herramienta. Explóralo y adáptalo a tu proyecto.
C#: Kafka frente a RabbitMQ, qué significa confirmar progreso

Ejecutar el ejemplo

.NET SDK 9. Descarga Program.cs y Example.csproj en la misma carpeta. No requiere Kafka, RabbitMQ ni paquetes NuGet adicionales.

dotnet run --project Example.csproj

Salida esperada: OK: failure unconfirmed; retry completed

Código del ejemplo
csharp
// Local contract model, not a Kafka or RabbitMQ client.
var kafka = new KafkaPartitionProgress();
var rabbit = new RabbitChannelProgress();
var first = new Delivery("order-1", 4, 17);
var second = new Delivery("order-2", 5, 18);
var handler = new Handler();
foreach (IProgress progress in new IProgress[] { kafka, rabbit })
{
    handler.Process(first, progress, fail: false);
    try { handler.Process(second, progress, fail: true); }
    catch (InvalidOperationException) { /* Retry the failed delivery before advancing. */ }
}
if (kafka.NextOffset != 5 || !rabbit.Acknowledged.SetEquals(new ulong[] { 17 }))
    throw new Exception("Failed work advanced progress");
handler.Process(second, kafka, fail: false);
handler.Process(second, rabbit, fail: false);
if (kafka.NextOffset != 6 || !rabbit.Acknowledged.Contains(18))
    throw new Exception("Successful retry did not advance progress");
Console.WriteLine("OK: failure unconfirmed; retry completed");

record Delivery(string Id, long Offset, ulong Tag);
interface IProgress { void Complete(Delivery delivery); }
sealed class KafkaPartitionProgress : IProgress
{
    // Exactly one partition, sequential processing, no rebalance in this model.
    public long NextOffset { get; private set; } = 4;
    public void Complete(Delivery delivery) => NextOffset = delivery.Offset + 1;
}
sealed class RabbitChannelProgress : IProgress
{
    // Exactly one channel; delivery tags are scoped to this channel.
    public HashSet<ulong> Acknowledged { get; } = new();
    public void Complete(Delivery delivery) => Acknowledged.Add(delivery.Tag);
}
sealed class Handler
{
    public void Process(Delivery delivery, IProgress progress, bool fail)
    {
        if (fail) throw new InvalidOperationException("Processing failed");
        // A real adapter must persist its business effect before confirming progress.
        progress.Complete(delivery);
    }
}

Ejercicio para continuar

Diseña una prueba de integración que detenga el proceso después de guardar el efecto y antes de confirmar. Define dónde deduplicarás una entrega repetida.