ASP.NET Core SignalR 托管和缩放ASP.NET Core SignalR hosting and scaling

作者: Andrew StantonBrady GasterTom DykstraBy Andrew Stanton-Nurse, Brady Gaster, and Tom Dykstra

本文介绍使用 ASP.NET Core 的高流量应用的托管和扩展注意事项 SignalR 。This article explains hosting and scaling considerations for high-traffic apps that use ASP.NET Core SignalR.

粘滞会话Sticky Sessions

SignalR 要求针对特定连接的所有 HTTP 请求都由同一服务器进程处理。SignalR requires that all HTTP requests for a specific connection be handled by the same server process. 当 SignalR 在服务器场中运行 (多个服务器) 时,必须使用 "粘滞会话"。When SignalR is running on a server farm (multiple servers), "sticky sessions" must be used. 某些负载均衡器也称为 "粘滞会话"。"Sticky sessions" are also called session affinity by some load balancers. Azure App Service 使用 应用程序请求路由 (ARR) 来路由请求。Azure App Service uses Application Request Routing (ARR) to route requests. 启用 Azure App Service 中的 "ARR 相似性" 设置将启用 "粘滞会话"。Enabling the "ARR Affinity" setting in your Azure App Service will enable "sticky sessions". 不需要手写会话的唯一情况是:The only circumstances in which sticky sessions are not required are:

  1. 在单个服务器上承载时,在单个进程中。When hosting on a single server, in a single process.
  2. 使用 Azure 服务时 SignalR 。When using the Azure SignalR Service.
  3. 当所有客户端都配置为 使用 websocket 时, 并且 在客户端配置中启用了 SkipNegotiation 设置When all clients are configured to only use WebSockets, and the SkipNegotiation setting is enabled in the client configuration.

在所有其他情况下 (包括) 使用 Redis 底板时,必须为粘滞会话配置服务器环境。In all other circumstances (including when the Redis backplane is used), the server environment must be configured for sticky sessions.

有关为配置 Azure App Service 的指南 SignalR ,请参阅 SignalR将 ASP.NET Core 应用程序发布到 Azure App ServiceFor guidance on configuring Azure App Service for SignalR, see SignalR将 ASP.NET Core 应用程序发布到 Azure App Service.

TCP 连接资源TCP connection resources

Web 服务器可以支持的并发 TCP 连接数受到限制。The number of concurrent TCP connections that a web server can support is limited. 标准 HTTP 客户端使用 临时 连接。Standard HTTP clients use ephemeral connections. 当客户端进入空闲状态并在稍后重新打开时,可以关闭这些连接。These connections can be closed when the client goes idle and reopened later. 另一方面, SignalR 连接是 永久性 的。On the other hand, a SignalR connection is persistent . SignalR 即使客户端进入空闲状态,连接仍保持打开状态。SignalR connections stay open even when the client goes idle. 在服务于多个客户端的高流量应用程序中,这些持久连接可能会导致服务器达到其最大连接数。In a high-traffic app that serves many clients, these persistent connections can cause servers to hit their maximum number of connections.

持久性连接还会占用一些额外的内存,用于跟踪每个连接。Persistent connections also consume some additional memory, to track each connection.

与连接相关的资源的大量使用 SignalR 会影响托管在同一服务器上的其他 web 应用程序。The heavy use of connection-related resources by SignalR can affect other web apps that are hosted on the same server. 当 SignalR 打开并保存最近可用的 TCP 连接时,同一服务器上的其他 web 应用也不会有更多的可用连接。When SignalR opens and holds the last available TCP connections, other web apps on the same server also have no more connections available to them.

如果服务器的连接用尽,你会看到随机套接字错误和连接重置错误。If a server runs out of connections, you'll see random socket errors and connection reset errors. 例如: 。For example:

An attempt was made to access a socket in a way forbidden by its access permissions...

若要防止 SignalR 资源使用导致其他 web 应用中出现错误,请 SignalR 在不同于其他 web 应用的服务器上运行。To keep SignalR resource usage from causing errors in other web apps, run SignalR on different servers than your other web apps.

为了避免 SignalR 资源使用导致应用中出现错误 SignalR ,横向扩展以限制服务器必须处理的连接数。To keep SignalR resource usage from causing errors in a SignalR app, scale out to limit the number of connections a server has to handle.

向外扩展Scale out

