.NET 6 application that exposes a REST HTTP endpoint which receives messages from an external client. These messages are sent to a RabbitMQ message queue, processed and sent in batches to an REST HTTP endpoint. These batches are sent with a minimum specified time interval between sends, which can be changed in the configuration.
- No authorization or authentication is needed for the service.
- The order of batches should follow the order of arrival at the message service (FIFO).
- A maximum batch size can be set, but it is not specified.
- Batches must be sent every 10 seconds, at a minimum.
- The application should run in a docker container.
- The system should be designed to minimize data loss in case of failure.
A message queue service (RabbitMQ) was chosen to facilitate the asynchronous communication between the client and upstream aggregated service. The motivation fo this is:
- Decoupling: By using a message queue, microservices can communicate asynchronously, reducing dependencies and allowing each service to evolve and scale independently.
- Resilience: A message queue service provides a buffer for messages, allowing services to continue functioning even if one of them is unavailable.
- Scalability: RabbitMQ can handle a large number of messages and can be easily scaled to meet growing demands.
- Reliability: RabbitMQ is known for its reliability and is widely used in production environments.
- Load balancing: A message queue can help distribute the load between microservices, improving performance and reducing the risk of overloading any one service.
The queue service can be scaled multiple ways:
- Horizontal scaling: You can add more nodes to the cluster as demand grows, enabling you to scale out horizontally.
- Load balancing: You can use a load balancer to distribute incoming messages across multiple nodes in the cluster.
- Auto-scaling: RabbitMQ can be deployed on cloud platforms that support auto-scaling, allowing you to dynamically add or remove nodes based on demand.
The REST HTTP endpoint exposed by the applications publishes message to the queue. A retry mechanism was built into this flow. If the call to publish the message fails, it will be retried 3 times with 1 second until ultimately an error response is returned from the endpoint.
A background service was included to consume the messages form the queue and send them to the upstream service. This service sends message received from the queue every 10 sconds to the aggregation server. A maximum batch size is set in configuration. This should be tweaked to suit what the aggregated server can handle. A recommended starting value is 100. If this number is small compared to the number that could be sent from the queue after the given time interval, increase the batch size. It is not recommended to go past a size of 1000.
Similarly to the publishing of messages, sending batches to the aggregation server implements a retyr policy. The HTTP POST to the server will be attempted 3 times, with 1 a second interval. If this failes, the batches will not be discarded, but the servie will rather sleep for the time interval (10 seconds) before it tries to send batches to the aggregation server again.
- Instead of retries with fixed time intervals, backoff policies could be implemented when making calls ot he aggregated server.
- If the service needed to scale dramatically to process up to millions of events per second, Apache Kafka could be used instead of RabbitMQ as it can handle higher throughput.
Docker needs to be installed. Open the docker-compose.yml file, and update the variable named AGGREGATED_MESSAGE_SERVER_URL with the URL of the upstream service. Navigate to the route directory of the project and run the following command:
docker compose up --build
This will run the docker-compose script in the repository. This script runs two containers:
- A container that hosts a RabbitMQ queue.
- A container that hosts the ASP.Net Core Web API, with the hosted service (consumer).
The API can then be accessed on http://localhost:8080, or Swagger, and the RabbitMQ dashboard at http://localhost:15672.