本教學課程將逐步引導您如何在無伺服器模式中建立適用於 Socket.IO 服務的Web PubSub,並建置與 Azure Function 整合的聊天應用程式。
尋找本教學課程中使用的完整程式碼範例:
重要
默認模式需要持續性伺服器,您無法將 Web PubSub 與 Azure Function 整合為預設模式中的 Socket.IO。
重要
原始 連接字串 只會出現在本文中,僅供示範之用。
連接字串包含應用程式存取 Azure Web PubSub 服務所需的授權資訊。 連接字串內的存取金鑰類似於服務的根密碼。 在生產環境中,請一律保護您的存取金鑰。 使用 Azure 金鑰保存庫,安全地管理和輪替密鑰,並使用保護連線WebPubSubServiceClient。
避免將存取金鑰散發給其他使用者、寫入程式碼,或將其以純文字儲存在他人可以存取的位置。 如果您認為金鑰可能已遭盜用,請輪替金鑰。
必要條件
- 具有有效訂用帳戶的 Azure 帳戶。 如果您沒有帳戶,您可以建立免費帳戶。
- Azure 函式核心工具
- 對 Socket.IO 程式庫有一定的了解。
在無伺服器模式中為 Socket.IO 資源建立 Web PubSub
若要建立 Socket.IO 的 Web PubSub,您可以使用下列 Azure CLI 命令:
az webpubsub create -g <resource-group> -n <resource-name>--kind socketio --service-mode serverless --sku Premium_P1
在本機建立 Azure 函式專案
您應該遵循步驟來起始本機 Azure 函式專案。
請遵循步驟來安裝最新的 Azure Function 核心工具
在終端機視窗中或從命令提示字元執行下列命令,以在
SocketIOProject資料夾中建立專案:func init SocketIOProject --worker-runtime javascript --model V4此命令會建立 JavaScript 專案。 然後輸入資料夾
SocketIOProject以執行下列命令。函式套件組合目前不包含 Socket.IO 函式系結,因此您必須手動新增套件。
若要排除函式套件組合參考,請編輯host.json檔案,並移除下列幾行。
"extensionBundle": { "id": "Microsoft.Azure.Functions.ExtensionBundle", "version": "[4.*, 5.0.0)" }執行命令:
func extensions install -p Microsoft.Azure.WebJobs.Extensions.WebPubSubForSocketIO -v 1.0.0-beta.4
建立交涉的函式。 交涉函式,用於產生端點和令牌,供用戶端存取服務。
func new --template "Http Trigger" --name negotiate在 中
src/functions/negotiate.js開啟 檔案,並以下列程式代碼取代 :const { app, input } = require('@azure/functions'); const socketIONegotiate = input.generic({ type: 'socketionegotiation', direction: 'in', name: 'result', hub: 'hub' }); async function negotiate(request, context) { let result = context.extraInputs.get(socketIONegotiate); return { jsonBody: result }; }; // Negotiation app.http('negotiate', { methods: ['GET', 'POST'], authLevel: 'anonymous', extraInputs: [socketIONegotiate], handler: negotiate });此步驟會建立一個具有 HTTP 觸發器和
negotiate輸出繫結的函式SocketIONegotiation,這表示您可以使用 HTTP 呼叫來觸發該函式,並傳回由SocketIONegotiation繫結產生的協商結果。建立函式來傳送訊息。
func new --template "Http Trigger" --name message開啟檔案
src/functions/message.js,並以下列程式代碼取代 :const { app, output, trigger } = require('@azure/functions'); const socketio = output.generic({ type: 'socketio', hub: 'hub', }) async function chat(request, context) { context.extraOutputs.set(socketio, { actionName: 'sendToNamespace', namespace: '/', eventName: 'new message', parameters: [ context.triggerMetadata.socketId, context.triggerMetadata.message ], }); } // Trigger for new message app.generic('chat', { trigger: trigger.generic({ type: 'socketiotrigger', hub: 'hub', eventName: 'chat', parameterNames: ['message'], }), extraOutputs: [socketio], handler: chat });這會使用
SocketIOTriggerSocket.IO 用戶端訊息觸發,並使用SocketIO系結將訊息廣播至命名空間。建立函式以傳回要流覽的索引 HTML。
在下
public建立資料夾src/。使用下列內容建立 HTML 檔案
index.html。<html> <body> <h1>Socket.IO Serverless Sample</h1> <div id="chatPage" class="chat-container"> <div class="chat-input"> <input type="text" id="chatInput" placeholder="Type your message here..."> <button onclick="sendMessage()">Send</button> </div> <div id="chatMessages" class="chat-messages"></div> </div> <script src="https://cdn.socket.io/4.7.5/socket.io.min.js"></script> <script> function appendMessage(message) { const chatMessages = document.getElementById('chatMessages'); const messageElement = document.createElement('div'); messageElement.innerText = message; chatMessages.appendChild(messageElement); hatMessages.scrollTop = chatMessages.scrollHeight; } function sendMessage() { const message = document.getElementById('chatInput').value; if (message) { document.getElementById('chatInput').value = ''; socket.emit('chat', message); } } async function initializeSocket() { const negotiateResponse = await fetch(`/api/negotiate`); if (!negotiateResponse.ok) { console.log("Failed to negotiate, status code =", negotiateResponse.status); return; } const negotiateJson = await negotiateResponse.json(); socket = io(negotiateJson.endpoint, { path: negotiateJson.path, query: { access_token: negotiateJson.token } }); socket.on('new message', (socketId, message) => { appendMessage(`${socketId.substring(0,5)}: ${message}`); }) } initializeSocket(); </script> </body> </html>若要傳回 HTML 頁面,請建立函式並複製程式代碼:
func new --template "Http Trigger" --name index開啟檔案
src/functions/index.js,並以下列程式代碼取代 :const { app } = require('@azure/functions'); const fs = require('fs').promises; const path = require('path') async function index(request, context) { try { context.log(`HTTP function processed request for url "${request.url}"`); const filePath = path.join(__dirname,'../public/index.html'); const html = await fs.readFile(filePath); return { body: html, headers: { 'Content-Type': 'text/html' } }; } catch (error) { context.log(error); return { status: 500, jsonBody: error } } }; app.http('index', { methods: ['GET', 'POST'], authLevel: 'anonymous', handler: index });
如何在本機執行應用程式
備妥程式代碼之後,請遵循指示來執行範例。
設定 Azure 函式的 Azure 儲存體
Azure Functions 需要記憶體帳戶,即使在本機執行時也能運作。 選擇下列兩個選項之一:
- 執行免費的 Azurite 模擬器。
- 使用 Azure 儲存體服務。 如果您繼續使用它,可能會產生成本。
- 安裝 Azurite
npm install -g azurite
- 啟動 Azurite 儲存體模擬器:
azurite -l azurite -d azurite\debug.log
- 確定
AzureWebJobsStorage中的 local.settings.json 設定為UseDevelopmentStorage=true。
設定適用於 Socket.IO 的 Web PubSub 組態
- 將 連接字串 新增至函式應用程式:
func settings add WebPubSubForSocketIOConnectionString "<connection string>"
- 將中樞設定新增至 web PubSub for Socket.IO
az webpubsub hub create -n <resource name> -g <resource group> --hub-name hub --event-handler url-template="tunnel:///runtime/webhooks/socketio" user-event-pattern="*"
Azure CLI 命令可以取得 連接字串
az webpubsub key show -g <resource group> -n <resource name>
輸出包含 primaryConnectionString 和 secondaryConnectionString,而且兩者都可供使用。
設定通道
在無伺服器模式中,服務會使用 Webhook 來觸發函式。 當您嘗試在本機執行應用程式時,關鍵問題是讓服務能夠存取您的本機函式端點。
達成其最簡單的方式是使用 通道工具
安裝通道工具:
npm install -g @azure/web-pubsub-tunnel-tool執行通道
awps-tunnel run --hub hub --connection "<connection string>" --upstream http://127.0.0.1:7071--upstream是本機 Azure 函式公開的 URL。 埠可能不同,您可以在下一個步驟中啟動函式時檢查輸出。
執行範例應用程式
通道工具執行之後,您可以在本機執行函式應用程式:
func start
並在瀏覽網頁 http://localhost:7071/api/index。
下一步
接下來,您可以嘗試使用 Bicep,透過身分識別型驗證在在線部署應用程式: