Reputation: 194
When implementing, let's say, a service to handle email messages, should I put the code of the RabbitMQ subscriber to the separate process from the main program? Or should it be in the common codebase?
Are there any drawbacks of putting them together?
We develop a microservice-oriented application with .Net Core 3. We have to use a messaging bus to let many services react to some events published by another services. We use RabbitMQ.
We've already tried to create two separate applications communicating over HTTP. One listens for the new messages and triggers webhooks on another. The second one does all the stuff.
I expect some advice on how to organize the code. Would the common codebase be easier to maintain in future? Is the network requests timing overhead really important?
Upvotes: 0
Views: 2407
Reputation: 1224
We wrote a wrapper that we use in our microservices and applications to abstract away the implementation details of RabbitMQ. One of the key things it handles is tracking Subscribed Events and their associated Handlers. So, the application can define/inject a handler class and it automatically gets called whenever a matching message arrives.
For us, we treat the actual messaging implementation as a cross-cutting concern so we package and use it (Nuget package) just like any other, it's a completely separate project from everything else. It's actually a public Nuget package too, feel free to play with it you want (it's not well documented though).
Here's a quick example of how ours works so you can see the level of integration:
In Startup.cs
using MeshIntegrationBus.RabbitMQ;
public class Startup
{
public RabbitMqConfig GetRabbitMqConfig()
{
ExchangeType exchangeType = (ExchangeType)Enum.Parse(typeof(ExchangeType),
Configuration["RabbitMQ:IntegrationEventExchangeType"], true);
var rabbitMqConfig = new RabbitMqConfig
{
ExchangeName = Configuration["RabbitMQ:IntegrationEventExchangeName"],
ExchangeType = exchangeType,
HostName = Configuration["RabbitMQ:HostName"],
VirtualHost = Configuration["RabbitMQ:VirtualHost"],
UserName = Configuration["RabbitMQ:UserName"],
Password = Configuration["RabbitMQ:Password"],
ClientProviderName = Configuration["RabbitMQ:ClientProviderName"],
Port = Convert.ToInt32(Configuration["RabbitMQ:Port"])
}
return rabbitMqConfig
}
public void ConfigureServices(IServicecollection services)
{
services.Add.... // All your stuff
// If this service will also publish events, add that Service as well (scoped!)
services.AddScoped<IMeshEventPublisher, RabbitMqEventPublisher>(s =>
new RabbitMqEventPublisher(rabbitConfig));
// Since this service will be a singleton, wait until the end to actually add it
// to the servicecollection - otherwise BuildServiceProvider would duplicate it
RabbitMqListener rabbitMqListener = new RabbitMqListener(rabbitConfig,
services.BuildServiceProvider());
var nodeEventSubs = Configuration.GetSection(
$"RabbitMQ:SubscribedEventIds:ServiceA").Get<string[]>();
// Attach our list of events to a handler
// Handler must implement IMeshEventProcessor and does have
// access to the IServiceProvider so can use DI
rabbitMqListener.Subscribe<ServiceAEventProcessor>(nodeEventSubs);
services.AddSingleton(rabbitMqListener);
}
}
That's really all there is to it. You can add multiple handlers per subscribed event and/or reuse the same handler(s) for multiple events. It's proven pretty flexible and reliable - we've been using it for a couple years - and it's easy enough to change/update as needed and let individual services pull the changes when they want/need to.
Upvotes: 1
Reputation: 94
I suppose you are talking about situation when there is one publisher and many different applications (microservices) that process messages from a message queue. It's worth noticing that we are talking about applications that have different business purpose but not about many instances of the same application.
I would recommend to follow the next suggestions:
The main feature of microservice architecture is simplicity of system modification and those advices should help you to keep it simple from one side and prevent a hell of having absolutely unstructured system (3rd advice) from another side.
One more notice about a case when there is one publisher and one consumer. This can happen when you want to process data in background (for instance, some process can take a lot of time but your website should answer to client immediately. Customer in this case usually is a webjob). For this cases we are using one solution (repository) with both publisher and consumer for simplicity development.
Upvotes: 0
Reputation: 453
I recently designed and developed a software that communicating with other components via AMQP implemented by RabbitMQ.
As my matter of design I wrote a reusable service class which is being called by other service classes. That is happening in service layer or you can say business layer.
But as you asked if there are several applications and all of them need to implement RabbitMQ client I would create a library/module/package that can be easily imported to applications and configurable.
Upvotes: 0