5

I'm curious about the correct way to implement the EasyNetQ pub/sub pattern in an ASP.NET Core 2.x application. Specifically, I need to make sure that the lifecycle on all these resources are correct and that the subscription threads are owned/live properly.

I understand that IBus should be a singleton.

Standard practice is to create a single IBus instance for the lifetime of your application. Dispose it when your application closes.

https://github.com/EasyNetQ/EasyNetQ/wiki/Connecting-to-RabbitMQ

So, that looks like this (although, I should use the various appsettings files to provide environment-specific connection strings... let's assume this is OK for the purposes of this question).

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IBus>(RabbitHutch.CreateBus("host=localhost"));
}

Now, I like the Auto Subscriber feature, but it's not obvious when/where to run the various subscribe methods.

You can use it to easily scan a specific assembly for classes that implement either of the interfaces IConsume or IConsumeAsync, and then let the auto subscriber subscribe these consumers to your bus.

It doesn't seem right to run that directly in the Startup context, right?

  • Are the subscription threads "owned" by the correct parent thread (is there such a thing)?
  • What's the expected/correct lifecycle for AutoSubscriber, is Singleton even necessary?
  • Do I really even want to register AutoSubscriber? I don't want any other code to require it, I just need it easily/correctly accessible to Configure (seems like the right place to run a method like SubscribeAsync).

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IBus>(RabbitHutch.CreateBus("host=localhost"));
    services.AddSingleton<AutoSubscriber>(provider => new AutoSubscriber(provider.GetRequiredService<IBus>(), Assembly.GetExecutingAssembly().GetName().Name));
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.ApplicationServices.GetRequiredService<AutoSubscriber>().SubscribeAsync(Assembly.GetExecutingAssembly());
}

Should I use a Hosted Service, instead (should I implement the start/stop methods or is BackgroundService OK)?

Anthony Mastrean
  • 21,850
  • 21
  • 110
  • 188
  • 1
    This is just an hypothesis based on experience working with RabbitMQ with NodeJS. It highly recommended that when working with RabbitMQ, you have only one connection, always connected. If your code connects, publish, and then disconnect, you are doing a disfavor to yourself and those who use the RabbitMQ server. Therefore, I would not be surprise that you required a Singleton since it always returns the same instance aka. only one connection to use. Your `CreateBus` may require more parameters than just `host=localhost` such as `port`, `username`, `password` and `vhost`. I hope this helps you. – acarlstein May 13 '19 at 16:52
  • ?. Sorry, you lost me. What happen? – acarlstein May 13 '19 at 17:06
  • @acarlstein I'm not concerned with the lifecycle on the IBus. I have that covered. And EasyNetQ handles the primary work thread. I'm not opening/closing anything. What I'm concerned about is the intersection of the ASP.NET Core framework and the consume pattern exposed by EasyNetQ. – Anthony Mastrean May 13 '19 at 17:31

1 Answers1

2

I ran into this same conundrum. I figured out that you can inject anything registered in the container into Startup::Configure and perform one-time startup tasks like with the AutoSubscriber.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, IBus bus)
{
    var subscriber = new AutoSubscriber(bus, Assembly.GetExecutingAssembly().GetName().Name);
    subscriber.Subscribe(Assembly.GetExecutingAssembly());
}
Evan Kaiser
  • 181
  • 8