RabbitMQ .NET Client

repository·main·Indexed 25 days ago

https://github.com/rabbitmq/rabbitmq-dotnet-client

Official library for interacting with RabbitMQ brokers from .NET applications, enabling the implementation of messaging patterns such as publishing, consuming, and managing queues and exchanges. Includes support for OpenTelemetry instrumentation via the RabbitMQ.Client.OpenTelemetry library and provides compatibility for .NET Framework 4.5.1+ and .NET Core.

Tokens
5.6K
Snippets
8
Records
30
Agent score
81%

What's inside RabbitMQ .NET Client

  1. Instrument RabbitMQ .NET Client with OpenTelemetry

    main

    Use the RabbitMQ.Client.OpenTelemetry library to add OpenTelemetry instrumentation to your RabbitMQ .NET Client applications. This allows you to trace RabbitMQ operations and integrate them with other OpenTelemetry-compatible tools.

    To ensure distributed tracing works correctly across service boundaries, you must configure a CompositeTextMapPropagator using TraceContextPropagator and BaggagePropagator to follow the W3C specification for context propagation.

  2. Understand GH-1968 failure and slow signatures

    main

    When running the reproduction script, results are categorized into three main outcomes:

    1. FAIL: The test failed. The script groups these by reason. A true GH-1968 failure is identified by an OperationCanceledException. If the failure is due to an unrelated error (e.g., ObjectDisposedException), it is not considered a GH-1968 occurrence.
    2. slow: The test passed, but its duration exceeded the -SlowSeconds threshold. This is important because Connection.CloseAsync may raise non-abort timeouts up to the InternalConstants.DefaultConnectionCloseTimeout (30s), even if a shorter timeout was requested.
    3. PASS: The test completed successfully within the expected time and without errors.
  3. Understand connection shutdown and cancellation behavior

    main

    When a RabbitMQ connection is shutting down, the library uses ShutdownEventArgs.CancellationToken to signal to your handlers that the connection is going away.

    There are two primary paths for shutdown:

    1. Application Path: Triggered by the caller (e.g., calling AbortAsync). The CancellationToken provided by the caller is passed through to the handlers.
    2. Library Path: Triggered by the MainLoop (e.g., a dead socket). In this path, the library cancels its internal _mainLoopCts.Token to signal shutdown.

    Important: In the Library path, the shutdown token may arrive already-cancelled. Your shutdown cleanup logic must not be gated on this token (e.g., do not use await semaphore.WaitAsync(args.CancellationToken)), as this can cause deadlocks or hangs. Instead, use a bounded wait with CancellationToken.None and manually fault any pending tasks.

  4. Configure the RabbitMQ node for testing via RABBITMQ_RABBITMQCTL_PATH

    main

    The test suite requires a RabbitMQ node listening on localhost:5672 (and the default TLS port for TLS tests). The tests use the rabbitmqctl tool to manage the node. You can specify the location of this tool using the RABBITMQ_RABBITMQCTL_PATH environment variable.

    Windows (Binary Release)

    If using a RabbitMQ binary release on Windows, set the path to the .bat file:

    $env:RABBITMQ_RABBITMQCTL_PATH='C:\Program Files\RabbitMQ Server\rabbitmq_server-X.Y.Z\sbin\rabbitmqctl.bat'

    MacOS and Linux (Binary Release)

    Add the RabbitMQ CLI tools to your PATH so rabbitmqctl can be invoked directly.

    MacOS and Linux (Source Build)

    If running a node built from source, set the path to the rabbitmqctl script:

    RABBITMQ_RABBITMQCTL_PATH=/path/to/rabbitmqctl dotnet test projects/Test/Unit/Unit.csproj

    Using Docker

    To run tests against a RabbitMQ node in a Docker container, set RABBITMQ_RABBITMQCTL_PATH to DOCKER:<container_name_or_id>. The tests will then execute commands via docker exec <container> rabbitmqctl <args>.

    Example container setup:

    docker run -d --hostname rabbitmq-dotnet-client-rabbitmq --name rabbitmq-dotnet-client-rabbitmq -p 15672:15672 -p 5672:5672 rabbitmq:management
  5. Understand the Connection Shutdown and Channel 0 Model

    main

    In the RabbitMQ .NET Client (7.x), every connection maintains a hidden internal channel/session pair on channel number 0 (_channel0 / _session0). This channel is used for connection-level operations like connection.start, connection.tune, connection.open, and connection.close, rather than application data traffic.

    Key distinction for shutdown behavior:

    • Application Channels (ID != 0): These automatically shut down when the connection shuts down because their sessions subscribe to the Connection.ConnectionShutdownAsync event.
    • Channel 0: This channel does not subscribe to Connection.ConnectionShutdownAsync. Its shutdown is exclusively managed by Connection.FinishCloseAsync.

    Critical Lifecycle Rule: Connection.FinishCloseAsync is only called at the end of the MainLoop (after ReceiveLoopAsync returns or throws). Therefore, if the MainLoop never runs to completion, channel 0 is never shut down. An un-shut-down channel 0 can cause the client to hang during disposal because the client will wait for the full ContinuationTimeout (default 20s) attempting to close an open channel that will never receive a response.

  6. Handle message body memory in Async consumers

    main

    When using AsyncEventingBasicConsumer.ReceivedAsync or sub-classing AsyncDefaultBasicConsumer, the ReadOnlyMemory<byte> provided in the message body is owned by the library. This memory is only valid during the execution of the ReceivedAsync event or the HandleBasicDeliverAsync method.

    If you need to use the message body outside of these specific methods, you MUST copy the data to a new buffer (e.g., using .ToArray()) to avoid accessing invalid memory.

  7. Reproduce the net472 cold-start race on Windows

    main

    On net472 (targeting netstandard2.0) on native Windows, the failure is a cold-start race. The abort code path is un-JITted and takes longer to reach SetCloseReason, allowing the MainLoop to win the race. To reproduce this reliably, you must run exactly one iteration per fresh process.

    Use the provided PowerShell script repro.ps1 to automate building and running multiple cold iterations.

    Usage:

    # Run with default settings
    .\projects\Applications\GH-1960\repro.ps1
    
    # Run with custom host and iteration count
    .\projects\Applications\GH-1960\repro.ps1 -Host_ localhost -Count 50
    .\projects\Applications\GH-1960\repro.ps1
    .\projects\Applications\GH-1960\repro.ps1 -Host_ localhost -Count 50
  8. Run the GH-1921 cancellation regression repro

    main

    The GH-1921 cancellation repro is a standalone console application used to reproduce and verify fixes for Windows-specific test failures related to CancellationToken firing during connection opening.

    Requirements:

    • Must be run on native Windows (WSL will not manifest the specific WSA* socket behaviors).
    • A reachable RabbitMQ broker must be available (e.g., a Docker container publishing port 5672 to the Windows host).

    Execution: Build and run the application using the dotnet run command, specifying the target framework and the broker hostname as an optional argument.

    # Run for .NET 8.0
    dotnet run -c Release -f net8.0 -- localhost
    
    # Run for .NET Framework 4.7.2
    dotnet run -c Release -f net472 -- localhost
    dotnet run -c Release -f net8.0 -- localhost
    dotnet run -c Release -f net472 -- localhost
  9. Run the GH-1968 reproduction script

    main

    The repro.ps1 script is used to measure the failure rate of Test.Integration.TestConnectionShutdown.TestCleanClosureWithSocketClosedOutOfBand on .NET Framework 4.7.2. This test specifically targets an intermittent OperationCanceledException that occurs during connection shutdown when the frame handler is closed out-of-band.

    Requirements:

    • Native Windows PowerShell.
    • A RabbitMQ broker reachable at localhost:5672.
    • If using WSL2, ensure localhost forwarding is configured from the Docker container.

    Note on Configuration: Always use -Configuration Debug (the default) when trying to match the CI baseline. The CI integration-win32 job builds in Debug and passes this test. Using Release may trigger unrelated, deterministic failures (like ObjectDisposedException) that mask the intermittent issue being investigated.

  10. Run integration tests against a local broker

    main

    To run integration or sequential-integration tests, you must have a RabbitMQ broker running. The CI environment uses a rabbitmq:management container.

    To run specific integration tests (e.g., those related to TestCreateConnectionAsync) after setting up your broker, use the following command:

    dotnet test projects/Test/Integration/Integration.csproj -c Release \
      --filter "FullyQualifiedName~TestCreateConnectionAsync"
  11. Migrate to RabbitMQ .NET Client 7.x using async/await

    main
    In version 7.x, the entire public API has transitioned to the Task Asynchronous Programming (TAP) model. All methods that perform I/O or long-running operations now end with the Async suffix and must be await-ed. You should update your code to use await with these new method signatures to ensure proper asynchronous execution.