Partager via


Héberger et déployer des applications côté Blazor serveur

Remarque

Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 9 de cet article.

Avertissement

Cette version d'ASP.NET Core n'est plus prise en charge. Pour plus d’informations, consultez la stratégie de support .NET et .NET Core. Pour la version actuelle, consultez la version .NET 9 de cet article.

Importante

Ces informations portent sur un produit en phase de pré-lancement, qui est susceptible d’être substantiellement modifié avant sa commercialisation. Microsoft n’offre aucune garantie, expresse ou implicite, en ce qui concerne les informations fournies ici.

Pour la version actuelle, consultez la version .NET 9 de cet article.

Cet article explique comment héberger et déployer des applications côté Blazor serveur (Blazor Web Apps et Blazor Server applications) à l’aide de ASP.NET Core.

Valeurs de configuration de l’hôte

Les applications côté Blazor serveur peuvent accepter des valeurs de configuration d’hôte générique.

Déploiement

Dans un modèle d’hébergement côté serveur, Blazor est exécuté sur le serveur au sein d’une application ASP.NET Core. Les mises à jour de l’interface utilisateur, la gestion des événements et les appels JavaScript sont gérés via une SignalR connexion.

Un serveur web capable d’héberger une application ASP.NET Core est nécessaire. Visual Studio inclut un modèle de projet d’application côté serveur. Pour plus d’informations sur les modèles de projet Blazor, consultez ASP.NET structure de projet principale Blazor.

Publiez une application dans la configuration Release et déployez le contenu du dossier bin/Release/{TARGET FRAMEWORK}/publish, où le placeholder {TARGET FRAMEWORK} est l'infrastructure cible.

Extensibilité

Lorsque vous envisagez l’extensibilité d’un seul serveur (scale-up), la mémoire disponible pour une application est probablement la première ressource que l’application épuise à mesure que les demandes des utilisateurs augmentent. La mémoire disponible sur le serveur affecte les éléments suivants :

  • Nombre de circuits actifs qu’un serveur peut prendre en charge.
  • Latence de l’interface utilisateur sur le client.

Pour obtenir des conseils sur la création d’applications côté Blazor serveur sécurisées et évolutives, consultez les ressources suivantes :

Chaque circuit utilise environ 250 Ko de mémoire pour une application de style Hello World minimale. La taille d’un circuit dépend du code de l’application et des exigences de maintenance de l’état associées à chaque composant. Nous vous recommandons de mesurer les demandes de ressources pendant le développement de votre application et de votre infrastructure, mais la base de référence suivante peut être un point de départ dans la planification de votre cible de déploiement : si vous attendez que votre application prend en charge 5 000 utilisateurs simultanés, envisagez de budgéter au moins 1,3 Go de mémoire serveur à l’application (ou ~273 Ko par utilisateur).

Configuration SignalR

SignalRLes conditions d’hébergement et de mise à l’échelle s’appliquent aux Blazor applications qui utilisent SignalR.

Pour plus d'informations sur SignalR dans les applications Blazor, y compris les instructions de configuration, consultez les conseils pour ASP.NET Core BlazorSignalR.

Transports

Blazorfonctionne mieux lors de l’utilisation SignalR de WebSockets comme transport en raison d’une latence plus faible, d’une meilleure fiabilité et d’une sécurité améliorée. L’interrogation longue est utilisée lorsque SignalR WebSockets n’est pas disponible ou si l’application est explicitement configurée pour utiliser l’interrogation longue.

Un avertissement de console s’affiche si l’interrogation longue est utilisée :

Échec de la connexion via WebSockets, avec le transport de secours d’interrogation longue. Cela peut être dû à un VPN ou à un proxy bloquant la connexion.

Échecs de déploiement et de connexion globaux

Recommandations pour les déploiements globaux vers des centres de données géographiques :

  • Déployez l’application dans les régions où résident la plupart des utilisateurs.
  • Prenez en compte la latence accrue pour le trafic entre les continents. Pour contrôler l’apparence de l’interface utilisateur de reconnexion, consultez les instructions ASP.NET CoreBlazorSignalR.
  • Envisagez d’utiliser le service AzureSignalR.

