Service Bus
Azure Service Bus emulation with a management plane for entity topology (queues, topics, subscriptions) over HTTP, and an AMQP 1.0 data plane backed by an Apache ActiveMQ Artemis sidecar managed automatically by floci-az.
| Plane | Protocol | Transport | Port |
|---|---|---|---|
| Management | HTTP | /{account}-servicebus/ on :4577 |
4577 |
| Data | AMQP 1.0 | Apache ActiveMQ Artemis sidecar | 5673 (AMQP) / 5674 (AMQPS) |
Unlike Event Hubs, entity topology is not pre-configured — queues, topics and subscriptions are created dynamically through the management API (or auto-created on first use by the SDK).
Mocked mode (default). With
mocked: truethe Artemis sidecar is not started: the management API responds, but the AMQP data plane is unavailable. Setmocked: false(and expose the AMQP ports) to send and receive messages.
Management plane
Entity CRUD is served over HTTP at /{account}-servicebus/:
| Method | Path | Description |
|---|---|---|
GET |
/{account}-servicebus/$Resources/queues |
List queues |
PUT / DELETE |
/{account}-servicebus/{queue} |
Create / delete a queue |
GET |
/{account}-servicebus/$Resources/topics |
List topics |
PUT / DELETE |
/{account}-servicebus/{topic} |
Create / delete a topic |
GET |
/{account}-servicebus/{topic}/subscriptions |
List subscriptions |
PUT / GET / DELETE |
/{account}-servicebus/{topic}/subscriptions/{sub} |
Manage a subscription |
GET |
/{account}-servicebus/{topic}/subscriptions/{sub}/rules |
List subscription rules |
PUT / GET / DELETE |
/{account}-servicebus/{topic}/subscriptions/{sub}/rules/{rule} |
Manage a subscription rule |
Subscription rules and filters
Subscriptions filter which topic messages they receive through named rules, matching Azure semantics:
- Every new subscription starts with the implicit
$Defaultrule (aTrueFilterthat accepts everything). The usual SDK flow — add your real rule, then delete$Default— works as on Azure, as does passing aDefaultRuleDescriptionin the subscription create body (CreateSubscriptionAsync(subscriptionOptions, ruleOptions)). CorrelationFilter— exact-match (case-sensitive) AND-combination overCorrelationId,Label/Subject,SessionId, and application properties.SqlFilter— SQL92 expressions over application properties andsys.CorrelationId,sys.Label,sys.Subject,sys.SessionId(includingLIKE,IN,BETWEEN,IS NULL,EXISTS(prop), arithmetic and boolean operators).TrueFilter/FalseFilter— accept-all / accept-none.- Multiple rules combine as a logical OR and deliver a single copy of a matching message. A subscription whose rules have all been deleted receives nothing.
Filters compile to Artemis queue selectors, so evaluation happens inside the broker at routing time — messages that don't match a subscription's rules are never routed to it (no delivery-count inflation, no spurious dead-lettering).
Emulator deviations from Azure:
- Filters on
MessageId,To,ReplyTo,ReplyToSessionId, orContentType(and theirsys.*forms) are rejected with HTTP 400 — these AMQP fields have no broker-side selector mapping. - Rule actions (
SqlRuleAction) are stored and echoed back by the management API but are not applied to delivered messages, and rules with actions do not produce extra message copies. - Property names in filters are case-sensitive and must be valid selector identifiers
(letters, digits,
_,$). Correlation-filter values typedint/long/double/booleanetc. compare with their declared type; other non-string types compare as strings. - Rule changes update the subscription's filter in place (messages already routed to the subscription stay, receivers stay attached) and, as on Azure, apply to future messages only.
Message sessions
Queues and subscriptions created with RequiresSession support Azure SDK session receivers.
Set SessionId on sent messages, then use a specific-session receiver, an accept-next-session
receiver, or a session processor. The broker translates Azure's AMQP session filter into an
Artemis JMSXGroupID selector, which keeps each session on one receiver and preserves FIFO order
within that session. Attach responses include Azure's com.microsoft:locked-until-utc property.
Session ownership lasts for the receiver link. Session state and explicit session-lock renewal are not currently emulated.
Connection String
Endpoint=sb://localhost:5673;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=devkey;UseDevelopmentEmulator=true;
UseDevelopmentEmulator=true tells the SDK to use plain AMQP (no TLS). The SharedAccessKey value is
ignored — Artemis runs without authentication in dev mode.
Python SDK
from azure.servicebus import ServiceBusClient, ServiceBusMessage
CONN = (
"Endpoint=sb://localhost:5673;"
"SharedAccessKeyName=RootManageSharedAccessKey;"
"SharedAccessKey=devkey;"
"UseDevelopmentEmulator=true;"
)
with ServiceBusClient.from_connection_string(CONN) as client:
with client.get_queue_sender("myqueue") as sender:
sender.send_messages(ServiceBusMessage("hello world"))
with client.get_queue_receiver("myqueue", max_wait_time=5) as receiver:
for msg in receiver:
print(str(msg))
receiver.complete_message(msg)
.NET SDK
await using var client = new ServiceBusClient(
"Endpoint=sb://localhost:5673;SharedAccessKeyName=RootManageSharedAccessKey;" +
"SharedAccessKey=devkey;UseDevelopmentEmulator=true;");
ServiceBusSender sender = client.CreateSender("myqueue");
await sender.SendMessageAsync(new ServiceBusMessage("hello world"));
The Artemis sidecar includes the MSSBCBS anonymous SASL mechanism expected by
Azure.Messaging.ServiceBus; authorization continues through the standard CBS link.
Configuration
Docker Compose
services:
floci-az:
image: floci/floci-az:latest
ports:
- "4577:4577" # floci-az HTTP (management plane)
- "5673:5673" # Service Bus AMQP (Artemis)
- "5674:5674" # Service Bus AMQPS (Artemis)
environment:
FLOCI_AZ_SERVICES_SERVICE_BUS_ENABLED: "true"
FLOCI_AZ_SERVICES_SERVICE_BUS_MOCKED: "false"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
Environment variables
| Variable | Default | Description |
|---|---|---|
FLOCI_AZ_SERVICES_SERVICE_BUS_ENABLED |
true |
Enable/disable the service |
FLOCI_AZ_SERVICES_SERVICE_BUS_MOCKED |
true |
Mocked mode (management plane only, no Artemis) |
FLOCI_AZ_SERVICES_SERVICE_BUS_AMQP_PORT |
5673 |
Host port for AMQP (Artemis) |
FLOCI_AZ_SERVICES_SERVICE_BUS_AMQP_TLS_PORT |
5674 |
Host port for AMQPS |
FLOCI_AZ_SERVICES_SERVICE_BUS_ARTEMIS_IMAGE |
apache/activemq-artemis:2.44.0 |
Artemis image; must match bundled protocol patches |
FLOCI_AZ_SERVICES_SERVICE_BUS_MAX_DELIVERY_COUNT |
10 |
Max delivery attempts before dead-lettering |
FLOCI_AZ_SERVICES_SERVICE_BUS_LOCK_DURATION_SECONDS |
60 |
Peek-lock duration |
application.yml
floci-az:
services:
service-bus:
enabled: true
mocked: true # true = management plane only, no Docker. false = real Artemis sidecar
amqp-port: 5673
amqp-tls-port: 5674
artemis-image: "apache/activemq-artemis:2.44.0"
max-delivery-count: 10
lock-duration-seconds: 60
Out of scope (future work)
- Session state and explicit session-lock renewal
- Deferred messages and auto-forwarding
- Duplicate detection and message transactions
- Geo-disaster recovery and partitioned entities