AMQP.Net Lite Documentation

repository·master·Indexed 19 days ago

https://github.com/azure/amqpnetlite

A lightweight, cross-platform AMQP 1.0 implementation for the .NET ecosystem. It supports both client and listener roles for peer-to-peer or brokered messaging across various platforms, including .NET Framework, .NET Core, nanoFramework, and NETMF. Key features include TLS and SASL security, WebSocket support, and integration with Azure Service Bus and Azure Event Hubs. The library provides synchronous and asynchronous APIs, buffer pooling via IBufferManager for performance, and specialized support for resource-constrained devices.

Tokens
17.4K
Snippets
44
Records
81
Agent score
64%

What's inside AMQP.Net Lite

  1. Overview of AMQP.Net Lite features

    master

    AMQP.Net Lite is a lightweight AMQP 1.0 library designed for a wide range of .NET and Windows Runtime platforms. It supports both client and listener roles, enabling peer-to-peer and broker-based messaging.

    Key Features:

    • Protocol Control: Full control over AMQP 1.0 protocol behavior.
    • Messaging Models: Supports both peer-to-peer and brokered messaging.
    • Security: Secure communication via TLS and SASL (supports SASL PLAIN, EXTERNAL, and ANONYMOUS).
    • Extensibility: Supports extensible transport providers and includes WebSocket support.
    • API Styles: Provides both synchronous and asynchronous API support.
    • Versatility: Includes Listener APIs for building brokers, routers, proxies, and more.
  2. Explore AmqpNetLite C# Examples

    master

    The AmqpNetLite repository contains several categories of C# example projects designed to demonstrate different usage patterns, ranging from resource-constrained devices to full-scale AMQP brokers and Azure Service Bus integration.

    Note: Some examples require an AMQP 1.0 broker with pre-configured queues to function correctly.

  3. Batch multiple messages using the Event Hubs extended format

    master

    Azure Event Hubs supports an extended message format (0x80013700) that allows packing multiple messages into a single AMQP message. This is highly efficient for high-latency networks or small messages. The resulting AMQP message contains multiple Data sections, which the service extracts and delivers to receivers individually.

    To implement this, you must set the Format to 0x80013700 and provide a DataList containing the serialized payloads in the BodySection.

    public class MessageBatch : Message
    {
        public const uint BatchFormat = 0x80013700;
    
        public static MessageBatch Create<T>(IEnumerable<T> objects)
        {
            DataList dataList = new DataList();
            foreach (var obj in objects)
            {
                ByteBuffer buffer = new ByteBuffer(1024, true);
                var section = new AmqpValue<T>(obj);
                AmqpSerializer.Serialize(buffer, section);
                dataList.Add(new Data() { Buffer = buffer });
            }
    
            return new MessageBatch() { Format = BatchFormat, BodySection = dataList };
        }
    }
  4. Understand AMQP.Net Lite serialization types and resolution order

    master

    The serializer converts .NET objects into AMQP bytes and vice versa. It supports primitives, collections, Enums, Nullables, IAmqpSerializable implementations, and custom types annotated with AmqpContractAttribute or handled by a custom IContractResolver.

    Type Resolution Order: The serializer attempts to resolve types in this specific order. If a type cannot be resolved in any of these steps, an exception is thrown:

    1. AmqpContractAttribute
    2. AMQP primitive types
    3. IAmqpSerializable
    4. Nullable<T>
    5. Enum
    6. Array/List/Map

    Important Note: A class should not implement both AmqpContractAttribute and IAmqpSerializable. The serializer stops checking once it finds the AmqpContractAttribute.

  5. Receive Messages

    master

    There are two primary patterns for receiving messages using a ReceiverLink:

    1. Receive Loop (Manual)

    Use a loop to call ReceiverLink.Receive(int). This call blocks until a message is available or the timeout elapses.

    2. MessageCallback (Automatic)

    Register a callback using ReceiverLink.Start(int, MessageCallback). This model eliminates the need for a manual receive loop in your application code.

    Link credit controls how many messages a remote peer can send.

    • Automatic Mode (Default): The ReceiverLink manages credit. Credit is decremented when a message arrives and incremented when you call ReceiverLink.Accept(Message) or ReceiverLink.Reject(Message).
    • Manual Mode: Call ReceiverLink.SetCredit(int, true) to set initial credit, or ReceiverLink.SetCredit(int, false) to take full control. If autoRestore is false, the library stops sending flow performatives, and you must manually renew credit via SetCredit.
  6. Configure tracing levels in AMQP.Net Lite

    master

    AMQP.Net Lite provides multiple tracing levels to assist in troubleshooting and debugging. Tracing levels are hierarchical from low to high:

    • Error
    • Warning
    • Information
    • Verbose
    • Frame: Outputs incoming and outgoing AMQP protocol headers and frames.
    • Buffer: Outputs incoming and outgoing raw bytes.

    Levels from Error to Verbose are cumulative (e.g., Verbose includes Information, Warning, and Error). Frame and Buffer levels can be combined with other levels, though this may increase output volume and complexity.

  7. Monitor AmqpObject state and errors

    master

    All core AMQP objects (Connection, Session, Link) inherit from AmqpObject. You can use the following properties and events to manage their lifecycle:

    • AmqpObject.IsClosed: A boolean indicating if the object has been closed.
    • AmqpObject.Error: If set, contains the error condition under which the object was closed.
    • AmqpObject.Closed: An event that notifies subscribers when the object reaches its end state.

    Note: If an AmqpObject is in a closing, ending, or detaching state, any API call other than Close() will throw an AmqpException with the error condition amqp:illegal-state.

  8. Use AmqpProvidesAttribute for type resolution and inheritance

    master

    The [AmqpProvides] attribute allows the decoder to resolve specific types based on a descriptor in the payload. This is useful for handling class inheritance or acting as a type registry.

    Scenario 1: Class Inheritance If a base class and derived classes have the same EncodingType, you can annotate the base class with [AmqpProvides] for each known derived type. This allows AmqpSerializer.Deserialize<Base>(buffer) to return the correct concrete instance.

    Scenario 2: Type Resolver (Registry) If you have multiple unrelated types that might appear in a payload, create a 'Resolver' class. Annotate the Resolver class with [AmqpProvides(typeof(T))] for every possible type. To decode, use the specialized method: AmqpSerializer.Deserialize<Resolver, TAs>(buffer) where TAs is the base type of all decoded objects.

    Example:

    [AmqpContract]
    [AmqpProvides(typeof(Person))]
    [AmqpProvides(typeof(Student))]
    [AmqpProvides(typeof(Address))]
    class Resolver
    {
    }
    
    // Usage:
    var obj = AmqpSerializer.Deserialize<Resolver, object>(buffer);
    [AmqpContract]
    class Student : Person
    {
        [AmqpMember]
        public double GPA { get; set; }
    }
    
    [AmqpContract]
    [AmqpProvides(typeof(Student))]
    class Person
    {
    }
    
    // Deserialization returns the concrete Student instance
    var person = AmqpSerializer.Deserialize<Person>(buffer);
  9. Avoid blocking the asynchronous connection pump

    master

    AMQP.Net Lite uses an asynchronous connection "pump" (a continuous loop that processes I/O) to handle sending and receiving. It is critical that your application does not block this pump, as doing so prevents the library from processing I/O, which typically leads to deadlocks, application hangs, or timeout errors.

    There are two primary ways to accidentally block the pump:

    1. Performing blocking operations in a callback: If you use a callback (e.g., in a Send method), do not perform long-running or blocking work (like Thread.Sleep) directly inside it. Instead, schedule the work asynchronously.
    2. Blocking in an async continuation: If an await operation completes and its continuation runs synchronously on the same thread used by the pump, blocking that thread will halt all I/O. To prevent this, ensure continuations do not run on threads performing blocking work, or wrap async operations using a TaskCompletionSource with TaskCreationOptions.RunContinuationsAsynchronously.

    Important: Avoid mixing the synchronous API with the asynchronous API on the same thread. For example, calling a synchronous .Send() immediately after an await .SendAsync() can cause the synchronous call to timeout because the pump cannot process the required acknowledgements.

    // BAD: Blocking inside a callback
    SenderLink sender = new SenderLink(session, "sender", "q1");
    sender.Send(
        new Message("test"),
        (m, o, s) => Thread.Sleep(120000), // This blocks the pump
        sender);
    
    // BAD: Mixing sync and async in a way that blocks the pump
    SenderLink sender = new SenderLink(session, "sender", "q1");
    await sender.SendAsync(new Message("m1"));
    sender.Send(new Message("m2")); // This will likely timeout
  10. How ContainerHost works as an AMQP listener

    master

    ContainerHost is the simplest way to implement an AMQP listener, allowing you to specify multiple endpoints in a single host. The host listens on all transport endpoints for incoming connections and routes them based on the following logic:

    1. Address Resolution: If an address resolver is set, the host translates the incoming attach address. This is useful for routing messages to different destinations or serving messages from multiple nodes.
    2. Message/Request Routing: If a message or request processor is registered at the resolved address, the host creates a link endpoint and routes all received messages to that processor.
    3. Link Routing: If no message/request processor matches but a ILinkProcessor is registered, the attach request is routed to that processor.
    4. Rejection: If no match is found, the attach is rejected with an amqp:not-found error.

    You can configure protocol behavior via the Listeners properties of the ContainerHost.

  11. Specify an AMQP Address

    master

    An Address object represents the AMQP endpoint. It can be initialized using a URI string or individual parameters.

    Key behaviors:

    • URI Strings: If using a URI, the username and password must be URL encoded.
    • Parameters: If using individual parameters, they must not be URL encoded.
    • Scheme: The Scheme property (e.g., amqps) determines if TLS/SSL is used.
    • Authentication: The user info part in the address triggers SASL negotiation. If user info is absent, the library skips SASL negotiation entirely (it does not default to SASL ANONYMOUS).
    • WebSockets: The Path property is only utilized when using WebSocket transport (ws or wss).
    • Hostname: The Host property is passed to the open.hostname field during connection negotiation.
    var address = new Address("amqps://contoso.com:5671");