动手实验:使用 SignalR 实现实时 Web 应用程序
警告
本文档不适用于最新版本的 SignalR。 查看 ASP.NET Core SignalR。
下载 Web Camp 培训工具包,2015 年 10 月版本
实时 Web 应用程序能够实时将服务器端内容推送到连接的客户端。 对于 ASP.NET 开发人员, ASP.NET SignalR 是一个库,用于向其应用程序添加实时 Web 功能。 它利用多种传输,根据客户端和服务器的最佳可用传输,自动选择最佳可用传输。 它利用 WebSocket,这是一种 HTML5 API,可实现浏览器和服务器之间的双向通信。
SignalR 还提供了一个简单的高级 API,用于执行服务器到客户端 RPC, (客户端浏览器中从 ASP.NET 应用程序中的服务器端 .NET 代码) 调用 JavaScript 函数,并为连接管理添加有用的挂钩,例如连接/断开连接事件、分组连接和授权。
SignalR 是客户端和服务器之间执行实时工作所需的某些传输的抽象。 SignalR 连接以 HTTP 开头,然后升级为 WebSocket 连接(如果可用)。 WebSocket 是 SignalR 的理想传输,因为它最有效地利用服务器内存,延迟最低, (具有最基础的功能,例如客户端和服务器) 之间的全双工通信,但它也有最严格的要求:WebSocket 要求服务器使用 Windows Server 2012 或 Windows 8, 以及 .NET Framework 4.5。 如果未满足这些要求, SignalR 将尝试使用其他传输将其连接 (,例如 Ajax 长轮询) 。
SignalR API 包含用于在客户端和服务器之间进行通信的两个模型:持久连接和中心。 连接表示用于发送单一收件人、分组或广播消息的简单终结点。 中心是基于连接 API 构建的更高级别管道,它允许客户端和服务器直接相互调用方法。
所有示例代码和代码片段都包含在 2015 年 10 月发布的 Web Camp 培训工具包中,网址为 https://github.com/Microsoft-Web/WebCampTrainingKit/releases/tag/v2015.10.13b。 请注意,该页面上的安装程序链接不再有效;请改用“资产”部分下的链接之一。
概述
目标
在本动手实验中,您将了解如何:
- 使用 SignalR 将通知从服务器发送到客户端。
- 使用 SQL Server Scale Out SignalR 应用程序。
先决条件
完成本动手实验需要以下各项:
设置
若要在此动手实验室中运行练习,需要先设置环境。
- 打开 Windows 资源管理器窗口并浏览到实验室的 “源 ”文件夹。
- 右键单击“ 安装程序.cmd ”,然后选择“ 以管理员身份运行” ,启动将配置环境的安装过程,并安装用于此实验室的 Visual Studio 代码片段。
- 如果显示“用户帐户控制”对话框,请确认操作以继续。
注意
确保在运行安装过程之前,您已检查本实验的所有依赖项。
使用代码段
在整个实验文档中,将指示您插入代码块。 为方便起见,此代码的大部分作为Visual Studio Code代码段提供,你可以从Visual Studio 2013内部访问这些代码片段,以避免手动添加代码段。
注意
每个练习都附带一个起始解决方案,该解决方案位于练习的 Begin 文件夹中,使你能够独立于其他练习跟踪每个练习。 请注意,在练习期间添加的代码片段在这些起始解决方案中缺失,在完成练习之前可能无法正常工作。 在练习的源代码中,还可以找到一个 End 文件夹,其中包含 Visual Studio 解决方案,其中包含完成相应练习中的步骤后产生的代码。 如果您在演练本动手实验时需要其他帮助,则可以使用这些解决方案作为指南。
练习
本动手实验包括以下练习:
完成本实验室的估计时间: 60 分钟
注意
首次启动 Visual Studio 时,必须选择一个预定义的设置集合。 每个预定义集合都设计为匹配特定的开发样式,并确定窗口布局、编辑器行为、IntelliSense 代码片段和对话框选项。 本实验室中的过程描述了在使用 “常规开发设置” 集合时,在 Visual Studio 中完成给定任务所需的操作。 如果为开发环境选择不同的设置集合,则应考虑的步骤可能存在差异。
练习 1:使用 SignalR 处理Real-Time数据
虽然聊天通常用作示例,但你可以通过实时 Web 功能执行更多操作。 每当用户刷新网页以查看新数据或页面实现 Ajax 长时间轮询以检索新数据时,可以使用 SignalR。
SignalR 支持 服务器推送 或 广播 功能;它会自动处理连接管理。 在用于客户端-服务器通信的经典 HTTP 连接中,会为每个请求重新建立连接,但 SignalR 在客户端和服务器之间提供持久连接。 在 SignalR 中,服务器代码使用远程过程调用在浏览器中调用客户端代码, (RPC) ,而不是我们今天知道的请求-响应模型。
在本练习中,你将配置 Geek Quiz 应用程序以使用 SignalR 显示统计信息仪表板更新的指标,而无需刷新整个页面。
任务 1 - 浏览极客测验统计信息页
在此任务中,你将遍历应用程序并验证统计信息页的显示方式,以及如何改进信息的更新方式。
打开 Visual Studio Express 2013 for Web,并打开 Source\Ex1-WorkingWithRealTimeData\Begin 文件夹中的 GeekQuiz.sln 解决方案。
按“F5”运行该解决方案。 浏览器中应显示 “登录 ”页。
运行解决方案
单击页面右上角的“ 注册 ”,在应用程序中创建新用户。
注册链接
在 “注册 ”页中,输入 “用户名 ”和“ 密码”,然后单击“ 注册”。
注册用户
应用程序注册新帐户,用户进行身份验证并重定向回显示第一个测验问题的主页。
在新窗口中打开 “统计信息 ”页,将 “主页 ”和“ 统计信息 ”页并排放置。
并排窗口
在“ 主页 ”中,单击其中一个选项来回答问题。
回答问题
单击其中一个按钮后,应会显示答案。
问题已正确解答
请注意,“统计信息”页中提供的信息已过时。 刷新页面以查看更新的结果。
“统计信息”页
返回 Visual Studio 并停止调试。
任务 2 - 向极客测验添加 SignalR 以显示联机图表
在此任务中,你将将 SignalR 添加到解决方案,并在向服务器发送新答案时自动向客户端发送更新。
在 Visual Studio 的“ 工具 ”菜单中,选择“ NuGet 包管理器”,然后单击“ 包管理器控制台”。
在 “包管理器控制台” 窗口中,执行以下命令:
Install-Package Microsoft.AspNet.SignalR
SignalR 包安装
注意
从全新的 MVC 5 应用程序安装 SignalR NuGet 包版本 2.0.2 时,在安装 SignalR 之前,需要手动将 OWIN 包更新到版本 2.0.1 (或更高版本) 。 为此,可以在 包管理器控制台中执行以下脚本:
get-package | where-object { $_.Id -like "Microsoft.Owin*"} | Update-Package
在 SignalR 的未来版本中,OWIN 依赖项将自动更新。
在解决方案资源管理器中,展开 Scripts 文件夹,注意 SignalR js 文件已添加到解决方案中。
SignalR JavaScript 参考
在“解决方案资源管理器”中,右键单击“GeekQuiz”项目,选择“添加新 | 文件夹”,并将其命名为“中心”。
右键单击 “中心 ”文件夹,然后选择“ 添加|新建项。
添加新项
在 “添加新项 ”对话框中,选择 Visual C# |Web | 在左窗格中的 SignalR 节点中,从中心窗格中选择“ SignalR 中心类 (v2) ”,将文件命名为 StatisticsHub.cs ,然后单击“ 添加”。
“添加新项”对话框
将 StatisticsHub 类中的代码替换为以下代码。
(代码片段 - RealTimeSignalR - Ex1 - StatisticsHubClass)
namespace GeekQuiz.Hubs { using Microsoft.AspNet.SignalR; public class StatisticsHub : Hub { } }
打开 Startup.cs 并在 Configuration 方法的末尾添加以下行。
(代码片段 - RealTimeSignalr - Ex1 - MapSignalr)
public void Configuration(IAppBuilder app) { this.ConfigureAuth(app); app.MapSignalR(); }
打开 Services 文件夹内的 StatisticsService.cs 页,并添加以下 using 指令。
(代码片段 - RealTimeSignalR - Ex1 - UsingDirectives)
using Microsoft.AspNet.SignalR; using GeekQuiz.Hubs;
若要通知连接的客户端更新,请先检索当前连接的 Context 对象。 Hub 对象包含将消息发送到单个客户端或广播到所有连接的客户端的方法。 将以下方法添加到 StatisticsService 类,以广播统计信息数据。
(代码片段 - RealTimeSignalR - Ex1 - NotifyUpdatesMethod)
public async Task NotifyUpdates() { var hubContext = GlobalHost.ConnectionManager.GetHubContext<StatisticsHub>(); if (hubContext != null) { var stats = await this.GenerateStatistics(); hubContext.Clients.All.updateStatistics(stats); } }
注意
在上面的代码中,使用任意方法名称在客户端 (调用函数,即 updateStatistics) 。 指定的方法名称被解释为动态对象,这意味着没有 IntelliSense 或编译时验证。 表达式在运行时计算。 执行方法调用时,SignalR 会将方法名称和参数值发送到客户端。 如果客户端具有与名称匹配的方法,则调用该方法并将参数值传递给它。 如果在客户端上找不到匹配的方法,则不会引发错误。 有关详细信息,请参阅 ASP.NET SignalR 中心 API 指南。
打开 Controllers 文件夹内的 TriviaController.cs 页,并添加以下 using 指令。
using GeekQuiz.Services;
将以下突出显示的代码添加到 Post 操作方法。
(代码片段 - RealTimeSignalR - Ex1 - NotifyUpdatesCall)
public async Task<IHttpActionResult> Post(TriviaAnswer answer) { if (!ModelState.IsValid) { return this.BadRequest(this.ModelState); } answer.UserId = User.Identity.Name; var isCorrect = await this.StoreAsync(answer); var statisticsService = new StatisticsService(this.db); await statisticsService.NotifyUpdates(); return this.Ok<bool>(isCorrect); }
在视图内打开 Statistics.cshtml 页 |主 文件夹。 找到 “脚本” 部分,并在该部分的开头添加以下脚本引用。
(代码片段 - RealTimeSignalR - Ex1 - SignalRScriptReferences)
@section Scripts { @Scripts.Render("~/Scripts/jquery.signalR-2.0.2.min.js"); @Scripts.Render("~/signalr/hubs"); ... }
注意
将 SignalR 和其他脚本库添加到 Visual Studio 项目时,包管理器可能会安装比本主题中显示的版本更新的 SignalR 脚本文件版本。 确保代码中的脚本引用与项目中安装的脚本库的版本匹配。
添加以下突出显示的代码,以将客户端连接到 SignalR 中心,并在从中心收到新消息时更新统计信息。
(代码片段 - RealTimeSignalR - Ex1 - SignalRClientCode)
@section Scripts { ... <script> ... var connection = $.hubConnection(); var hub = connection.createHubProxy("StatisticsHub"); hub.on("updateStatistics", function (statistics) { statisticsData = statistics; $("#correctAnswersCounter").text(statistics.CorrectAnswers); $("#incorrectAnswersCounter").text(statistics.IncorrectAnswers); showCharts(statisticsData); }); connection.start(); </script> }
在此代码中,你将创建中心代理并注册事件处理程序以侦听服务器发送的消息。 在这种情况下,将侦听通过 updateStatistics 方法发送的消息。
任务 3 - 运行解决方案
在此任务中,你将运行解决方案,以验证在回答新问题后是否使用 SignalR 自动更新统计信息视图。
按“F5”运行该解决方案。
注意
如果尚未登录到应用程序,请使用在任务 1 中创建的用户登录。
在新窗口中打开 “统计信息 ”页,将“ 主页 ”和“ 统计信息 ”页并排放置,就像在任务 1 中所做的那样。
在 主页 中,单击其中一个选项来回答问题。
回答另一个问题
单击其中一个按钮后,应会显示答案。 请注意,使用更新的信息回答问题后,页面上的“统计信息”信息会自动更新,无需刷新整个页面。
答案后刷新的“统计信息”页
练习 2:使用SQL Server横向扩展
缩放 Web 应用程序时,通常可以在 纵向扩展 和 横向扩展 选项之间进行选择。 纵向扩展 意味着使用更大的服务器, (CPU、RAM 等资源) 而 横向扩展 意味着添加更多服务器来处理负载。 后者的问题是,客户端可以路由到不同的服务器。 连接到一个服务器的客户端不会接收从另一个服务器发送的消息。
可以使用名为 “底板”的组件在服务器之间转发消息来解决这些问题。 启用底板后,每个应用程序实例都会向底板发送消息,而底板会将消息转发到其他应用程序实例。
目前,SignalR 有三种类型的底板:
- Windows Azure 服务总线。 服务总线是一种消息传送基础结构,允许组件发送松散耦合的消息。
- SQL Server。 SQL Server底板将消息写入 SQL 表。 底板使用 Service Broker 进行高效的消息传送。 但是,如果未启用 Service Broker,它也有效。
- Redis。 Redis 是内存中键值存储。 Redis 支持用于发送消息的发布/订阅 (“pub/sub”) 模式。
每条消息都通过消息总线发送。 消息总线实现 IMessageBus 接口,该接口提供发布/订阅抽象。 底板的工作原理是将默认 的 IMessageBus 替换为为该底板设计的总线。
每个服务器实例通过总线连接到底板。 发送消息时,消息将转到底板,底板将消息发送到每个服务器。 当服务器从底板收到消息时,它会将消息存储在其本地缓存中。 然后,服务器将消息从其本地缓存传递到客户端。
有关 SignalR 底板工作原理的详细信息,请阅读 此文。
注意
在某些情况下,底板可能会成为瓶颈。 下面是一些典型的 SignalR 方案:
在本练习中,你将使用 SQL Server 在 Geek Quiz 应用程序中分发消息。 你将在单个测试计算机上运行这些任务,以了解如何设置配置,但为了获得完整效果,需要将 SignalR 应用程序部署到两台或更多台服务器。 还必须在其中一台服务器上或单独的专用服务器上安装SQL Server。
任务 1 - 了解方案
在此任务中,你将在本地计算机上运行 2 个 Geek 测验 实例,模拟多个 IIS 实例。 在此方案中,在回答一个应用程序中的琐碎问题时,不会在第二个实例的统计信息页上通知更新。 此模拟类似于在多个实例上部署应用程序并使用负载均衡器与其通信的环境。
打开 Source/Ex2-ScalingOutWithSQLServer/Begin 文件夹中的 Begin.sln 解决方案。 加载后,你会在 服务器资源管理器 上注意到,解决方案具有两个具有相同结构但名称不同的项目。 这将模拟在本地计算机上运行同一应用程序的两个实例。
开始模拟 2 个 Geek 测验实例的解决方案
右键单击解决方案节点并选择“ 属性”,打开解决方案的属性页。 在 “启动项目”下,选择“ 多个启动项目 ”,并将两个项目的 “操作” 值更改为 “启动”。
启动多个项目
按“F5”运行该解决方案。 应用程序将在不同端口中启动 Geek Quiz 的两个实例,模拟同一应用程序的多个实例。 将其中一个浏览器固定在左侧,另一个固定在屏幕右侧。 使用凭据登录或注册新用户。 登录后,将“琐事”页保留在左侧,然后转到右侧浏览器中的 “统计信息 ”页。
并排的极客测验
不同端口中的 Geek 测验
开始在左侧浏览器中回答问题,你会注意到右侧浏览器中的 “统计信息 ”页未更新。 这是因为 SignalR 使用本地缓存在其客户端之间分发消息,并且此方案正在模拟多个实例,因此缓存不会在它们之间共享。 可以通过测试相同的步骤(但使用单个应用)来验证 SignalR 是否正常工作。 在以下任务中,你将配置一个底板,以跨实例复制消息。
返回 Visual Studio 并停止调试。
任务 2 - 创建SQL Server底板
在此任务中,你将创建一个数据库,该数据库将用作 Geek Quiz 应用程序的底板。 你将使用 SQL Server 对象资源管理器 浏览服务器并初始化数据库。 此外,还将启用 Service Broker。
在 Visual Studio 中,打开菜单“视图”,然后选择“SQL Server 对象资源管理器”。
右键单击SQL Server节点并选择“添加SQL Server...”选项,连接到 LocalDB 实例。
将 SQL Server 实例添加到 SQL Server 对象资源管理器
将 服务器名称 设置为 (localdb) \v11.0 ,并将 Windows 身份验证 保留为身份验证模式。 单击“ 连接 ”以继续。
连接到 LocalDB
连接到 LocalDB 实例后,需要创建一个表示 SignalR SQL Server底板的数据库。 为此,请右键单击“ 数据库” 节点,然后选择“ 添加新数据库”。
添加新数据库
将数据库名称设置为 SignalR ,然后单击“ 确定” 创建它。
创建 SignalR 数据库
注意
可以为数据库选择任何名称。
若要更有效地从底板接收更新,建议为数据库启用 Service Broker。 Service Broker 为 SQL Server 中的消息传送和队列提供本机支持。 底板也可以在没有 Service Broker 的情况下工作。 右键单击数据库并选择“新建查询”,打开新 查询。
打开新查询
若要检查是否启用了 Service Broker,请在 sys.databases 目录视图中查询 is_broker_enabled 列。 在最近打开的查询窗口中执行以下脚本。
SELECT [name], [service_broker_guid], [is_broker_enabled] FROM [master].[sys].[databases]
查询 Service Broker 状态
如果数据库中 is_broker_enabled 列的值为“0”,请使用以下命令启用它。 将 YOUR-DATABASE> 替换为<在创建数据库 (时设置的名称,例如:SignalR) 。
ALTER DATABASE <YOUR-DATABASE> SET ENABLE_BROKER
启用 Service Broker
注意
如果此查询显示为死锁,请确保没有应用程序连接到 DB。
任务 3 - 配置 SignalR 应用程序
在此任务中,你将配置 Geek Quiz 以连接到SQL Server底板。 首先添加 SignalR.SqlServer NuGet 包,并将连接字符串设置为底板数据库。
从工具> NuGet 包管理器打开包管理器控制台。 确保在“默认项目”下拉列表中选择了 GeekQuiz 项目。 键入以下命令以安装 Microsoft.AspNet.SignalR.SqlServer NuGet 包。
Install-Package Microsoft.AspNet.SignalR.SqlServer
重复上一步,但这次对项目 GeekQuiz2 重复。
若要配置SQL Server底板,请打开 GeekQuiz 项目的 Startup.cs 文件,并将以下代码添加到 Configure 方法。 将 YOUR-DATABASE> 替换为<创建SQL Server底板时使用的数据库名称。 对 GeekQuiz2 项目重复此步骤。
(代码片段 - RealTimeSignalR - Ex2 - StartupConfiguration)
public class Startup { public void Configuration(IAppBuilder app) { var sqlConnectionString = @"Server=(localdb)\v11.0;Database=<YOUR-DATABASE>;Integrated Security=True;"; GlobalHost.DependencyResolver.UseSqlServer(sqlConnectionString); this.ConfigureAuth(app); app.MapSignalR(); } }
现在,这两个项目都配置为使用SQL Server底板,请按 F5 以同时运行它们。
同样, Visual Studio 将在不同的端口中启动 两个 Geek Quiz 实例。 将其中一个浏览器固定在左侧,另一个固定在屏幕右侧,并使用凭据登录。 保留左侧的“琐事”页,然后在右侧浏览器中转到 “统计信息 ”页。
在左侧浏览器中开始回答问题。 这一次,由于底板, “统计信息 ”页会更新。 应用程序之间的切换 (统计信息 现在位于左侧, 而 Trivia 位于右侧) 并重复测试以验证它是否适用于这两个实例。 底板充当每个连接的服务器的共享消息 缓存 ,每个服务器将消息存储在自己的本地缓存中,以便分发到连接的客户端。
返回 Visual Studio 并停止调试。
SQL Server底板组件在指定数据库上自动生成必要的表。 在SQL Server 对象资源管理器面板中,打开为底板 (创建的数据库,例如:SignalR) 并展开其表。 应会看到下表:
底板生成的表
右键单击 SignalR.Messages_0 表,然后选择“ 查看数据”。
查看 SignalR 底板消息表
回答琐碎问题时,可以看到发送到 中心 的不同消息。 底板将这些消息分发到任何连接的实例。
底板消息表
总结
在本动手实验室中,你已了解如何将 SignalR 添加到应用程序,并使用 中心将通知从服务器发送到连接的客户端。 此外,你还了解了在多个 IIS 实例中部署应用程序时如何使用 底板 组件横向扩展应用程序。