使用的应用程序 SignalR 需要跟踪其所有连接,这会为服务器场带来问题。An app that uses SignalR needs to keep track of all its connections, which creates problems for a server farm. 添加服务器,并获取其他服务器不知道的新连接。Add a server, and it gets new connections that the other servers don't know about. 例如, SignalR 在下图中的每个服务器上都不知道其他服务器上的连接。For example, SignalR on each server in the following diagram is unaware of the connections on the other servers. 当 SignalR 某个服务器要向所有客户端发送消息时,该消息只会发送到连接到该服务器的客户端。When SignalR on one of the servers wants to send a message to all clients, the message only goes to the clients connected to that server.

缩放:::非 loc (SignalR) :::无底板

解决此问题的方法是 Azure SignalR 服务Redis 底板The options for solving this problem are the Azure SignalR Service and Redis backplane.

Azure SignalR 服务Azure SignalR Service

Azure SignalR 服务是一种代理,而不是底板。The Azure SignalR Service is a proxy rather than a backplane. 每次客户端启动与服务器的连接时,客户端都将被重定向以连接到服务。Each time a client initiates a connection to the server, the client is redirected to connect to the service. 下图说明了该过程:That process is illustrated in the following diagram:

建立与 Azure::: no (SignalR) ::: Service 的连接

因此,服务管理所有客户端连接,而每个服务器只需要与服务建立少量的固定连接,如下图所示:The result is that the service manages all of the client connections, while each server needs only a small constant number of connections to the service, as shown in the following diagram:

连接到服务的客户端,连接到服务的服务器

与 Redis 底板替代方法相比,这种扩展方法具有多个优点:This approach to scale-out has several advantages over the Redis backplane alternative:

  • 粘滞会话(也称为 客户端关联)不是必需的,因为客户端在连接时立即重定向到 Azure SignalR 服务。Sticky sessions, also known as client affinity, is not required, because clients are immediately redirected to the Azure SignalR Service when they connect.
  • SignalR应用程序可以根据发送的消息数进行扩展,而 Azure 服务可以缩放 SignalR 以处理任意数量的连接。A SignalR app can scale out based on the number of messages sent, while the Azure SignalR Service scales to handle any number of connections. 例如,可能有数千个客户端,但如果每秒只发送了几条消息,则该 SignalR 应用无需向外扩展到多个服务器即可直接处理连接。For example, there could be thousands of clients, but if only a few messages per second are sent, the SignalR app won't need to scale out to multiple servers just to handle the connections themselves.
  • SignalR与 web 应用相比,应用不会使用更多的连接资源 SignalR 。A SignalR app won't use significantly more connection resources than a web app without SignalR.

出于此原因,我们建议 azure SignalR 服务适用于 SignalR azure 上托管的所有 ASP.NET Core 应用,包括应用服务、vm 和容器。For these reasons, we recommend the Azure SignalR Service for all ASP.NET Core SignalR apps hosted on Azure, including App Service, VMs, and containers.

有关详细信息,请参阅 Azure SignalR 服务文档For more information see the Azure SignalR Service documentation.

Redis 底板Redis backplane

Redis 是内存中的键-值存储,它支持具有发布/订阅模型的消息传送系统。Redis is an in-memory key-value store that supports a messaging system with a publish/subscribe model. SignalRRedis 底板使用 pub/sub 功能将消息转发到其他服务器。The SignalR Redis backplane uses the pub/sub feature to forward messages to other servers. 当客户端建立连接时,会将连接信息传递到底板。When a client makes a connection, the connection information is passed to the backplane. 当服务器要向所有客户端发送消息时,它会发送到底板。When a server wants to send a message to all clients, it sends to the backplane. 底板知道所有连接的客户端和它们所在的服务器。The backplane knows all connected clients and which servers they're on. 它通过各自的服务器将消息发送到所有客户端。It sends the message to all clients via their respective servers. 下图演示了此过程:This process is illustrated in the following diagram:

Redis 底板,从一台服务器发送到所有客户端的消息

对于托管在你自己的基础结构上的应用,建议使用 Redis 底板。The Redis backplane is the recommended scale-out approach for apps hosted on your own infrastructure. 如果在数据中心与 Azure 数据中心之间存在明显的连接延迟,则 SignalR 对于具有低延迟或高吞吐量要求的本地应用,Azure 服务可能不是可行的选择。If there is significant connection latency between your data center and an Azure data center, Azure SignalR Service may not be a practical option for on-premises apps with low latency or high throughput requirements.