Azure App Service

L’hébergement sur Azure App Service nécessite une configuration pour les WebSockets et l’affinité de session, également appelée affinité ARR (Application Request Routing).

Remarque

Une Blazor application sur Azure App Service ne nécessite pas Azure SignalR Service.

Activez les éléments suivants pour l’inscription de l’application dans Azure App Service :

  • WebSockets pour permettre le fonctionnement du transport WebSockets. La valeur par défaut est Désactivé.
  • Affinité de session pour rediriger les requêtes d'un utilisateur vers la même instance App Service. La valeur par défaut est Activé.
  1. Dans le portail Azure, accédez à l’application Web dans Services d’application.
  2. Ouvrez Paramètres>Configuration.
  3. Définissez Sockets Web sur Activé.
  4. Vérifiez que l’affinité de session est définie sur Activé.

Service Azure SignalR

Le service facultatif Azure SignalR fonctionne conjointement avec le hub SignalR de l’application pour effectuer la mise à l’échelle d’une application côté serveur sur un grand nombre de connexions simultanées. De plus, la portée générale du service et les centres de données hautes performances contribuent de manière significative à réduire la latence due aux emplacements géographiques.

Le service n’est pas nécessaire pour les applications Blazor hébergées dans Azure App Service ou Azure Container Apps, mais peut être utile dans d’autres environnements d’hébergement :

  • Pour faciliter le scale-out de la connexion.
  • Gérer la distribution globale.

Le service Azure SignalR avec le SDK v1.26.1 ou une version ultérieure prend en charge la reconnexion SignalR avec état (WithStatefulReconnect).

Si l’application utilise l’interrogation longue ou revient à l’interrogation longue au lieu de WebSockets, vous devrez peut-être configurer l’intervalle maximal d’interrogation (MaxPollIntervalInSecondspar défaut : 5 secondes, limite : 1 à 300 secondes), qui définit l’intervalle maximal d’interrogation autorisé pour les connexions d’interrogation longue dans le service Azure SignalR . Si la requête de sondage suivante n’arrive pas dans l’intervalle maximal d’interrogation, le service ferme la connexion cliente.

Pour obtenir des conseils sur l’ajout du service en tant que dépendance à un déploiement de production, consultez Publier une application ASP.NET Core SignalR sur Azure App Service.

Pour plus d’informations, consultez :

Azure Container Apps (Applications de Conteneur Azure)

