問題背景:當SignalR應用部署在一臺服務器的時候,隨着用戶量的增加,服務器的承載能力肯定會達到瓶頸,這時候就需要考慮多部署幾臺服務器來緩解壓力
解決辦法:多臺服務器部署SignalR應用,一臺服務器部署Redis服務
如上圖,一臺SignalR服務器對應一個用戶羣,SignalR應用服務器和Redis服務器之間採用發佈-訂閱模式。當用戶羣1的用戶向用戶羣2的用戶發送消息的時候,消息先到達SignalR應用服務器1,SignalR1服務器向Redis服務器發佈消息,其他服務器訂閱了Redis服務器的消息,會接受到SignalR服務器1的消息,SignalR服務器2發現是自己用戶消息,因此接受消息並將消息發送給用戶羣2裏面的用戶
實現:
在vs2017新建一個web應用,在nuget上安裝Microsoft.AspNetCore.SignalR.Redis 和Microsoft.AspNetCore.SignalR.StackExchangeRedis兩個應用組件
在Start.cs代碼文件裏面新增SignalR服務並配置Redis服務
services.AddSignalR()
.AddRedis("127.0.0.1:6379", opitons =>
{
opitons.Configuration.ChannelPrefix = "myapp2"; //同一個SignalR應用配置相同的Redis頻道前綴(應爲要訂閱相同頻道的Redis消息)
})
;
編寫Hub
public class ChatHubs : Hub
{
public override Task OnConnectedAsync()
{
Clients.Client(this.Context.ConnectionId).SendAsync("ReceiveConnectionID", this.Context.ConnectionId);
return base.OnConnectedAsync();
}
public override Task OnDisconnectedAsync(Exception exception)
{
return base.OnDisconnectedAsync(exception);
}
public async Task SendMessage(string connectionID,string user, string message) //給某一個人發送消息
{
//await Clients.All.SendAsync("ReceiveMessage", user, message);
await Clients.Client(connectionID).SendAsync("ReceiveMessage", user, message);
}
}
發佈應用到兩個文件夾,使用dotnet命令分別運行兩個應用將網站跑起來,兩個網站分別綁定到不同的端口,比如我這裏分別綁定到5001和5002兩個端口
在5001這個端口下面的應用的ConnectionID填寫5002端口下面的那個ConnectionID值,這樣就可以實現5001端口應用向5002端口應用發送消息
這樣只要知道需要接受消息的那個人的ConnectionID,就可以向那個人發送消息,即便這收發消息的兩個人不是連在同一個服務器上也可以進行通信
這裏只是講講自己對SignrR+Redis方面的擴展的理解,真正的擴展還需要考慮用戶和SignalR服務器的之間轉發問題,因爲用戶和SignalR服務器之間是通過ConnectionID關聯的,所以當有多個服務器的時候,就需要考慮怎麼保持ConnectionID和服務器的對應關係,這裏可以使用Nginx實現用戶分發問題,利用Nginx的ip綁定策略實現ip固定訪問服務器的功能。
這裏附上通過ip綁定策略配置Nginx負載配置
修改Nginx配置文件nginx.conf
upstream myweb{
ip_hash;
server localhost:5001;
server localhost:5002;
keepalive 1000;
}
server {
listen 81;
server_name localhost;
location / {
#root html;
index index.html index.htm;
proxy_pass http://myweb;
#Nginx配置websocket
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
這樣在瀏覽器訪問 http://localhost:81地址,同一個ip用戶請求將只會被轉發到一臺固定的服務器