Chapter 7: Components for messaging and caching
Introduces how message brokers connect distributed services, how queued messages are processed, and how queue-based and topic-based delivery differ.
After this chapter you will be able to
- Message broker communication role. Explain how a message broker lets independent services exchange messages without being directly connected, including why this suits asynchronous communication in distributed applications.
- Queued message processing flow. Trace a message from a service posting it to a queue through a receiving service reading and processing it, including what happens after processing.
- Queue and topic delivery modes. Distinguish the typical subscriber and delivery behavior of queue-based processing from topic-based processing.
- RabbitMQ message broker role. Explain that RabbitMQ manages and routes messages between applications, enabling reliable, asynchronous communication across services.
- Asynchronous order workflow rationale. Describe how placing an order can add a background message to notify the delivery driver, and explain why the customer can see success without waiting for driver assignment.
- RabbitMQ container host setup. Describe how to begin hosting RabbitMQ in Aspire: add Aspire.Hosting.RabbitMQ to the Aspire host project, then use the component package in AppHost.cs to add a RabbitMQ container.
- Product identification for orders. Explain how the web client specifies an order using product IDs and quantities, and why product IDs must be included in the product DTO returned by the API.
- Order models and tables. Describe the two kinds of order data introduced and the corresponding database-creation change.
- Order Placement Transaction. Trace what the POST endpoint does before sending a message: validate the basket, calculate the total from the ordered products, save the order and its items, and commit or roll back the database transaction.
- Order Message Publication. Describe when the endpoint publishes its RabbitMQ message, what the message contains, and why this example leaves the exchange parameter blank.
- Order-created message consumption. Trace how the LocationUpdater background service responds to an orders.created message and what that means for the order-tracking map.
- Azure Queue Storage purpose. Explain that Azure Queue Storage is part of an Azure Storage account and provides a simple, scalable queue for asynchronous communication, rather than the advanced messaging features of RabbitMQ or Azure Service Bus.
- Order message queue workflow. Trace the described workflow: the order-placement endpoint puts generated messages in a queue, and the location-tracking background service consumes them to update location information. Explain that this applies the same principles used with RabbitMQ to a different message broker.
- Queue Storage host integration. Explain that Azure Queue Storage uses the standard Azure Storage hosting package and that its AppHost reference is passed to both the web frontend and API services.
- Queue Storage client setup. Explain which package the API project references, how the client is registered in both applications, and which service is resolved where a queue is used.
- Order message publishing sequence. Describe how the order-placement endpoint prepares the queue and sends a message, including why the queue is named orders-created.
- LocationUpdater message consumption loop. Describe how LocationUpdater consumes queued messages in ExecuteAsync(): it loops until cancellation, reads a batch of 10 messages on each iteration, and processes each message in a for-each block.
- Shared distributed cache. Explain why a cache is distributed when separate applications or machines can use the same stored data, and why caching can improve performance.
- Distributed lock coordination. Explain how a distributed lock limits concurrent access to a shared resource across applications and how it relates to a distributed cache.
- Redis component setup. Describe adding Aspire.Hosting.Redis to the Aspire host, registering a Redis component in AppHost.cs, and passing its reference to the API service for caching and locking.
- Redis cache client setup. Describe how the API application is prepared for Redis distributed caching: install the Aspire.StackExchange.Redis.DistributedCaching package in OnlineShop.ApiService, then configure the Redis cache client in Program.cs using a connectionName that matches the Aspire host’s Redis resource name.
- Product retrieval cache flow. Trace how the products endpoint checks the distributed cache, loads product data from the database on a miss, stores it as JSON, and serves it from the cache on a later request.
- Product cache invalidation. Describe when and why to remove a cached product entry and what happens on its next retrieval.
- Consistent stock updates. Explain why order placement needs a distributed lock when multiple users try to buy the same product.
- Redis lock acquisition. Describe how the endpoint attempts to acquire a Redis lock and what happens if that lock already exists.
- Reliable lock release. Explain how the endpoint releases an acquired lock even when the stock update fails.
- Redis Commander cache management. Describe how to open Redis Commander from the Aspire dashboard and use it to inspect or modify cached data and locks.
What it builds on
- Building a delivery-tracking system (chapter 4) · The earlier delivery-tracking system whose order placement was emulated.
- Adding a Keycloak container to an Aspire host (chapter 2) · Adding a container through an Aspire host.
- Working with the SQL data model (chapter 3) · Working with the SQL data model.
- Setting up a SignalR hub (chapter 4) · The SignalR hub used for delivery tracking.
- Azure Storage integration (chapter 3) · Introduces Azure Storage integrations.
- Hosting Azure Storage components (chapter 3) · Hosting Azure Storage components in Aspire.
- Connecting Aspire to an existing database (chapter 3) · connecting Aspire to an existing data resource
Section by section
Introduction
Introduces how message brokers connect distributed services, how queued messages are processed, and how queue-based and topic-based delivery differ.
- Message broker communication role: Explain how a message broker lets independent services exchange messages without being directly connected, including why this suits asynchronous communication in distributed applications.
Why it matters: This explains why separate services can communicate without depending on a direct connection to one another. - Queued message processing flow: Trace a message from a service posting it to a queue through a receiving service reading and processing it, including what happens after processing.
Why it matters: The send-and-process flow is the basic pattern behind the chapter’s messaging examples. - Queue and topic delivery modes: Distinguish the typical subscriber and delivery behavior of queue-based processing from topic-based processing.
Why it matters: The distinction clarifies whether a message is handled by one subscriber or delivered to several.
Key terms: message broker, asynchronously, message queue, subscribes, processed, Queue-based processing, Topic-based processing, subscriber
7.1 Working with RabbitMQ
Introduces RabbitMQ as an asynchronous message broker and explains why a background queue suits the book’s order-placement workflow.
- RabbitMQ message broker role: Explain that RabbitMQ manages and routes messages between applications, enabling reliable, asynchronous communication across services.
Why it matters: This role explains why RabbitMQ fits a workflow that passes work between services. - Asynchronous order workflow rationale: Describe how placing an order can add a background message to notify the delivery driver, and explain why the customer can see success without waiting for driver assignment.
Why it matters: It connects the earlier emulated order tracking to a practical reason for using a message queue.
Key terms: RabbitMQ, message broker, AMQP, asynchronous message delivery, order-tracking functionality, queue, asynchronously
7.1.1 Hosting a RabbitMQ component
This subsection identifies the package and host-file setup needed to add a RabbitMQ container to an Aspire application.
- RabbitMQ container host setup: Describe how to begin hosting RabbitMQ in Aspire: add Aspire.Hosting.RabbitMQ to the Aspire host project, then use the component package in AppHost.cs to add a RabbitMQ container.
Why it matters: This establishes where RabbitMQ is added before services are configured to use it.
Key terms: Aspire.Hosting.RabbitMQ, Aspire host project, RabbitMQ container, AppHost.cs
7.1.2 Adding order-processing functionality
This section prepares the online shop to place orders by identifying products, representing and storing orders, and adding a minimal ordering UI.
- Product identification for orders: Explain how the web client specifies an order using product IDs and quantities, and why product IDs must be included in the product DTO returned by the API.
Why it matters: The client needs a reliable way to say which products, and how many of each, the customer ordered. - Order models and tables: Describe the two kinds of order data introduced and the corresponding database-creation change.
Why it matters: Order processing needs both an overall order record and records for the products in it. - Minimal ordering interface: Describe what the modified Home.razor page lets a user see and do.
Why it matters: The small UI gives users a way to exercise the order functionality without making interface design the focus.
Key terms: ProductsApiClient, product ID, quantity, ProductDto, /products endpoint, Orders, OrderItems, Models, database-creation script, Home.razor
7.1.3 Sending messages via RabbitMQ
This section adds an order-placement endpoint that saves an order and publishes its ID through RabbitMQ.
- RabbitMQ Client Registration: Describe how the API service is connected to RabbitMQ by installing the Aspire.RabbitMQ.Client package and using the RabbitMQ component’s registered name in Program.cs.
Why it matters: The API needs its RabbitMQ client configured before it can publish an order message. - Order Placement Transaction: Trace what the POST endpoint does before sending a message: validate the basket, calculate the total from the ordered products, save the order and its items, and commit or roll back the database transaction.
Why it matters: The message announces an order that the endpoint has already saved. - Order Queue Configuration: Explain what the orders.created queue’s durable, exclusive, autoDelete, and arguments settings control.
Why it matters: These settings determine how long the queue exists and which connections can use it. - Order Message Publication: Describe when the endpoint publishes its RabbitMQ message, what the message contains, and why this example leaves the exchange parameter blank.
Why it matters: The order ID gives the later consumer a message identifying the newly placed order.
Key terms: Aspire.RabbitMQ.Client, OnlineShop.ApiService, Program.cs, RabbitMQ component, POST endpoint, basket, order amount, order items, transaction, rollback
7.1.4 Consuming RabbitMQ messages
This section explains how a RabbitMQ message starts order tracking and how later location updates reach the tracking system.
- Order-created message consumption: Trace how the LocationUpdater background service responds to an orders.created message and what that means for the order-tracking map.
Why it matters: It connects order creation to the first visible location in the tracking workflow. - Subsequent location updates: Explain how the initial tracking location can later be updated through LocationHub.
Why it matters: The warehouse location is only a starting point; later coordinates can come from a separate client.
Key terms: LocationUpdater, orders.created, initial order-tracking location, LocationHub, SignalR client, UpdateLocation
7.2 Working with Azure Queue Storage
Introduces Azure Queue Storage as a simple messaging alternative and shows how it fits the existing order-placement and location-tracking workflow.
- Azure Queue Storage purpose: Explain that Azure Queue Storage is part of an Azure Storage account and provides a simple, scalable queue for asynchronous communication, rather than the advanced messaging features of RabbitMQ or Azure Service Bus.
Why it matters: This distinction explains why Azure Queue Storage is an option for the chapter’s messaging workflow. - Order message queue workflow: Trace the described workflow: the order-placement endpoint puts generated messages in a queue, and the location-tracking background service consumes them to update location information. Explain that this applies the same principles used with RabbitMQ to a different message broker.
Why it matters: Following the two services’ roles makes clear what stays the same when the messaging component changes.
Key terms: Azure Queue Storage, Azure Storage account, asynchronous communication, order-placement endpoint, location-tracking background service, queue
7.2.1 Hosting an emulated Azure Queue Storage component
This subsection identifies the package needed to host Azure Queue Storage and the services that receive its reference.
- Queue Storage host integration: Explain that Azure Queue Storage uses the standard Azure Storage hosting package and that its AppHost reference is passed to both the web frontend and API services.
Why it matters: This identifies where the Queue Storage integration is configured and which services receive its reference.
Key terms: Azure Queue Storage, Azure Storage component, Aspire.Hosting.Azure.Storage, AppHost.cs
7.2.2 Sending messages via Azure Queue Storage
This section shows how to register an Azure Queue Storage client and use it to publish an order-placement message.
- Queue Storage client setup: Explain which package the API project references, how the client is registered in both applications, and which service is resolved where a queue is used.
Why it matters: The client must be registered and available before the application can use the queue. - Order message publishing sequence: Describe how the order-placement endpoint prepares the queue and sends a message, including why the queue is named orders-created.
Why it matters: The publishing steps put the order message into a valid queue that exists before the send.
Key terms: Aspire.Azure.Storage.Queues, QueueServiceClient, Queue Storage reference, queue client, orders-created, Azure Queue Storage
7.2.3 Consuming Azure Queue Storage messages
Explains how the LocationUpdater service consumes Azure Queue Storage messages and why background message processing matters in distributed applications.
- LocationUpdater message consumption loop: Describe how LocationUpdater consumes queued messages in ExecuteAsync(): it loops until cancellation, reads a batch of 10 messages on each iteration, and processes each message in a for-each block.
Why it matters: This is the processing pattern the section gives for consuming queued messages. - Background messaging rationale: Explain why message brokers are useful in distributed applications: not every interaction between services should be synchronous, and queues let applications process messages in the background without affecting user experience.
Why it matters: It connects the queue-consumption example to the reason distributed applications use message brokers.
Key terms: LocationUpdater, ExecuteAsync(), cancellation, batch of 10 messages, for-each block, message brokers, message queues, synchronously, background
7.3 Overview of distributed cache and lock
This section explains how a shared cache improves access to frequently used data and how distributed locks coordinate access to shared resources.
- Shared distributed cache: Explain why a cache is distributed when separate applications or machines can use the same stored data, and why caching can improve performance.
Why it matters: This explains both the performance benefit of caching and the value of sharing a cache across services. - Distributed lock coordination: Explain how a distributed lock limits concurrent access to a shared resource across applications and how it relates to a distributed cache.
Why it matters: A shared lock coordinates applications that might otherwise change the same resource at the same time.
Key terms: distributed caching, RAM, Redis, distributed locking, distributed lock, race conditions, distributed cache
7.4 Setting up Redis for caching and locking
Introduces the upcoming Redis setup for caching and locking in an Aspire-hosted application.
7.4.1 Installing a Redis component
This subsection shows how to add a Redis component to an Aspire host, pass it to the API service, and optionally add Redis Commander.
- Redis component setup: Describe adding Aspire.Hosting.Redis to the Aspire host, registering a Redis component in AppHost.cs, and passing its reference to the API service for caching and locking.
Why it matters: The API must receive the Redis reference before it can use Redis for the chapter’s caching and locking examples. - Redis Commander companion container: Explain that WithRedisCommander() creates a separate Redis Commander container that can be accessed from the Aspire dashboard to manage Redis data in a browser.
Why it matters: It distinguishes the Redis instance from the companion service used to inspect or manage its data.
Key terms: Aspire.Hosting.Redis, Aspire host application, Redis component, API service, AppHost.cs, WithRedisCommander(), Redis Commander, Aspire dashboard
7.4.2 Installing the Redis client for distributed caching
This subsection sets up the API application to connect to an Aspire-hosted Redis resource for distributed caching.
- Redis cache client setup: Describe how the API application is prepared for Redis distributed caching: install the Aspire.StackExchange.Redis.DistributedCaching package in OnlineShop.ApiService, then configure the Redis cache client in Program.cs using a connectionName that matches the Aspire host’s Redis resource name.
Why it matters: This setup gives the API a client connected to the Redis resource it will use for caching.
Key terms: OnlineShop.ApiService, Aspire.StackExchange.Redis.DistributedCaching, Program.cs, connectionName, cache
7.4.3 Using a distributed cache
Shows how the products endpoint caches database results in Redis, shares cached data across API instances, and invalidates entries when products change.
- Product retrieval cache flow: Trace how the products endpoint checks the distributed cache, loads product data from the database on a miss, stores it as JSON, and serves it from the cache on a later request.
Why it matters: This explains why repeated product requests can avoid a slower database lookup. - Cache access across instances: Explain why the distributed cache remains useful when the API runs multiple instances.
Why it matters: Requests can benefit from cached data even when they reach different API instances. - Product cache invalidation: Describe when and why to remove a cached product entry and what happens on its next retrieval.
Why it matters: Invalidation keeps cached product information from becoming outdated after catalog changes.
Key terms: IDistributedCache, GetStringAsync(), SetStringAsync(), JSON string, distributed cache, scale out, service instance, invalidate, RemoveAsync(), repopulated
7.4.4 Using a distributed lock
This section shows how a simplified order-placement endpoint uses a Redis lock to protect stock updates when orders overlap.
- Consistent stock updates: Explain why order placement needs a distributed lock when multiple users try to buy the same product.
Why it matters: The lock protects the stock adjustment when orders overlap. - Redis lock acquisition: Describe how the endpoint attempts to acquire a Redis lock and what happens if that lock already exists.
Why it matters: A product-specific key determines which updates contend for the lock, while expiry limits the effect of a failed lock holder. - Reliable lock release: Explain how the endpoint releases an acquired lock even when the stock update fails.
Why it matters: Releasing the lock lets another update proceed after this attempt finishes, including when it fails.
Key terms: distributed locking, thread-safe, stock, IConnectionMultiplexer, GetDatabase(), LockTakeAsync(), lock key, lock value, lock expiry time, LockReleaseAsync()
7.5 Managing the Redis cache
This section shows how to inspect and manage Redis data with Redis Commander and explains how an Aspire AppHost can reference externally hosted Redis through a connection string.
- Redis Commander cache management: Describe how to open Redis Commander from the Aspire dashboard and use it to inspect or modify cached data and locks.
Why it matters: Redis Commander gives you a way to inspect cached data and locks and intervene when they become stale. - External Redis connection strings: Explain how an Aspire AppHost can refer to Redis hosted elsewhere instead of hosting its own Redis container, and state the condition under which the same approach applies to other external resources.
Why it matters: An Aspire application need not host its own Redis container when a suitable Redis instance already exists elsewhere.
Key terms: Redis Commander, Aspire dashboard, cached data, locks, generic connection string, builder.AddConnectionString(), AppHost
7.6 Summary
Where this is used later
- Running multiple instances of a service (chapter 9) · Provides context for running multiple instances of a service.
- Modifying the dashboard (chapter 9) · Prior work with managing the Redis cache.
Concepts explained in this chapter
- distributed lockA distributed lock grants exclusive access to a shared resource across processes or application instances.
- distributed cacheA distributed cache stores data for access by multiple processes or application instances.
- message brokerA software system that mediates communication among applications or services by sending, receiving, and routing messages.
- Azure Storage QueuesAn Azure service that stores messages in queues for asynchronous exchange between services.
- distributed cachingDistributed caching stores frequently accessed data in a shared cache accessible across multiple servers or nodes.
- RabbitMQRabbitMQ is an open-source message broker that routes messages between producers and consumers.
- RedisRedis is an in-memory data structure store commonly used as a database, cache, and message broker.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