Pour une exploration plus approfondie de la mise à l’échelle des applications côté Blazor serveur sur le service Azure Container Apps, consultez Mise à l’échelle ASP.NET Core Apps sur Azure. Le tutoriel explique comment créer et intégrer les services requis pour héberger des applications sur Azure Container Apps. Les étapes de base sont également fournies dans cette section.

  1. Configurez le service Azure Container Apps pour l’affinité de session en suivant les instructions de l’affinité de session dans Azure Container Apps (documentation Azure).

  2. Le service de protection des données principale (DP) ASP.NET doit être configuré pour conserver les clés dans un emplacement centralisé auquel toutes les instances de conteneur peuvent accéder. Les clés peuvent être stockées dans stockage Blob Azure et protégées avec Azure Key Vault. Le service DP utilise les clés pour désérialiser les composants Razor. Pour configurer le service DP afin d’utiliser Stockage Blob Azure et Azure Key Vault, référencez les packages NuGet suivants :

    Remarque

    Pour obtenir des conseils sur l'ajout de packages à des applications .NET, consultez les articles figurant sous Installer et gérer des packages dans Flux de travail de la consommation des packages (documentation NuGet). Vérifiez les versions du package sur NuGet.org.

  3. Mettez à jour Program.cs avec le code mis en évidence suivant :

    using Azure.Identity;
    using Microsoft.AspNetCore.DataProtection;
    using Microsoft.Extensions.Azure;
    
    var builder = WebApplication.CreateBuilder(args);
    var BlobStorageUri = builder.Configuration["AzureURIs:BlobStorage"];
    var KeyVaultURI = builder.Configuration["AzureURIs:KeyVault"];
    
    builder.Services.AddRazorPages();
    builder.Services.AddHttpClient();
    builder.Services.AddServerSideBlazor();
    
    builder.Services.AddAzureClientsCore();
    
    builder.Services.AddDataProtection()
                    .PersistKeysToAzureBlobStorage(new Uri(BlobStorageUri),
                                                    new DefaultAzureCredential())
                    .ProtectKeysWithAzureKeyVault(new Uri(KeyVaultURI),
                                                    new DefaultAzureCredential());
    var app = builder.Build();
    
    if (!app.Environment.IsDevelopment())
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }
    
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    
    app.UseRouting();
    
    app.UseAuthorization();
    
    app.MapRazorPages();
    
    app.Run();
    

    Les modifications précédentes permettent à l’application de gérer le service DP à l’aide d’une architecture centralisée et évolutive. DefaultAzureCredential découvre l’identité managée de l’application conteneur une fois le code déployé sur Azure et l’utilise pour se connecter au stockage Blob et au coffre de clés de l’application.

  4. Pour créer l’identité managée de l’application conteneur et lui accorder l’accès au stockage blob et à un coffre de clés, procédez comme suit :

    1. Dans le portail Azure, accédez à la page de vue d’ensemble de l’application conteneur.
    2. Sélectionnez Service Connector dans le volet de navigation gauche.
    3. Sélectionnez + Créer dans le volet de navigation supérieur.
    4. Dans le menu volant Créer une connexion , entrez les valeurs suivantes :
      • Conteneur : sélectionnez l’application conteneur que vous avez créée pour héberger votre application.
      • Type de service : sélectionnez Stockage Blob.
      • Abonnement : sélectionnez l’abonnement propriétaire de l’application conteneur.
      • Nom de la connexion : entrez un nom de scalablerazorstorage.
      • Type de client : sélectionnez .NET , puis sélectionnez Suivant.
    5. Sélectionnez Identité managée affectée par le système , puis sélectionnez Suivant.
    6. Utilisez les paramètres réseau par défaut et sélectionnez Suivant.
    7. Une fois qu’Azure a validé les paramètres, sélectionnez Créer.

    Répétez les paramètres précédents pour le coffre de clés. Sélectionnez le service de coffre de clés et la clé appropriés sous l’onglet Informations de base .

Remarque

L’exemple précédent utilise DefaultAzureCredential pour simplifier l’authentification lors du développement d’applications qui se déploient sur Azure en combinant les informations d’identification utilisées dans les environnements d’hébergement Azure avec les informations d’identification utilisées dans le développement local. Lors du passage en production, il est préférable d’opter pour une alternative, telle que ManagedIdentityCredential. Pour plus d’informations, consultez Authentifier les applications .NET hébergées par Azure auprès des ressources Azure à l’aide d’une identité managée affectée par le système.

IIS

Lorsque vous utilisez IIS, activez :

Pour plus d’informations, consultez les instructions et les liens croisés de ressources IIS externes dans Publier une application ASP.NET Core sur IIS.

Kubernetes

Créez une définition d’entrée avec les annotations Kubernetes suivantes pour l’affinité de session :

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: <ingress-name>
  annotations:
    nginx.ingress.kubernetes.io/affinity: "cookie"
    nginx.ingress.kubernetes.io/session-cookie-name: "affinity"
    nginx.ingress.kubernetes.io/session-cookie-expires: "14400"
    nginx.ingress.kubernetes.io/session-cookie-max-age: "14400"

Linux avec Nginx

Suivez les instructions pour une application ASP.NET Core SignalR avec les modifications suivantes :

  • Remplacez le chemin location de /hubroute (location /hubroute { ... }) par le chemin racine / (location / { ... }).
  • Supprimez la configuration de la mise en mémoire tampon du proxy (proxy_buffering off;), car le paramètre s’applique uniquement aux événements envoyés par le serveur (SSE), qui ne sont pas pertinents pour les interactions client-serveur de l’applicationBlazor.

Pour plus d’informations et pour obtenir de l’aide sur la configuration, consultez les ressources suivantes :

Linux avec Apache

Pour héberger une Blazor application derrière Apache sur Linux, configurez ProxyPass le trafic HTTP et WebSockets.

