Partager via


Tutoriel : Créer une application de conversation instantanée avec Azure Functions en mode serverless (préversion)

Ce tutoriel vous guide tout au long de la création d’un service Web PubSub pour Socket.IO en mode serverless et la création d’une application de conversation intégrée à Azure Functions.

Obtenez les exemples de code complets utilisés dans ce tutoriel :

Important

Le mode par défaut a besoin d’un serveur persistant. Vous ne pouvez pas intégrer Web PubSub pour Socket.IO en mode par défaut avec Azure Functions.

Important

Des chaînes de connexion brutes sont utilisées dans cet article uniquement à des fins de démonstration.

Une chaîne de connexion contient les informations d’autorisation requises pour que votre application accède au service Azure Web PubSub. La clé d’accès dans la chaîne de connexion est comme un mot de passe principal pour votre service. Dans les environnements de production, protégez toujours vos clés d’accès. Utilisez Azure Key Vault pour gérer et faire pivoter vos clés en toute sécurité et sécuriser votre connexion avec WebPubSubServiceClient.

Évitez de distribuer des clés d’accès à d’autres utilisateurs, de les coder en dur ou de les enregistrer en texte brut dans un emplacement accessible à d’autres personnes. Effectuez une rotation de vos clés si vous pensez qu’elles ont pu être compromises.

Prérequis

Créer une ressource Web PubSub pour Socket.IO en mode serverless

Pour créer une ressource web PubSub pour Socket.IO, vous pouvez utiliser la commande Azure CLI suivante :

az webpubsub create -g <resource-group> -n <resource-name>--kind socketio --service-mode serverless --sku Premium_P1

Créer un projet Azure Functions localement

Vous devez suivre les étapes pour lancer un projet Azure Functions local.

  1. Suivez l’étape pour installer la dernière version d’Azure Functions Core Tools.

  2. Dans la fenêtre de terminal ou à partir d’une invite de commandes, exécutez la commande suivante pour créer un projet dans le dossier SocketIOProject :

    func init SocketIOProject --worker-runtime javascript --model V4
    

    Cette commande crée un projet JavaScript. Entrez le dossier SocketIOProject pour exécuter les commandes suivantes.

  3. Actuellement, le bundle de fonctions n’inclut pas la liaison de fonction Socket.IO. Vous devez donc ajouter manuellement le package.

    1. Pour éliminer la référence du bundle de fonctions, modifiez le fichier host.json et supprimez les lignes suivantes.

      "extensionBundle": {
          "id": "Microsoft.Azure.Functions.ExtensionBundle",
          "version": "[4.*, 5.0.0)"
      }
      
    2. Exécutez la commande suivante :

      func extensions install -p Microsoft.Azure.WebJobs.Extensions.WebPubSubForSocketIO -v 1.0.0-beta.4
      
  4. Créez une fonction pour la négociation. Fonction de négociation utilisée pour générer des points de terminaison et des jetons pour que le client accède au service.

    func new --template "Http Trigger" --name negotiate
    

    Ouvrez le fichier dans src/functions/negotiate.js et remplacez le contenu par le code suivant :

    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
    });
    

    Cette étape crée une fonction negotiate avec un déclencheur HTTP et une liaison de sortie SocketIONegotiation, ce qui signifie que vous pouvez utiliser un appel HTTP pour déclencher la fonction et retourner un résultat de négociation généré par la liaison SocketIONegotiation.

  5. Créez une fonction pour la gestion des messages.

    func new --template "Http Trigger" --name message
    

    Ouvrez le fichier src/functions/message.js et remplacez le contenu par le code suivant :

    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
    });
    

    Cela utilise SocketIOTrigger pour le déclenchement par un message client Socket.IO, et utilise la liaison SocketIO pour diffuser des messages à l’espace de noms.

  6. Créez une fonction pour retourner un fichier HTML d’index pour la visite.

    1. Créez un dossier public sous src/.

    2. Créez un fichier HTML index.html avec le contenu suivant.

      <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>
      
    3. Pour retourner la page HTML, créez une fonction et copiez les codes :

      func new --template "Http Trigger" --name index
      
    4. Ouvrez le fichier src/functions/index.js et remplacez le contenu par le code suivant :

      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
      });
      
      

Comment exécuter l’application localement

Une fois le code préparé, suivez les instructions pour exécuter l’exemple.

Configurer Stockage Azure pour Azure Functions

Un compte de stockage est nécessaire pour l’utilisation d’Azure Functions, même en cas d’exécution locale. Choisissez l’une des deux options suivantes :

  • Exécutez l’émulateur Azurite gratuit.
  • Utilisez le service Stockage Azure. Cela peut entraîner des coûts si vous continuez à l’utiliser.
  1. Installez l’émulateur Azurite.
npm install -g azurite
  1. Démarrez l’émulateur de stockage Azurite :
azurite -l azurite -d azurite\debug.log
  1. Vérifiez que le AzureWebJobsStorage dans local.settings.json est défini sur UseDevelopmentStorage=true.

Définissez la configuration de Web PubSub pour Socket.IO

  1. Ajoutez une chaîne de connexion à l’application de fonction :
func settings add WebPubSubForSocketIOConnectionString "<connection string>"
  1. Ajoutez des paramètres de hub à Web PubSub pour 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="*"

La chaîne de connexion peut être obtenue par la commande Azure CLI

az webpubsub key show -g <resource group> -n <resource name>

La sortie contient primaryConnectionString et secondaryConnectionString, et les deux sont disponibles.

Configurer le tunnel

En mode serverless, le service utilise des webhooks pour déclencher la fonction. Lorsque vous essayez d’exécuter l’application localement, l’un des soucis majeurs consiste à permettre au service d’accéder à votre point de terminaison de fonction local.

Le moyen le plus simple d’y parvenir consiste à utiliser Tunnel Tool.

  1. Installez Tunnel Tool :

    npm install -g @azure/web-pubsub-tunnel-tool
    
  2. Exécutez le tunnel :

    awps-tunnel run --hub hub --connection "<connection string>" --upstream http://127.0.0.1:7071
    

    --upstream est l’URL exposée par la fonction Azure locale. Le port peut être différent, et vous pouvez vérifier la sortie lors du démarrage de la fonction à l’étape suivante.

Exécuter l’exemple d’application

Une fois que l’outil tunnel est en cours d’exécution, vous pouvez exécuter l’application de fonction localement :

func start

Et accéder à la page web à http://localhost:7071/api/index.

Capture d’écran de l’application de conversation serverless.

Étapes suivantes

Ensuite, vous pouvez essayer d’utiliser Bicep pour déployer l’application en ligne avec l’authentification basée sur l’identité :