Want to know more about the future of RawRabbit? Checkout the 2.0.0-beta1 release notes and read the blog posts:
- One method to rule them all: deep dive into the middleware architecture
- No such thing as perfect code: tweaking defaults of the client
- Controlled concurrency: consume concurrency and unexpected synergies
- Making sense of all those logs: serilog + elastic + rawrabbit = first class logging experience
RawRabbit
is a modern .NET client for communication over RabbitMq. It is written for .NET Core
and uses Microsoft’s new frameworks for logging, configuration and dependecy injection. Full documentation available at rawrabbit.readthedocs.org
.
Setting up publish/subscribe in just a few lines of code.
var client = BusClientFactory.CreateDefault();
client.SubscribeAsync<BasicMessage>(async (msg, context) =>
{
Console.WriteLine($"Recieved: {msg.Prop}.");
});
await client.PublishAsync(new BasicMessage { Prop = "Hello, world!"});
RawRabbits
request/response (RPC
) implementation uses the direct reply-to feature for better performance and lower resource allocation.
var client = BusClientFactory.CreateDefault();
client.RespondAsync<BasicRequest, BasicResponse>(async (request, context) =>
{
return new BasicResponse();
});
var response = await client.RequestAsync<BasicRequest, BasicResponse>();
Message context are passed through to the registered message handler. The context is customizable, meaning that domain specific metadata (like originating country, user claims, global message id etc) can be stored there. In adition to this, RawRabbit
comes with an AdvancedMessageContext
that is wired up to support more advanced scenarios
The AdvancedMessageContext
has a method Nack()
that will perform a basic.reject
for the message.
var client = service.GetService<IBusClient<AdvancedMessageContext>>();
client.RespondAsync<BasicRequest, BasicResponse>((req, context) =>
{
context.Nack(); // the context implements IAdvancedMessageContext.
return Task.FromResult<BasicResponse>(null);
}, cfg => cfg.WithNoAck(false));
For some scenarios it makes sense to schedule a message for later handling.
subscriber.SubscribeAsync<BasicMessage>(async (msg, context) =>
{
if (CanNotProcessRightNow(msg))
{
context.RetryLater(TimeSpan.FromMinutes(5));
return;
}
// five minutes later, we're here...
});
The AdvancedMessageContext
has the properties OriginalSent
and NumberOfRetries
that can be used to create a "retry x times then nack" strategy.
It is easy to write extensions for RawRabbit
. The RawRabbit.Extensions
NuGet package contains useful extensions, like BulkGet
for retrieving multiple messages from multiple queues and Ack
/Nack
them in bulk:
var bulk = client.GetMessages(cfg => cfg
.ForMessage<BasicMessage>(msg => msg
.FromQueues("first_queue", "second_queue")
.WithBatchSize(4))
.ForMessage<SimpleMessage>(msg => msg
.FromQueues("another_queue")
.GetAll()
.WithNoAck()
));
From the very first commit, RawRabbit
was built with pluggability in mind. Registering custom implementations by using the optional argument when calling BusClientFactory.CreateDefault
.
var publisher = BusClientFactory.CreateDefault(ioc => ioc
.AddSingleton<IMessageSerializer, CustomerSerializer>()
.AddTransient<IChannelFactory, ChannelFactory>()
);
All operations have a optional configuration builder that opens up for granular control over the topology
subscriber.SubscribeAsync<BasicMessage>(async (msg, i) =>
{
//do stuff..
}, cfg => cfg
.WithRoutingKey("*.topic.queue")
.WithPrefetchCount(1)
.WithNoAck()
.WithQueue(queue =>
queue
.WithName("first.topic.queue")
.WithArgument("x-dead-letter-exchange", "dead_letter_exchange"))
.WithExchange(exchange =>
exchange
.WithType(ExchangeType.Topic)
.WithAutoDelete()
.WithName("raw_exchange"))
);