當一個Web應用程式達到一台伺服器能力限制,即請求處理數量限制之後,有2種解決方案:縱向擴充和橫向擴充。
- 縱向擴充即用更強的伺服器(或虛拟機),或為目前的伺服器添加更多的記憶體,CPU等
- 橫向擴充即添加多台伺服器或者虛拟機來做負載均衡
縱向擴充的問題是,伺服器的配置更新的費用比較高,而且總有更新的極限,很容易就再次達到限制。而橫向擴充就沒有這個限制,不同的使用者可以被分流到不同的伺服器,進而解決負載問題

但是這樣的橫向解決方案,會有一個問題,即使用者被分流到不同SignalR伺服器之後, 不同伺服器上的使用者就沒有辦法同步消息。
以一個聊天室程式為例,可能使用者A,使用者B被分流到伺服器A, 使用者C被分流到伺服器B, 當使用者A發送普通廣播消息, 使用者B因為和使用者A分流到同一台伺服器,是以能收到這條消息,使用者C因為不在伺服器A上,是以就沒有辦法收到這條消息。
底闆(backplane)
Signal引入了一個底闆的概念來解決不同伺服器之間同步的問題。如果SignalR啟用底闆功能,每個應用執行個體發送消息的消息都會先傳輸到底闆,底闆會向所有連接配接的伺服器發送同步消息,每個SignalR執行個體會把接收到的消息儲存在記憶體中,然後同步給連接配接該執行個體的用戶端。這樣就解決了消息不同步的問題。
但是正因為有了底闆機制,與單伺服器SignalR應用相比,端到端通信和高并發應用的速度會減慢,同時發送的消息數量會減少,因為所有的用戶端消息都是高頻率的發送到SignalR伺服器,SignalR伺服器發送該消息到底闆,底闆再和所有的伺服器同步消息,最在才通知到所有的連接配接用戶端,是以對于這2種應用場景最适合還是單伺服器SignalR應用。
底闆最适合的是伺服器廣播,股市報價或者比賽文字直播就是最好的例子,因為所有的用戶端都是被動接受消息,伺服器端可以控制同時發送消息的數量。
SignalR預設支援的三種底闆
- Azure Service Bus
- Redis
- SQL Server
SignalR使用SQL Server做橫向擴充
底闆功能需要SQL Server 2005或以上版本的支援(精簡版除外)。
配置資料庫
- 打開SQL Server管理器,建立新的資料庫SignalR
- 使用一下指令檢查資料庫是否支援Service Broker
SELECT [name], [service_broker_guid], [is_broker_enabled]
FROM [master].[sys].[databases]
- 如果SignalR的is_broker_enabled标志是false, 請輸入以下指令啟用Service Broker
ALTER DATABASE YOUR_DATABASE SET ENABLE_BROKER
Service Broker
Service Broker為SQL Server提供的原生的消息隊列功能,啟用Service Broker可以使底闆更有效率的更新同步消息。但是不啟用Service Broker, 底闆依然可以運作。
建立項目
參照學習筆記(三)的代碼,建立以下解決方案
ScaleoutSqlServer, ScaleoutSqlServer2與之前學習筆記(三)SignalRSelfHost代碼完全相同
ChatRoom和ChatRoom2與與之前學習筆記(三)ChatRoom的代碼完全相同
引入SQL Server底闆支援
打開Package Manage Console面闆,輸入以下指令, 分别對
Install-Package Microsoft.AspNet.SignalR.SqlServer
啟用SQL Server底闆
打開ScaleoutSqlServer和ScaleoutSqlServer2工程中的Program.cs
ScaleoutSqlServer
class Program
{
static void Main(string[] args)
{
using (WebApp.Start<Startup>("http://localhost:9021"))
{
Console.WriteLine("Server started.");
Console.Read();
}
}
}
class Startup
{
public void Configuration(IAppBuilder app)
{
// Any connection or hub wire up and configuration should go here
string sqlConnectionString = "Server=.;Initial Catalog=SignalR; Integrated Security=true";
GlobalHost.DependencyResolver.UseSqlServer(sqlConnectionString);
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR();
}
}
ScaleoutSqlServer2
class Program
{
static void Main(string[] args)
{
using (WebApp.Start<Startup>("http://localhost:9032"))
{
Console.WriteLine("Server started.");
Console.Read();
}
}
}
class Startup
{
public void Configuration(IAppBuilder app)
{
// Any connection or hub wire up and configuration should go here
string sqlConnectionString = "Server=.;Initial Catalog=SignalR; Integrated Security=true";
GlobalHost.DependencyResolver.UseSqlServer(sqlConnectionString);
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR();
}
}
前台頁面連結不同的SignalR伺服器
修改ChatRoom, ChatRoom2中的ChatRoom.html
ChatRoom
<script src="Scripts/jquery.signalR-2.2.2.min.js"></script>
<!--<script src="signalr/hubs"></script>-->
<script src="http://localhost:9021/signalr/hubs"></script>
<script type="text/javascript">
$(function () {
$.connection.hub.url = 'http://localhost:9021/signalr';
ChatRoom2
<script src="Scripts/jquery.signalR-2.2.2.min.js"></script>
<!--<script src="signalr/hubs"></script>-->
<script src="http://localhost:9032/signalr/hubs"></script>
<script type="text/javascript">
$(function () {
$.connection.hub.url = 'http://localhost:9032/signalr';
最終效果
分别啟動2個SignalR伺服器, 并分别打開2個ChatRoom.html頁面。
雖然2個Web應用程式通路的是不同的SignalR伺服器,但是他們之間的消息同步了。