AMQP on the wire : messages content framing

After the previous post on the AMQP builtin type system, I’d like to show you a different sample frame strictly related to the sending messages. For starting, I used the SASL mechanisms frame as example to understand the encoding/decoding system just because it’s one of the first exchanged frames in the protocol but every day we use AMQP to send messages so understand their encoding is much more important.

An AMQP message on the wire

Consider the following source code that uses AMQP .Net Lite library to send a simple message :

Connection connection = new Connection(address);
Session session = new Session(connection);
SenderLink sender = new SenderLink(session, "sender-" + testName, "q1");

Message message = new Message("Hello");
message.Properties = new Properties();
message.Properties.MessageId = Guid.NewGuid().ToString();
message.ApplicationProperties = new ApplicationProperties();
message.ApplicationProperties["prop1"] = 1; 
message.ApplicationProperties["prop2"] = "value";

sender.Send(message, 60000);

In the above code, we are interested in the encoding and framing used in the AMQP protocol to send the Message object on the wire. As reported on the official specification (page 38), each frame has a format as showed in the following picture (we already saw this format for the SASL frames with some “little” changes).

amqp_frame

As we can see, the real body starts with the so called performative; you can think about it as an “operation” executed against the AMQP broker (opening the connection or the session, attaching the link, transfering a message and so on).

amqp_message_01

In this case, we have the transfer performative for sending messages and the encoded frame is much more complex than the first one (SASL mechanisms).

The objective of this article is to understand how the builtin type system is used to encode the three main parts of an AMQP message.

amqp_message_format

From the above picture we’ll consider :

  • Message properties : system properties well defined by the AMQP specification and used by the broker for manipulation, routing and so on;
  • Application properties : user defined properties to carry data without using the following payload;
  • Application data : user defined payload of the message;

The content of all these parts is encoded using the builtin type system. Starting from the “Message-Properties” section we have the descriptor (0x00 0x53 0x73) where the 0x73 code defines the “AMQP properties list” (page 84 of official specification) that is a list (format code 0xC0) of 39 bytes (0x27) with only one element (0x01). This element is an UTF8 encoded string (0xA1) with size of 36 characters expressed with only one byte (0x24). This string is the “Message-Id” we set in our source code (it’s the autogenerated GUID value). You can see that there is no information to describe that this field is the “Message-Id” field but we know that because it’s the first field of the composite type “AMQP properties list”. If the “Message-Id” field weren’t explicitly set by code, it would be null but encoded inside the properties list itself. This means that all missing fields between two defined fields are encoded as null but inserted inside the list (to guarantee the fields sequence); if all subsequent fields (until the end of the list) aren’t set, they aren’t encoded inside the frame (to avoid a waste of space).

amqp_msg_properties

The “Application-Properties” section starts with its related descriptor (0x00 0x53 0x74) where the 0x74 code defines the “Application properties map” (page 86 of official specification) that is a map (format code 0xC1) of 24 bytes (0x18) with four elements (0x04). A map is a compound type in the AMQP builtin type system we didn’t see yet; it’s a collection with key-value pairs each encoded as defined in the specification.

amqp_map

In our example, we have two pairs (“prop1” = 1 and “prop2” = “value”) so four elements in the map. The name of the first key “prop1” is encoded as a UTF8 string (code 0xA1) with 0x05 a size (five characters for “prop1”) and the related value that is the string “value” is encoded in the same way. Then there is the name of the second key “prop2” and the related value 0x01 that is an integral type expressed with only one byte (format code 0x54, page 27 on official specification).

amqp_app_properties

The last part of the message is the data section (the “Hello” string) that has the descriptor (0x00 0x53 0x77) where the code 0x77 defines an “AMQP value” (page 86 of official specification) that in this case is an UTF8 string (0xA1) with 0x05 characters.

amqp_value

Changing the data as raw binary

The interesting thing is that the data part of an AMQP message could be encoded as an “AMQP value” (as previous), as an “AMQP sequence” (of values) or as pure “Binary” data.

Consider the previous example but with following changes related to the creation of Message instance.

Message message = new Message()
{
    BodySection = new Data() { Binary = Encoding.UTF8.GetBytes("Hello") }
};

Using the BodySection as Data object and the related Binary field the encoding of “Hello” string on the wire is defined as binary (0xA0) with 0x05 following bytes.

amqp_binary_data

Encoding the data in the following way is an advantage for the consumer of the message that needs to understand pure binary data and not “AMQP value” encoding.

Conclusion

The AMQP message format seems to be more complex than any other protocol but deeping into it …. it’s seems to be simpler.

Of course, there is another part in the message we didn’t consider named the “Message Annotations” that are used a lot for customization inside brokers like for Event Hubs inside Microsoft Azure Service Bus.

However, we can’t see them on the wire because we know that Service Bus traffic is encrypted (using SSL/TLS protocol) and the local AMQP broker for testing doesn’t support event hubs for clear reasons 🙂

One comment

Leave a comment