https://learn.microsoft.com/en-us/azure/azure-signalr/signalr-quickstart-azure-functions-python?pivots=python-mode-decorators
Real-Time Progress Updates with Azure Functions and SignalR Using Python
This article demonstrates how to build a serverless Python application using Azure Functions and Azure SignalR Service to deliver real-time progress updates to clients. The solution includes HTTP endpoints, negotiation for SignalR connections, and timer triggers that broadcast messages and simulate progress updates for long-running operations.
Project File Structure
├── function_app.py
├── host.json
├── local.settings.json
├── requirements.txt
├── content/
│ └── index.html
Required Files
1. function_app.py
Main Azure Functions app with SignalR integration and timer triggers.
2. host.json
Azure Functions host configuration.
{
"version": "2.0"
}
3. local.settings.json
Local development settings (update AzureSignalRConnectionString
with your value).
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "python",
"AzureSignalRConnectionString": "<your-signalr-connection-string>"
}
}
4. requirements.txt
Python dependencies for Azure Functions and HTTP requests.
azure-functions
requests
5. content/index.html
Sample HTML page for the index endpoint.
<html>
<body> <h1>Azure SignalR Serverless Sample</h1> <div id="messages"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/3.1.7 /signalr.min.js"></script> <script> let messages = document.querySelector('#messages'); const apiBaseUrl = window.location.origin; const connection = new signalR.HubConnectionBuilder() .withUrl(apiBaseUrl + '/api') .configureLogging(signalR.LogLevel.Information) .build(); connection.on('newMessage', (message) => { document.getElementById("messages").innerHTML = message; });
// Display progress updates from the InsertTables timer connection.on('ProgressUpdate', (percent, message) => { const div = document.createElement('div'); div.textContent = `${percent}% - ${message}`; messages.appendChild(div); });
connection.start() .catch(console.error); </script></body>
</html>
Overview
- Azure Functions: Used to create serverless endpoints and timer triggers.
- Azure SignalR Service: Enables real-time communication between server and clients.
- Python: Implements the logic for HTTP triggers, negotiation, and progress simulation.
Key Features
- HTTP Trigger: Responds to HTTP requests with a personalized message.
- Index Endpoint: Serves a static HTML page.
- SignalR Negotiation: Provides connection info for SignalR clients.
- Broadcast Timer: Periodically fetches GitHub star count and broadcasts it.
- Insert Tables Timer: Simulates inserting tables and sends progress updates to SignalR clients.
Complete Code
import azure.functions as func
import logging
import azure.functions as func
import os
import requests
import json
import time
app = func.FunctionApp(http_auth_level=func.AuthLevel.FUNCTION)
etag = ''
start_count = 0
@app.route(route="http_trigger")
def http_trigger(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
name = req.params.get('name')
if not name:
try:
req_body = req.get_json()
except ValueError:
pass
else:
name = req_body.get('name')
if name:
return func.HttpResponse(f"Hello, {name}. This HTTP triggered function executed successfully.")
else:
return func.HttpResponse(
"This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.",
status_code=200
)
@app.route(route="index", auth_level=func.AuthLevel.ANONYMOUS)
def index(req: func.HttpRequest) -> func.HttpResponse:
f = open(os.path.dirname(os.path.realpath(__file__)) + '/content/index.html')
return func.HttpResponse(f.read(), mimetype='text/html')
@app.route(route="negotiate", auth_level=func.AuthLevel.ANONYMOUS, methods=["POST"])
@app.generic_input_binding(arg_name="connectionInfo", type="signalRConnectionInfo", hubName="serverless", connectionStringSetting="AzureSignalRConnectionString")
def negotiate(req: func.HttpRequest, connectionInfo) -> func.HttpResponse:
return func.HttpResponse(connectionInfo)
@app.timer_trigger(schedule="*/1 * * * *", arg_name="myTimer",
run_on_startup=False,
use_monitor=False)
@app.generic_output_binding(arg_name="signalRMessages", type="signalR", hubName="serverless", connectionStringSetting="AzureSignalRConnectionString")
def broadcast(myTimer: func.TimerRequest, signalRMessages: func.Out[str]) -> None:
global etag
global start_count
headers = {'User-Agent': 'serverless', 'If-None-Match': etag}
res = requests.get('https://api.github.com/repos/azure/azure-functions-python-worker', headers=headers)
if res.headers.get('ETag'):
etag = res.headers.get('ETag')
if res.status_code == 200:
jres = res.json()
start_count = jres['stargazers_count']
signalRMessages.set(json.dumps({
'target': 'newMessage',
'arguments': [ 'Current star count of https://api.github.com/repos/azure/azure-functions-python-worker is: ' + str(start_count) ]
}))
# New timer trigger: simulate inserting 10 tables and emit progress updates to SignalR
@app.timer_trigger(schedule="*/1 * * * *", arg_name="tablesTimer",
run_on_startup=False,
use_monitor=False)
@app.generic_output_binding(arg_name="signalRMessages", type="signalR", hubName="serverless", connectionStringSetting="AzureSignalRConnectionString")
def insert_tables_timer(tablesTimer: func.TimerRequest, signalRMessages: func.Out[str]) -> None:
logging.info('InsertTables timer started.')
total_tables = 10
messages = []
for i in range(1, total_tables + 1):
# Simulate time-consuming insert
time.sleep(1)
percent = (i * 100) // total_tables
msg = f"Table {i} of {total_tables} inserted."
messages.append({
'target': 'ProgressUpdate',
'arguments': [percent, msg]
})
# Final success message
messages.append({
'target': 'ProgressUpdate',
'arguments': [100, 'All tables inserted successfully.']
})
# Note: SignalR output binding in Python sends messages when the function completes.
# We therefore batch all progress updates in a single payload.
signalRMessages.set(json.dumps(messages))
logging.info('InsertTables timer completed and progress sent to SignalR.')
How It Works
- The
broadcast
function fetches the latest star count from GitHub and sends it to all connected SignalR clients every minute. - The
insert_tables_timer
function simulates a long-running operation (inserting tables) and sends progress updates to SignalR clients in real time. - All SignalR messages are sent when the function completes, so progress is batched and delivered together.
Next Steps
- Deploy the function app to Azure.
- Configure the Azure SignalR Service and connection string.
- Build a client (e.g., web app) to receive and display real-time updates.
This approach is ideal for scenarios where you need to inform users about the progress of background tasks, such as data imports or batch processing, using serverless technologies and real-time messaging.
index.html
No comments:
Post a Comment