Continuously receiving messages from Azure ServiceBus
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
For continuous message consumption from Azure Service Bus, the usual goal is not an infinite while loop that polls aggressively. The goal is a long-running receiver that handles messages as they arrive, manages locks correctly, and keeps throughput predictable under load.
With the current .NET SDK, the standard tool for this is ServiceBusProcessor. It gives you an event-driven receive loop with concurrency, retries, and settlement hooks built in.
Prefer ServiceBusProcessor for Long-Running Receivers
The Azure.Messaging.ServiceBus package exposes ServiceBusProcessor, which is designed for continuously receiving messages from a queue or subscription.
This is the normal continuous-receive model for a worker process or hosted service.
Why a Manual Receive Loop Is Usually Less Clean
You can also call ReceiveMessageAsync or ReceiveMessagesAsync in a loop, but then you own the polling strategy, backoff behavior, and much more of the connection lifecycle. That approach is sometimes fine for specialized batch consumers, but it is more plumbing than most applications need.
If your requirement is “keep listening forever,” ServiceBusProcessor is usually the right abstraction.
Settle Messages Deliberately
Continuous receiving only works well if message settlement is correct. The important choices are:
- complete the message after successful processing
- abandon it if you want it retried
- dead-letter it when the message is invalid or unrecoverable
Leaving AutoCompleteMessages off gives you explicit control. That is safer when handlers do real work such as database writes or HTTP calls.
Tune Concurrency and Prefetch Carefully
Service Bus throughput depends heavily on handler speed. If one message takes a second to process, a single-threaded receiver will not keep up with high traffic. Increase MaxConcurrentCalls carefully and observe downstream dependencies.
Also consider prefetch for higher throughput, but do not raise concurrency blindly. If the handler talks to a database, a third-party API, or other rate-limited resources, the bottleneck may simply move somewhere else.
Hosted Service Is a Good Shape in ASP.NET Core
In .NET applications, a background hosted service is often the cleanest place to run a processor continuously. That keeps startup, shutdown, and cancellation integrated with the application host instead of hiding the receiver in a controller or random singleton.
The same principle applies outside ASP.NET Core: keep the receiver in a long-lived worker, not in request-response code.
Common Pitfalls
- Writing a tight manual polling loop when
ServiceBusProcessoralready solves the lifecycle problem. - Completing messages before the real business work has succeeded.
- Increasing concurrency without checking database, API, or CPU limits downstream.
- Ignoring poison messages instead of dead-lettering them deliberately.
- Hosting the receiver in a short-lived process that exits before continuous processing can matter.
Summary
- For continuous receiving from Azure Service Bus in .NET, prefer
ServiceBusProcessor. - Register message and error handlers, then start the processor and keep the host alive.
- Settle messages explicitly so success, retry, and dead-letter behavior stay correct.
- Tune concurrency based on handler cost and downstream capacity.
- Run the receiver in a long-lived worker or hosted service, not in request code.

