Apache ActiveMQ Artemis transport
BabelQueue.Artemis is an Apache ActiveMQ Artemis transport on the .NET core, built on AMQP
1.0 (AMQPNetLite). It sends the canonical envelope as the message
body with the §7 projection, and
consumes by routing each message to a handler by URN — so a message it produces is consumed by
any other BabelQueue SDK, and vice-versa. Artemis gives the binding native settlement, scheduled
delivery, a delivery counter and a dead-letter address, so it maps onto them rather than
re-implementing.
Install
dotnet add package BabelQueue.Artemis
Requirements: .NET 8. It pulls BabelQueue.Core and AMQPNetLite.Core transitively.
Produce
using Amqp;
using BabelQueue.Artemis;
var connection = new Connection(new Address("amqp://user:pass@localhost:5672"));
var session = new Session(connection);
var sender = new AmqpSenderLink(new SenderLink(session, "orders-sender", "orders"), "orders");
var id = await new ArtemisPublisher(sender)
.PublishAsync("urn:babel:orders:created", new Dictionary<string, object?> { ["order_id"] = 1042 });
PublishAsync returns the message meta.id; pass a traceId to continue a trace, or a delay
(TimeSpan) for native AMQP scheduled delivery.
Consume
using Amqp;
using BabelQueue;
using BabelQueue.Artemis;
var receiver = new AmqpReceiverLink(new ReceiverLink(session, "orders-worker", "orders"));
var dlq = new AmqpSenderLink(new SenderLink(session, "dlq-sender", "orders.dlq"), "orders.dlq");
var handlers = new Dictionary<string, BabelHandler>
{
["urn:babel:orders:created"] = (envelope, message, ct) =>
{
// envelope.Data, envelope.TraceId, envelope.Attempts ...
return Task.CompletedTask;
},
};
var options = new ArtemisConsumerOptions { DeadLetterSender = dlq, MaxTries = 3 };
var worker = new ArtemisConsumer(receiver, handlers, options);
await worker.RunAsync(cancellationToken); // receive → process → Accept
A successful handler Accepts the message. A throwing handler Releases it (the broker
redelivers and bumps delivery-count); once MaxTries is reached the envelope goes to
<queue>.dlq with a dead_letter block. The consumer routes on the x-opt-jms-type annotation
(falling back to the body URN). Unknown-URN strategy is one of fail / delete / release /
dead_letter.
AmqpSenderLink / AmqpReceiverLink wrap the AMQPNetLite links; the publisher and consumer
depend on the IAmqpSender / IAmqpReceiver seams, so they unit-test against mocks with no
broker.
Contract mapping (§7)
| Envelope | Apache ActiveMQ Artemis (AMQP 1.0) |
|---|---|
| body | message body (byte-identical across SDKs) |
job (URN) |
x-opt-jms-type annotation → JMSType (consumer routes on this) |
trace_id |
correlation-id → JMSCorrelationID |
meta.created_at |
creation-time → JMSTimestamp (Unix ms) |
meta.schema_version |
application property bq_schema_version |
meta.lang |
application property bq_source_lang |
attempts |
max(body, delivery-count) (AMQP counter is 0-based) |
| reserve / ack | Receive → process → Accept |
| retry / delay | Release redelivery · native x-opt-delivery-time |
| dead-letter | <queue>.dlq + dead_letter block (alongside the native DLA) |
The AMQP send/receive seams are mocked with Moq — no Artemis, no network. The envelope is
unchanged (schema_version stays 1); Apache ActiveMQ Artemis is purely additive.