Nguon: Microsoft Learn · .NET 8.0

Hosting và scaling SignalR trong môi trường production

Nguồn: ASP.NET Core SignalR production hosting and scaling

Bởi Andrew Stanton-Nurse, Brady Gaster và Tom Dykstra

Bài viết này giải thích các cân nhắc về hosting (lưu trữ) và scaling (mở rộng quy mô) cho các ứng dụng lưu lượng cao sử dụng ASP.NET Core SignalR.

Sticky sessions (Phiên cố định)

SignalR yêu cầu cùng một tiến trình máy chủ xử lý tất cả các yêu cầu HTTP cho một kết nối cụ thể. Khi SignalR chạy trên server farm (nhiều máy chủ), phải sử dụng "sticky sessions" (phiên cố định). "Sticky sessions" còn được gọi là session affinity (sự ái lực phiên). Azure App Service sử dụng Microsoft Application Request Routing (ARR) để định tuyến yêu cầu. Bật cài đặt "Session affinity" (ARR Affinity) trong ứng dụng App Service sẽ bật sticky sessions.

Có ba tình huống không cần sticky sessions cho một ứng dụng:

Trong tất cả các trường hợp khác (bao gồm khi sử dụng Redis backplane), môi trường máy chủ phải được cấu hình cho sticky sessions.

Tài nguyên kết nối TCP

Số lượng kết nối TCP đồng thời mà một máy chủ web có thể hỗ trợ là có giới hạn. HTTP client tiêu chuẩn sử dụng các kết nối tạm thời (ephemeral). Các kết nối này có thể đóng khi client không hoạt động và mở lại sau. Mặt khác, kết nối SignalR là bền vững (persistent). Các kết nối SignalR vẫn mở ngay cả khi client không hoạt động. Trong một ứng dụng lưu lượng cao phục vụ nhiều client, các kết nối bền vững này có thể khiến các máy chủ đạt đến số lượng kết nối tối đa.

Các kết nối bền vững cũng tiêu thụ thêm bộ nhớ để theo dõi mỗi kết nối.

Việc sử dụng tài nguyên liên quan đến kết nối nhiều của SignalR có thể ảnh hưởng đến các ứng dụng web khác được lưu trữ trên cùng máy chủ. Khi SignalR mở và giữ các kết nối TCP cuối cùng còn lại, các ứng dụng web khác trên cùng máy chủ cũng không có thêm kết nối nào.

Nếu máy chủ hết kết nối, bạn sẽ thấy lỗi socket ngẫu nhiên và lỗi đặt lại kết nối. Ví dụ:

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

Để giữ cho việc sử dụng tài nguyên SignalR không gây ra lỗi trong các ứng dụng web khác, hãy chạy SignalR trên các máy chủ khác với các ứng dụng web khác của bạn.

Để giữ cho việc sử dụng tài nguyên SignalR không gây ra lỗi trong ứng dụng SignalR, hãy scale out để giới hạn số lượng kết nối mà một máy chủ phải xử lý.

Scale out (Mở rộng theo chiều ngang)

Một ứng dụng sử dụng SignalR cần theo dõi tất cả các kết nối của mình, điều này tạo ra vấn đề cho server farm. Thêm một máy chủ và nó nhận các kết nối mới mà các máy chủ khác không biết. Ví dụ, SignalR trên mỗi máy chủ trong sơ đồ sau không biết về các kết nối trên các máy chủ khác. Khi SignalR trên một trong các máy chủ muốn gửi tin nhắn đến tất cả client, tin nhắn chỉ đến với các client kết nối đến máy chủ đó.

!Minh họa mở rộng SignalR không có backplane

Các tùy chọn để giải quyết vấn đề này là Azure SignalR Service và Redis backplane.

Azure SignalR Service

Azure SignalR Service hoạt động như một proxy cho lưu lượng real-time và đồng thời là backplane khi ứng dụng được scale out trên nhiều máy chủ. Mỗi khi client khởi tạo kết nối đến máy chủ, client được chuyển hướng để kết nối đến dịch vụ. Sơ đồ sau minh họa quy trình này:

!Minh họa thiết lập kết nối đến Azure SignalR Service

Kết quả là dịch vụ quản lý tất cả các kết nối client, trong khi mỗi máy chủ chỉ cần một số lượng kết nối nhỏ cố định đến dịch vụ, như được hiển thị trong sơ đồ sau:

!Minh họa client và máy chủ kết nối đến dịch vụ

Cách tiếp cận scale out này có một số lợi thế so với Redis backplane:

Vì những lý do này, khuyến nghị sử dụng Azure SignalR Service cho tất cả ứng dụng ASP.NET Core SignalR được lưu trữ trên Azure, bao gồm App Service, máy ảo và container.

Để biết thêm thông tin, xem tài liệu Azure SignalR Service.

Redis backplane

Redis là kho lưu trữ key-value trong bộ nhớ hỗ trợ hệ thống tin nhắn với mô hình publish/subscribe (xuất bản/đăng ký). Redis backplane của SignalR sử dụng tính năng publish/subscribe để chuyển tiếp tin nhắn đến các máy chủ khác. Khi client tạo kết nối, thông tin kết nối được truyền đến backplane. Khi máy chủ muốn gửi tin nhắn đến tất cả client, nó gửi đến backplane. Backplane biết tất cả client kết nối và máy chủ nào họ đang kết nối. Nó gửi tin nhắn đến tất cả client thông qua máy chủ tương ứng. Quy trình này được minh họa trong sơ đồ sau:

!Minh họa Redis backplane với tin nhắn được gửi từ một máy chủ đến tất cả client

Redis backplane là cách tiếp cận scale out được khuyến nghị cho các ứng dụng được lưu trữ trên cơ sở hạ tầng của riêng bạn. Nếu có độ trễ kết nối đáng kể giữa trung tâm dữ liệu của bạn và trung tâm dữ liệu Azure, Azure SignalR Service có thể không phải là lựa chọn thực tế cho các ứng dụng on-premises có yêu cầu độ trễ thấp hoặc thông lượng cao.

Các lợi thế của Azure SignalR Service được mô tả trước đó là bất lợi cho Redis backplane:

Giới hạn IIS trên hệ điều hành client Windows

Windows 10 và Windows 8.x là hệ điều hành client. IIS (Internet Information Services) trên hệ điều hành client có giới hạn 10 kết nối đồng thời. Các kết nối SignalR có các đặc điểm sau:

Những đặc điểm này làm cho việc đạt giới hạn 10 kết nối trên hệ điều hành client rất có thể xảy ra. Khi sử dụng hệ điều hành client để phát triển, hãy xem xét các khuyến nghị sau:

Linux với Nginx

Code sau chứa các cài đặt tối thiểu cần thiết để bật WebSockets, ServerSentEvents và LongPolling cho SignalR:

nginx
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;
      # WebSockets were implemented after http/1.0
      proxy_http_version 1.1;

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

Khi sử dụng nhiều máy chủ backend, phải thêm sticky sessions để ngăn các kết nối SignalR chuyển đổi máy chủ khi kết nối. Có nhiều cách để thêm sticky sessions trong Nginx. Hai ví dụ sau đây dựa trên những gì bạn có sẵn.

Code sau bổ sung cấu hình ví dụ trước. Trong các đoạn code, backend là tên của nhóm máy chủ.

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

ip_hash; } } ```

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

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

Các nhà cung cấp SignalR backplane khác

Các nhà cung cấp không phải Microsoft sau đây cũng cung cấp SignalR backplane: