SquidStd.Messaging
In-memory transport for SquidStd.Messaging. Provides channel-backed IQueueProvider and ITopicProvider
implementations behind the shared IMessageQueue / IMessageTopic facades: per-queue buffering,
round-robin (competing-consumers) delivery, retry and dead-letter handling, topic fan-out, and metrics -
all registered with a single AddInMemoryMessaging() call.
Queues deliver each message to exactly one subscriber; topics fan every message out to all subscribers.
The contracts live in SquidStd.Messaging.Abstractions, so code written against IMessageQueue and
IMessageTopic moves unchanged to a real broker: use this in-memory provider for single-process apps,
tests, and local dev, then swap in SquidStd.Messaging.RabbitMq or SquidStd.Messaging.Sqs for
production.
Install
dotnet add package SquidStd.Messaging
Usage
Register through the bootstrap - the providers are ISquidStdServices, so StartAsync / StopAsync
manage their lifecycle for you:
using SquidStd.Messaging.Abstractions.Interfaces;
using SquidStd.Messaging.Extensions;
using SquidStd.Services.Core.Extensions;
using SquidStd.Services.Core.Services.Bootstrap;
var bootstrap = SquidStdBootstrap.Create(o => o.ConfigName = "myapp");
bootstrap.ConfigureServices(c => c.RegisterCoreServices().AddInMemoryMessaging());
await bootstrap.StartAsync();
Queues (competing consumers)
Each queue message goes to exactly one subscriber. Implement a listener and subscribe it; dispose the subscription to unsubscribe.
public sealed record OrderPlaced(string Id);
public sealed class OrderListener : IQueueMessageListenerAsync<OrderPlaced>
{
public Task HandleAsync(OrderPlaced message, CancellationToken cancellationToken)
{
Console.WriteLine($"queue handled {message.Id}");
return Task.CompletedTask;
}
}
var queue = bootstrap.Resolve<IMessageQueue>();
using (queue.Subscribe("orders", new OrderListener()))
{
await queue.PublishAsync("orders", new OrderPlaced("order-1"));
}
A synchronous IQueueMessageListener<TMessage> overload of Subscribe is also available.
Topics (fan-out)
Every subscriber receives every message published to the topic.
var topic = bootstrap.Resolve<IMessageTopic>();
using (topic.Subscribe<OrderPlaced>("order-events", (order, _) =>
{
Console.WriteLine($"topic saw {order.Id}");
return Task.CompletedTask;
}))
{
await topic.PublishAsync("order-events", new OrderPlaced("order-2"));
}
Configuration
AddInMemoryMessaging takes an optional MessagingOptions. Options are code-only - this package
registers no YAML config section.
c.AddInMemoryMessaging(new MessagingOptions
{
MaxDeliveryAttempts = 5,
DeadLetterQueueSuffix = ".dead",
RetryDelay = TimeSpan.FromMilliseconds(250)
});
| Property | Default | Purpose |
|---|---|---|
MaxDeliveryAttempts |
3 |
Delivery attempts before a message is dead-lettered. |
DeadLetterQueueSuffix |
".dlq" |
Suffix appended to a queue name to form its dead-letter queue. |
RetryDelay |
Zero |
Delay before a failed message is re-enqueued. |
A connection-string overload is also available:
AddInMemoryMessaging("memory://local?maxDeliveryAttempts=5&retryDelayMs=250&deadLetterSuffix=.dead").
When a listener throws, the message is re-enqueued (after RetryDelay) until MaxDeliveryAttempts is
reached, then moved to <queue><DeadLetterQueueSuffix> - subscribe to that queue to inspect failures.
MessagingMetricsProvider is registered as IMessagingMetrics / IMetricProvider and tracks published,
delivered, retried, failed, and dead-lettered counts per queue.
Key types
| Type | Purpose |
|---|---|
MessagingRegistrationExtensions |
AddInMemoryMessaging(...) registration. |
InMemoryQueueProvider |
Channel-based in-memory IQueueProvider with retry/DLQ. |
InMemoryTopicProvider |
In-memory ITopicProvider (fan-out). |
IMessageQueue / IMessageTopic |
Typed facades from SquidStd.Messaging.Abstractions. |
MessagingOptions |
Retry, dead-letter, and delivery-attempt configuration. |
Related
- Article: Messaging
- Article: Messaging abstractions
- Tutorial: Messaging
- Production transports: SquidStd.Messaging.RabbitMq, SquidStd.Messaging.Sqs
License
MIT - part of SquidStd.
How messages flow
flowchart LR
P[Publisher] -->|enqueue| Q[(Queue)]
Q -->|competing consumers,<br/>one receives| C1[Consumer A]
Q -.-> C2[Consumer B]
P2[Publisher] -->|publish| T((Topic))
T -->|fan-out,<br/>all receive| S1[Subscriber A]
T --> S2[Subscriber B]
Queues deliver each message to exactly one consumer (work distribution); topics fan out every message to all subscribers (notifications). Both are behind IMessageQueue / IMessageTopic regardless of the backend.