Skip to content

Commit 78c483f

Browse files
authored
Add python toturial
1 parent 8b30f76 commit 78c483f

File tree

4 files changed

+324
-1
lines changed

4 files changed

+324
-1
lines changed
55.5 KB
Loading

articles/azure-web-pubsub/socketio-serverless-quickstart.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ We need to do two steps to deploy the sample app.
9797

9898
### Run Sample App
9999

100-
After the code is deployd, visit the website to try the sample:
100+
After the code is deployed, visit the website to try the sample:
101101

102102
```bash
103103
https://<function-endpoint>/api/index
Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
---
2+
title: 'Tutorial: Publish data to Socket.IO clients in Serverless Mode in Python'
3+
description: In this tutorial, you learn how to use Web PubSub for Socket.IO with Azure Function in Serverless Mode to publish data to sockets with a real-time NASDAQ index update application
4+
keywords: Socket.IO, serverless, azure function, Socket.IO on Azure, multi-node Socket.IO, scaling Socket.IO, socketio, azure socketio
5+
author: zackliu
6+
ms.author: chenyl
7+
ms.date: 09/01/2024
8+
ms.service: azure-web-pubsub
9+
ms.topic: tutorial
10+
---
11+
12+
# Tutorial: Publish data to Socket.IO clients in Serverless Mode in Azure Function with Python (Preview)
13+
14+
This tutorial guides you through how to publish data to Socket.IO clients in Serverless Mode in Python by creating a real-time NASDAQ index application integrated with Azure Function.
15+
16+
Find full code samples that are used in this tutorial:
17+
18+
- [Socket.IO Serverless Pyhon Sample](https://github.com/Azure/azure-webpubsub/tree/main/sdk/webpubsub-socketio-extension/examples/chat-serverless-javascript)
19+
20+
> [!IMPORTANT]
21+
> Default Mode needs a persistent server, you cannot integration Web PubSub for Socket.IO in default mode with Azure Function.
22+
23+
## Prerequisites
24+
25+
> [!div class="checklist"]
26+
> * An Azure account with an active subscription. If you don't have one, you can [create a free account](https://azure.microsoft.com/free/).
27+
> * [Azure Function core tool](../azure-functions/functions-run-local.md)
28+
> * Some familiarity with the Socket.IO library.
29+
30+
## Create a Web PubSub for Socket.IO resource in Serverless Mode
31+
32+
To create a Web PubSub for Socket.IO, you can use the following [Azure CLI](/cli/azure/install-azure-cli) command:
33+
34+
```azcli
35+
az webpubsub create -g <resource-group> -n <resource-name>---kind socketio --service-mode serverless --sku Premium_P1
36+
```
37+
38+
## Create an Azure Function project locally
39+
40+
You should follow the steps to initiate a local Azure Function project.
41+
42+
1. Follow to step to install the latest [Azure Function core tool](../azure-functions/functions-run-local.md#install-the-azure-functions-core-tools)
43+
44+
1. In the terminal window or from a command prompt, run the following command to create a project in the `SocketIOProject` folder:
45+
46+
```bash
47+
func init SocketIOProject --worker-runtime python
48+
```
49+
50+
This command creates a Python-based Function project. And enter the folder `SocketIOProject` to run the following commands.
51+
52+
1. Currently, the Function Bundle doesn't include Socket.IO Function Binding, so you need to manually add the package.
53+
54+
1. To eliminate the function bundle reference, edit the host.json file and remove the following lines.
55+
56+
```json
57+
"extensionBundle": {
58+
"id": "Microsoft.Azure.Functions.ExtensionBundle",
59+
"version": "[4.*, 5.0.0)"
60+
}
61+
```
62+
63+
1. Run the command:
64+
65+
```bash
66+
func extensions install -p Microsoft.Azure.WebJobs.Extensions.WebPubSubForSocketIO -v 1.0.0-beta.4
67+
```
68+
69+
1. Replace the content in `function_app.py` with the codes:
70+
71+
```python
72+
import random
73+
import azure.functions as func
74+
from azure.functions.decorators.core import DataType
75+
from azure.functions import Context
76+
import json
77+
78+
app = func.FunctionApp()
79+
current_index= 14000
80+
81+
@app.timer_trigger(schedule="* * * * * *", arg_name="myTimer", run_on_startup=False,
82+
use_monitor=False)
83+
@app.generic_output_binding("sio", type="socketio", data_type=DataType.STRING, hub="hub")
84+
def publish_data(myTimer: func.TimerRequest,
85+
sio: func.Out[str]) -> None:
86+
change = round(random.uniform(-10, 10), 2)
87+
global current_index
88+
current_index = current_index + change
89+
sio.set(json.dumps({
90+
'actionName': 'sendToNamespace',
91+
'namespace': '/',
92+
'eventName': 'update',
93+
'parameters': [
94+
current_index
95+
]
96+
}))
97+
98+
@app.function_name(name="negotiate")
99+
@app.route(auth_level=func.AuthLevel.ANONYMOUS)
100+
@app.generic_input_binding("negotiationResult", type="socketionegotiation", hub="hub")
101+
def negotiate(req: func.HttpRequest, negotiationResult) -> func.HttpResponse:
102+
return func.HttpResponse(negotiationResult)
103+
104+
@app.function_name(name="index")
105+
@app.route(auth_level=func.AuthLevel.ANONYMOUS)
106+
def index(req: func.HttpRequest) -> func.HttpResponse:
107+
path = './index.html'
108+
with open(path, 'rb') as f:
109+
return func.HttpResponse(f.read(), mimetype='text/html')
110+
```
111+
112+
Here's the explain of these functions:
113+
114+
- `publish_data`: This function updates the NASDAQ index every second with a random change and broadcasts it to connected clients with Socket.IO Output Binding.
115+
116+
- `negotiate`: This function responses a negotiation result to the client.
117+
118+
- `index`: This function returns a static HTML page.
119+
120+
121+
Then add a `index.html` file
122+
123+
Create the index.html file with the content:
124+
125+
```html
126+
<!DOCTYPE html>
127+
<html lang="en">
128+
<head>
129+
<meta charset="UTF-8">
130+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
131+
<title>Nasdaq Index</title>
132+
<style>
133+
/* Reset some default styles */
134+
* {
135+
margin: 0;
136+
padding: 0;
137+
box-sizing: border-box;
138+
}
139+
140+
body {
141+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
142+
background: linear-gradient(135deg, #f5f7fa, #c3cfe2);
143+
height: 100vh;
144+
display: flex;
145+
justify-content: center;
146+
align-items: center;
147+
}
148+
149+
.container {
150+
background-color: white;
151+
padding: 40px;
152+
border-radius: 12px;
153+
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
154+
text-align: center;
155+
max-width: 300px;
156+
width: 100%;
157+
}
158+
159+
.nasdaq-title {
160+
font-size: 2em;
161+
color: #003087;
162+
margin-bottom: 20px;
163+
}
164+
165+
.index-value {
166+
font-size: 3em;
167+
color: #16a34a;
168+
margin-bottom: 30px;
169+
transition: color 0.3s ease;
170+
}
171+
172+
.update-button {
173+
padding: 10px 20px;
174+
font-size: 1em;
175+
color: white;
176+
background-color: #003087;
177+
border: none;
178+
border-radius: 6px;
179+
cursor: pointer;
180+
transition: background-color 0.3s ease;
181+
}
182+
183+
.update-button:hover {
184+
background-color: #002070;
185+
}
186+
</style>
187+
</head>
188+
<body>
189+
<div class="container">
190+
<div class="nasdaq-title">NASDAQ</div>
191+
<div id="nasdaqIndex" class="index-value">14,000.00</div>
192+
</div>
193+
194+
<script src="https://cdn.socket.io/4.7.5/socket.io.min.js"></script>
195+
<script>
196+
function updateIndexCore(newIndex) {
197+
newIndex = parseFloat(newIndex);
198+
currentIndex = parseFloat(document.getElementById('nasdaqIndex').innerText.replace(/,/g, ''))
199+
change = newIndex - currentIndex;
200+
// Update the index value in the DOM
201+
document.getElementById('nasdaqIndex').innerText = newIndex.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2});
202+
203+
// Optionally, change the color based on increase or decrease
204+
const indexElement = document.getElementById('nasdaqIndex');
205+
if (change > 0) {
206+
indexElement.style.color = '#16a34a'; // Green for increase
207+
} else if (change < 0) {
208+
indexElement.style.color = '#dc2626'; // Red for decrease
209+
} else {
210+
indexElement.style.color = '#16a34a'; // Neutral color
211+
}
212+
}
213+
214+
async function init() {
215+
const negotiateResponse = await fetch(`/api/negotiate`);
216+
if (!negotiateResponse.ok) {
217+
console.log("Failed to negotiate, status code =", negotiateResponse.status);
218+
return;
219+
}
220+
const negotiateJson = await negotiateResponse.json();
221+
socket = io(negotiateJson.endpoint, {
222+
path: negotiateJson.path,
223+
query: { access_token: negotiateJson.token}
224+
});
225+
226+
socket.on('update', (index) => {
227+
updateIndexCore(index);
228+
});
229+
}
230+
231+
init();
232+
</script>
233+
</body>
234+
</html>
235+
```
236+
237+
The key part in the `index.html`:
238+
239+
```javascript
240+
async function init() {
241+
const negotiateResponse = await fetch(`/api/negotiate`);
242+
if (!negotiateResponse.ok) {
243+
console.log("Failed to negotiate, status code =", negotiateResponse.status);
244+
return;
245+
}
246+
const negotiateJson = await negotiateResponse.json();
247+
socket = io(negotiateJson.endpoint, {
248+
path: negotiateJson.path,
249+
query: { access_token: negotiateJson.token}
250+
});
251+
252+
socket.on('update', (index) => {
253+
updateIndexCore(index);
254+
});
255+
}
256+
```
257+
258+
It first negotiate with the Function App to get the Uri and the path to the service. And register a callback to update index.
259+
260+
## How to run the App locally
261+
262+
After code is prepared, following the instructions to run the sample.
263+
264+
### Set up Azure Storage for Azure Function
265+
266+
Azure Functions requires a storage account to work even running in local. Choose either of the two following options:
267+
268+
* Run the free [Azurite emulator](../storage/common/storage-use-azurite.md).
269+
* Use the Azure Storage service. This may incur costs if you continue to use it.
270+
271+
#### [Local emulation](#tab/storage-azurite)
272+
273+
1. Install the Azureite
274+
275+
```bash
276+
npm install -g azurite
277+
```
278+
279+
1. Start the Azurite storage emulator:
280+
281+
```bash
282+
azurite -l azurite -d azurite\debug.log
283+
```
284+
285+
1. Make sure the `AzureWebJobsStorage` in *local.settings.json* set to `UseDevelopmentStorage=true`.
286+
287+
#### [Azure Blob Storage](#tab/azure-blob-storage)
288+
289+
Update the project to use the Azure Blob Storage connection string.
290+
291+
```bash
292+
func settings add AzureWebJobsStorage "<storage-connection-string>"
293+
```
294+
295+
---
296+
297+
### Set up configuration of Web PubSub for Socket.IO
298+
299+
1. Add connection string to the Function APP:
300+
301+
```bash
302+
func settings add WebPubSubForSocketIOConnectionString "<connection string>"
303+
```
304+
305+
### Run Sample App
306+
307+
After tunnel tool is running, you can run the Function App locally:
308+
309+
```bash
310+
func start
311+
```
312+
313+
And visit the webpage at `http://localhost:7071/api/index`.
314+
315+
:::image type="content" source="./media/socketio-serverless-tutorial-python/image.png" alt-text="Screenshot of the app.":::
316+
317+
## Next steps
318+
Next, you can try to use Bicep to deploy the app online with identity-based authentication:
319+
320+
> [!div class="nextstepaction"]
321+
> [Quickstart: Build chat app with Azure Function in Socket.IO Serverless Mode](./socketio-serverless-quickstart.md)

articles/azure-web-pubsub/toc.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@
155155
items:
156156
- name: Build real time code streaming app
157157
href: socketio-build-realtime-code-streaming-app.md
158+
- name: Build an app to publish data to Socket.IO clients with Python in Serverless Mode
159+
href: socketio-serverless-tutorial-python.md
158160
- name: Build chat app with Azure Function in Serverless Mode
159161
href: socketio-serverless-tutorial.md
160162
- name: How-to guides

0 commit comments

Comments
 (0)