Exercise - Publish and subscribe to events in Azure Managed Redis
Lab walkthrough: a Flask app on Azure Managed Redis that publishes JSON events, broadcasts to channels and listens with SUBSCRIBE and PSUBSCRIBE.
What this covers
This 30–40 minute lab builds a small Flask web app that publishes and subscribes to Redis channels on Azure Managed Redis from one page. You complete the code that connects with Microsoft Entra ID, publishes an order event, broadcasts an announcement to every channel, formats incoming messages, listens on a background thread, and subscribes to exact channels and patterns. Then you watch the messages arrive live. The lab makes the pub/sub delivery rules concrete: subscribe before you publish, or the message is gone.
Exam weight: Develop AI solutions by using Azure data management services (30–35%)
Skills measured
- "Implement Azure Managed Redis data operations, including caching, expiration, and invalidation"
Key concepts
Lab flow
- Download the starter files and set the resource group and region in
azdeploy.py. - Sign in with
az login, register Microsoft.Cache, and install or upgrade theredisenterpriseextension. - Run the script and choose the deploy option. Provisioning takes 5–10 minutes, so write code while it runs.
- Fill in
client/pubsub_functions.py. - After deployment, use the script to create the database, configure Entra ID access for your account, and write
.env/.env.ps1withREDIS_HOST. - Create a virtual environment in
client, installrequirements.txt, runapp.py, and openhttp://localhost:5000. - Subscribe to a channel and a pattern, publish events, broadcast, and check which messages arrive.
- Delete the resource group.
The functions you write
| Function | What it does | Key point |
|---|---|---|
get_client() | Builds a redis.Redis client from REDIS_HOST, TLS, port 10000, decode_responses=True, Entra credential provider | No access keys anywhere |
publish_order_created() | Serializes an order event to JSON and publishes it to orders:created | Return value = number of subscribers reached |
broadcast_to_all() | Publishes one announcement to every channel in AVAILABLE_CHANNELS and adds up the receivers | Shows that pub/sub is per channel |
format_message() | Pulls out channel, pattern, event type and fields, and falls back to raw data for non-JSON | Handles both message and pmessage |
listen_messages() | Background thread that iterates pubsub.listen() and buffers formatted messages | Errors are shown in the UI as system messages |
subscribe_to_channel() / subscribe_to_pattern() | subscribe("orders:created") or psubscribe("orders:*"), then restart the listener | New subscriptions only see future messages |
What you should observe
- If you publish while nothing is subscribed, the return value is 0 and the message is lost.
- A pattern subscription (
orders:*) receives events fromorders:created,orders:shippedand so on, delivered aspmessage. - If you subscribe to both
orders:createdandorders:*, one publish produces two deliveries: onemessageand onepmessage.
How it works
Hands-on
Azure portal
- After the deploy option finishes, open the AMR resource and copy its Endpoint.
- On Authentication, confirm that your account is listed under Microsoft Entra Authentication.
- While the app runs, open Metrics and watch Connected Clients. The listener holds its own connection.
Azure CLI
az login
az provider register --namespace Microsoft.Cache
az extension add --upgrade --name redisenterprise
python azdeploy.py
cd client
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
source ../.env && python app.py
az group delete --name <rg-name> --no-wait --yesPython
import json, os, threading, time
import redis
from redis_entraid.cred_provider import create_from_default_azure_credential
AVAILABLE_CHANNELS = ["orders:created", "orders:shipped", "inventory:alerts"]
def get_client():
provider = create_from_default_azure_credential(("https://redis.azure.com/.default",))
return redis.Redis(host=os.environ["REDIS_HOST"], port=10000, ssl=True,
decode_responses=True, credential_provider=provider)
r = get_client()
def publish_order_created(order_id, amount):
event = {"event_type": "order_created", "order_id": order_id,
"amount": amount, "ts": time.time()}
return r.publish("orders:created", json.dumps(event)) # subscriber count
def broadcast_to_all(text):
return sum(r.publish(ch, json.dumps({"event_type": "announcement", "text": text}))
for ch in AVAILABLE_CHANNELS)
def format_message(msg):
try:
body = json.loads(msg["data"])
except (TypeError, ValueError):
body = {"raw": msg["data"]}
return {"channel": msg["channel"], "pattern": msg.get("pattern"), **body}
buffer = []
pubsub = r.pubsub()
pubsub.subscribe("orders:created")
pubsub.psubscribe("orders:*")
def listen_messages():
for msg in pubsub.listen():
if msg["type"] in ("message", "pmessage"):
buffer.append(format_message(msg))
threading.Thread(target=listen_messages, daemon=True).start()
print(publish_order_created("A-100", 42.5)) # 2 deliveries: message + pmessageExam traps
| Lab question | Answer |
|---|---|
Why did publish return 0? | No active subscribers. The message is dropped |
Which call listens to orders:*? | psubscribe |
| Which message type do pattern deliveries have? | pmessage |
| Why use a background thread? | listen() blocks, and the web request thread must stay free |
| How does the app authenticate? | Entra ID token via redis-entraid, with no access key |
Quick review
- The lab needs Python 3.12+, Azure CLI, the
redisenterpriseextension and the Microsoft.Cache provider. - The client connects on port 10000 with TLS and an Entra credential provider.
publishreturns the number of subscribers that received the message. 0 means it was lost.subscribetakes exact channels and deliversmessage.psubscribetakes patterns and deliverspmessage.- The listener runs on its own thread with its own connection.
Practice
Practice: Azure Managed Redis data operations