前面所 SignalR 述的 Azure 服务优点是 Redis 底板的缺点:The Azure SignalR Service advantages noted earlier are disadvantages for the Redis backplane:

  • 需要将粘滞会话(也称为 客户端关联)用于以下 种情况:Sticky sessions, also known as client affinity, is required, except when both of the following are true:
    • 所有客户端都配置为 使用 websocket。All clients are configured to only use WebSockets.
    • 在客户端配置中启用了 SkipNegotiation 设置The SkipNegotiation setting is enabled in the client configuration. 在服务器上启动连接后,连接必须停留在该服务器上。Once a connection is initiated on a server, the connection has to stay on that server.
  • SignalR即使发送的消息太少,应用程序也必须基于客户端数进行扩展。A SignalR app must scale out based on number of clients even if few messages are being sent.
  • SignalR应用比 web 应用使用的连接资源明显更多 SignalR 。A SignalR app uses significantly more connection resources than a web app without SignalR.

Windows 客户端操作系统上的 IIS 限制IIS limitations on Windows client OS

Windows 10 和 Windows 8.x 是客户端操作系统。Windows 10 and Windows 8.x are client operating systems. 客户端操作系统上的 IIS 的并发连接数限制为10个。IIS on client operating systems has a limit of 10 concurrent connections. SignalR的连接是:SignalR's connections are:

  • 暂时性并经常重新建立。Transient and frequently re-established.
  • 不再使用时 不会 立即释放。Not disposed immediately when no longer used.

上述情况可能导致在客户端操作系统上达到10个连接限制。The preceding conditions make it likely to hit the 10 connection limit on a client OS. 当客户端操作系统用于开发时,建议:When a client OS is used for development, we recommend:

  • 避免 IIS。Avoid IIS.
  • 使用 Kestrel 或 IIS Express 作为部署目标。Use Kestrel or IIS Express as deployment targets.

Linux 与 NginxLinux with Nginx

以下内容包含为启用 Websocket、ServerSentEvents 和 LongPolling 所需的最低设置 SignalR :The following contains the minimum required settings to enable WebSockets, ServerSentEvents, and LongPolling for SignalR:

http {
  map $http_connection $connection_upgrade {
    "~*Upgrade" $http_connection;
    default keep-alive;
}

  server {
    listen 80;
    server_name example.com *.example.com;

    # Configure the SignalR Endpoint
    location /hubroute {
      # App server url
      proxy_pass http://localhost:5000;

      # Configuration for WebSockets
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection $connection_upgrade;
      proxy_cache off;

      # Configuration for ServerSentEvents
      proxy_buffering off;

      # Configuration for LongPolling or if your KeepAliveInterval is longer than 60 seconds
      proxy_read_timeout 100s;

      proxy_set_header Host $host;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
    }
  }
}

当使用多个后端服务器时,必须添加粘滞会话,以防 SignalR 连接时无法切换服务器。When multiple backend servers are used, sticky sessions must be added to prevent SignalR connections from switching servers when connecting. 有多种方法可在 Nginx 中添加粘滞会话。There are multiple ways to add sticky sessions in Nginx. 下面显示了两种方法,具体取决于你提供的功能。Two approaches are shown below depending on what you have available.

除了前面的配置外,还添加了以下。The following is added in addition to the previous configuration. 在以下示例中, backend 是服务器组的名称。In the following examples, backend is the name of the group of servers.

使用 Nginx 开放源代码,使用将 ip_hash 连接路由到基于客户端 IP 地址的服务器:With Nginx Open Source, use ip_hash to route connections to a server based on the client's IP address:

http {
  upstream backend {
    # App server 1
    server http://localhost:5000;
    # App server 2
    server http://localhost:5002;

    ip_hash;
  }
}

使用 Nginx Plus,使用 sticky 将添加 cookie 到请求,并将用户的请求固定到服务器:With Nginx Plus, use sticky to add a cookie to requests and pin the user's requests to a server:

http {
  upstream backend {
    # App server 1
    server http://localhost:5000;
    # App server 2
    server http://localhost:5002;

    sticky cookie srv_id expires=max domain=.example.com path=/ httponly;
  }
}

最后,将 proxy_pass http://localhost:5000 节中的更改 serverproxy_pass http://backendFinally, change proxy_pass http://localhost:5000 in the server section to proxy_pass http://backend.

有关 Nginx 上的 Websocket 的详细信息,请参阅 Nginx 作为 WebSocket 代理For more information on WebSockets over Nginx, see NGINX as a WebSocket Proxy.

有关负载平衡和粘滞会话的详细信息,请参阅 NGINX 负载均衡For more information on load balancing and sticky sessions, see NGINX load balancing.

有关 Nginx ASP.NET Core 的详细信息,请参阅以下文章:For more information about ASP.NET Core with Nginx see the following article:

第三方 SignalR 底板提供程序Third-party SignalR backplane providers

后续步骤Next steps

有关详细信息,请参阅以下资源:For more information, see the following resources: