托管和部署 Blazor ServerHost and deploy Blazor Server

作者:Luke LathamRainer StropekDaniel RothBy Luke Latham, Rainer Stropek, and Daniel Roth

主机配置值Host configuration values

Blazor Server 应用可以接受通用主机配置值Blazor Server apps can accept Generic Host configuration values.

部署Deployment

使用 Blazor Server 托管模型,可从 ASP.NET Core 应用中在服务器上执行 Blazor。Using the Blazor Server hosting model, Blazor is executed on the server from within an ASP.NET Core app. UI 更新、事件处理和 JavaScript 调用是通过 SignalR 连接进行处理。UI updates, event handling, and JavaScript calls are handled over a SignalR connection.

能够托管 ASP.NET Core 应用的 Web 服务器是必需的。A web server capable of hosting an ASP.NET Core app is required. Visual Studio 包括“Blazor Server 应用”项目模板(使用 dotnet new 命令时为 blazorserverside 模板)。Visual Studio includes the Blazor Server App project template (blazorserverside template when using the dotnet new command).

可伸缩性Scalability

计划部署以将可用的基础设施充分用于 Blazor Server 应用。Plan a deployment to make the best use of the available infrastructure for a Blazor Server app. 请参阅以下资源来解决 Blazor Server 应用的可伸缩性:See the following resources to address Blazor Server app scalability:

部署服务器Deployment server

考虑单一服务器(纵向扩展)的可伸缩性时,应用可用的内存可能是用户需求增加时应用将耗尽的第一个资源。When considering the scalability of a single server (scale up), the memory available to an app is likely the first resource that the app will exhaust as user demands increase. 服务器上的可用内存影响以下因素:The available memory on the server affects the:

  • 服务器可以支持的主动电路数。Number of active circuits that a server can support.
  • 客户端上的 UI 延迟。UI latency on the client.

有关生成安全且可伸缩的 Blazor 服务器应用的指南,请参阅 ASP.NET Core Blazor Server 的威胁缓解指南For guidance on building secure and scalable Blazor server apps, see ASP.NET Core Blazor Server 的威胁缓解指南.

每个电路使用约 250 KB 的内存来实现至少为 Hello World 样式的应用。Each circuit uses approximately 250 KB of memory for a minimal Hello World-style app. 电路大小取决于应用代码和与每个组件相关的状态维护要求。The size of a circuit depends on the app's code and the state maintenance requirements associated with each component. 我们建议你在开发应用和基础设施的过程中衡量资源需求,但在计划部署目标时可以将以下基准作为起点:如果希望应用支持 5,000 个并发用户,请考虑为应用预算至少 1.3 GB 服务器内存(或每用户 ~273 KB)。We recommend that you measure resource demands during development for your app and infrastructure, but the following baseline can be a starting point in planning your deployment target: If you expect your app to support 5,000 concurrent users, consider budgeting at least 1.3 GB of server memory to the app (or ~273 KB per user).

SignalR 配置SignalR configuration

Blazor Server 应用使用 ASP.NET Core SignalR 与浏览器进行通信。Blazor Server apps use ASP.NET Core SignalR to communicate with the browser. SignalR 的托管和缩放条件适用于 Blazor Server 应用。SignalR's hosting and scaling conditions apply to Blazor Server apps.

由于低延迟、可靠性和安全性,使用 WebSocket 作为 SignalR 传输时,Blazor 的效果最佳。Blazor works best when using WebSockets as the SignalR transport due to lower latency, reliability, and security. 当 WebSocket 不可用时,或在将应用显式配置为使用长轮询时,SignalR 将使用长轮询。Long Polling is used by SignalR when WebSockets isn't available or when the app is explicitly configured to use Long Polling. 部署到 Azure 应用服务时,请在服务的 Azure 门户设置中将应用配置为使用 WebSocket。When deploying to Azure App Service, configure the app to use WebSockets in the Azure portal settings for the service. 有关为 Azure 应用服务配置应用的详细信息,请参阅 SignalR 发布指南For details on configuring the app for Azure App Service, see the SignalR publishing guidelines.

Azure SignalR 服务Azure SignalR Service

我们建议将 Azure SignalR 服务用于 Blazor Server 应用。We recommend using the Azure SignalR Service for Blazor Server apps. 该服务允许将 Blazor Server 应用扩展到大量并发 SignalR 连接。The service allows for scaling up a Blazor Server app to a large number of concurrent SignalR connections. 此外,SignalR 服务的全球覆盖和高性能数据中心可帮助显著减少由于地理位置造成的延迟。In addition, the SignalR service's global reach and high-performance data centers significantly aid in reducing latency due to geography.

重要

禁用 WebSocket 后,Azure 应用服务使用 HTTP 长轮询模拟实时连接。When WebSockets are disabled, Azure App Service simulates a real-time connection using HTTP Long Polling. HTTP 长轮询明显比在启用 WebSocket 的情况下运行慢,WebSocket 不使用轮询来模拟客户机-服务器连接。HTTP Long Polling is noticeably slower than running with WebSockets enabled, which doesn't use polling to simulate a client-server connection.

建议对部署到 Azure 应用服务的 Blazor Server 应用使用 WebSocket。We recommend using WebSockets for Blazor Server apps deployed to Azure App Service. 默认情况下,Azure SignalR 服务使用 WebSockets。The Azure SignalR Service uses WebSockets by default. 如果应用不使用 Azure SignalR 服务,请参阅 SignalR将 ASP.NET Core 应用程序发布到 Azure App ServiceIf the app doesn't use the Azure SignalR Service, see SignalR将 ASP.NET Core 应用程序发布到 Azure App Service.

有关详细信息,请参阅:For more information, see:

ConfigurationConfiguration

若要为 SignalR 服务配置应用,应用必须支持粘滞会话;在此情况下,客户端在预呈现时被重定向回同一服务器To configure an app for the Azure SignalR Service, the app must support sticky sessions, where clients are redirected back to the same server when prerendering. ServerStickyMode 选项或配置值设置为 RequiredThe ServerStickyMode option or configuration value is set to Required. 通常,应用使用下述方法之一创建配置:Typically, an app creates the configuration using ONE of the following approaches:

  • Startup.ConfigureServices:Startup.ConfigureServices:

    services.AddSignalR().AddAzureSignalR(options =>
    {
        options.ServerStickyMode = 
            Microsoft.Azure.SignalR.ServerStickyMode.Required;
    });
    
  • 配置(使用下述方法之一):Configuration (use ONE of the following approaches):

    • appsettings.json中:In appsettings.json:

      "Azure:SignalR:StickyServerMode": "Required"
      
    • Azure 门户中的应用服务“配置” > “应用程序设置”(名称:Azure__SignalR__StickyServerMode,值:Required) 。The app service's Configuration > Application settings in the Azure portal (Name: Azure__SignalR__StickyServerMode, Value: Required). 如果预配 Azure SignalR 服务,则为应用自动采用此方式。This approach is adopted for the app automatically if you provision the Azure SignalR Service.

预配 Azure SignalR 服务Provision the Azure SignalR Service

若要在 Visual Studio 中为应用预配 Azure SignalR 服务:To provision the Azure SignalR Service for an app in Visual Studio:

  1. 在 Visual Studio 中创建适用于 Blazor Server 应用的 Azure 应用发布配置文件。Create an Azure Apps publish profile in Visual Studio for the Blazor Server app.
  2. Azure SignalR 服务 依赖项添加到配置文件。Add the Azure SignalR Service dependency to the profile. 如果 Azure 订阅没有要分配给应用的预先存在的 Azure SignalR 服务实例,请选择“创建新的 Azure SignalR 服务实例”以预配新的服务实例。If the Azure subscription doesn't have a pre-existing Azure SignalR Service instance to assign to the app, select Create a new Azure SignalR Service instance to provision a new service instance.
  3. 将应用发布到 Azure。Publish the app to Azure.

如果在 Visual Studio 中预配 Azure SignalR 服务,则会自动启用粘滞会话,并将 SignalR 连接字符串添加到应用服务的配置中。Provisioning the Azure SignalR Service in Visual Studio automatically enables sticky sessions and adds the SignalR connection string to the app service's configuration.

IISIIS

使用 IIS 时,请启用:When using IIS, enable:

KubernetesKubernetes

使用以下粘滞会话的 Kubernetes 注释创建入口定义:Create an ingress definition with the following Kubernetes annotations for sticky sessions:

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 与 NginxLinux with Nginx

要使 SignalR Websocket 正常运行,请确保将代理的 UpgradeConnection 标头设置为以下值,并将 $connection_upgrade 映射到以下值之一:For SignalR WebSockets to function properly, confirm that the proxy's Upgrade and Connection headers are set to the following values and that $connection_upgrade is mapped to either:

  • 默认情况下为升级标头值。The Upgrade header value by default.
  • 缺少升级标头或升级标头为空时为 closeclose when the Upgrade header is missing or empty.
http {
    map $http_upgrade $connection_upgrade {
        default Upgrade;
        ''      close;
    }

    server {
        listen      80;
        server_name example.com *.example.com
        location / {
            proxy_pass         http://localhost:5000;
            proxy_http_version 1.1;
            proxy_set_header   Upgrade $http_upgrade;
            proxy_set_header   Connection $connection_upgrade;
            proxy_set_header   Host $host;
            proxy_cache_bypass $http_upgrade;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Proto $scheme;
        }
    }
}

有关详细信息,请参阅以下文章:For more information, see the following articles:

Linux 与 ApacheLinux with Apache

若要在 Linux 上托管 Apache 后面的 Blazor 应用,请为 HTTP 和 WebSocket 流量配置 ProxyPassTo host a Blazor app behind Apache on Linux, configure ProxyPass for HTTP and WebSockets traffic.

如下示例中:In the following example:

  • Kestrel 服务器正在主机上运行。Kestrel server is running on the host machine.
  • 应用侦听端口 5000 上的流量。The app listens for traffic on port 5000.
ProxyRequests       On
ProxyPreserveHost   On
ProxyPassMatch      ^/_blazor/(.*) http://localhost:5000/_blazor/$1
ProxyPass           /_blazor ws://localhost:5000/_blazor
ProxyPass           / http://localhost:5000/
ProxyPassReverse    / http://localhost:5000/

启用以下模块:Enable the following modules:

a2enmod   proxy
a2enmod   proxy_wstunnel

检查浏览器控制台中的 WebSocket 错误。Check the browser console for WebSockets errors. 示例错误:Example errors:

  • Firefox 无法通过 ws://the-domain-name.tld/_blazor?id=XXX 与服务器建立连接。Firefox can't establish a connection to the server at ws://the-domain-name.tld/_blazor?id=XXX.
  • 错误:未能启动传输“WebSocket”:错误:传输出现错误。Error: Failed to start the transport 'WebSockets': Error: There was an error with the transport.
  • 错误:未能启动传输“LongPolling”:TypeError:未定义 this.transportError: Failed to start the transport 'LongPolling': TypeError: this.transport is undefined
  • 错误:无法使用任何可用传输连接到服务器。Error: Unable to connect to the server with any of the available transports. WebSocket 失败WebSockets failed
  • 错误:如果连接未处于“已连接”状态,则无法发送数据。Error: Cannot send data if the connection is not in the 'Connected' State.

有关详细信息,请参阅 Apache 文档For more information, see the Apache documentation.

衡量网络延迟Measure network latency

可以使用 JS 互操作来衡量网络延迟,如以下示例所示:JS interop can be used to measure network latency, as the following example demonstrates:

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

为获得合理的 UI 体验,我们建议使用 250 毫秒或更低的持续 UI 延迟。For a reasonable UI experience, we recommend a sustained UI latency of 250ms or less.