Dans l’exemple suivant :

  • Kestrel le serveur s’exécute sur l’ordinateur hôte.
  • L’application écoute le trafic sur le port 5000.
ProxyPreserveHost   On
ProxyPassMatch      ^/_blazor/(.*) http://localhost:5000/_blazor/$1
ProxyPass           /_blazor ws://localhost:5000/_blazor
ProxyPass           / http://localhost:5000/
ProxyPassReverse    / http://localhost:5000/

Activez les modules suivants :

a2enmod   proxy
a2enmod   proxy_wstunnel

Vérifiez la console du navigateur pour les erreurs de WebSockets. Exemples d’erreurs :

  • Firefox ne peut pas établir une connexion au serveur à l’adresse ws://the-domain-name.tld/_blazor?id=XXX
  • Erreur : Échec du démarrage du transport « WebSockets » : Erreur : une erreur s’est produite avec le transport.
  • Erreur : échec du démarrage du transport « Interrogation longue » : TypeError : this.transport is undefined
  • Erreur : Impossible de se connecter au serveur avec l’un des transports disponibles. Échec des WebSockets
  • Erreur : Impossible d’envoyer des données si la connexion n’est pas dans l’état « Connecté ».

Pour plus d’informations et pour obtenir de l’aide sur la configuration, consultez les ressources suivantes :

Mesurer la latence du réseau

JS L’interopérabilité peut être utilisée pour mesurer la latence du réseau, comme l’illustre l’exemple suivant.

MeasureLatency.razor :

@inject IJSRuntime JS

<h2>Measure Latency</h2>

@if (latency is null)
{
    <span>Calculating...</span>
}
else
{
    <span>@(latency.Value.TotalMilliseconds)ms</span>
}

@code {
    private DateTime startTime;
    private TimeSpan? latency;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            startTime = DateTime.UtcNow;
            var _ = await JS.InvokeAsync<string>("toString");
            latency = DateTime.UtcNow - startTime;
            StateHasChanged();
        }
    }
}
@inject IJSRuntime JS

<h2>Measure Latency</h2>

@if (latency is null)
{
    <span>Calculating...</span>
}
else
{
    <span>@(latency.Value.TotalMilliseconds)ms</span>
}

@code {
    private DateTime startTime;
    private TimeSpan? latency;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            startTime = DateTime.UtcNow;
            var _ = await JS.InvokeAsync<string>("toString");
            latency = DateTime.UtcNow - startTime;
            StateHasChanged();
        }
    }
}
@inject IJSRuntime JS

<h2>Measure Latency</h2>

@if (latency is null)
{
    <span>Calculating...</span>
}
else
{
    <span>@(latency.Value.TotalMilliseconds)ms</span>
}

@code {
    private DateTime startTime;
    private TimeSpan? latency;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            startTime = DateTime.UtcNow;
            var _ = await JS.InvokeAsync<string>("toString");
            latency = DateTime.UtcNow - startTime;
            StateHasChanged();
        }
    }
}
@inject IJSRuntime JS

<h2>Measure Latency</h2>

@if (latency is null)
{
    <span>Calculating...</span>
}
else
{
    <span>@(latency.Value.TotalMilliseconds)ms</span>
}

@code {
    private DateTime startTime;
    private TimeSpan? latency;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            startTime = DateTime.UtcNow;
            var _ = await JS.InvokeAsync<string>("toString");
            latency = DateTime.UtcNow - startTime;
            StateHasChanged();
        }
    }
}
@inject IJSRuntime JS

@if (latency is null)
{
    <span>Calculating...</span>
}
else
{
    <span>@(latency.Value.TotalMilliseconds)ms</span>
}

@code {
    private DateTime startTime;
    private TimeSpan? latency;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            startTime = DateTime.UtcNow;
            var _ = await JS.InvokeAsync<string>("toString");
            latency = DateTime.UtcNow - startTime;
            StateHasChanged();
        }
    }
}

Pour une expérience d’interface utilisateur raisonnable, nous recommandons une latence d’interface utilisateur soutenue de 250 ms ou moins.