ASP.NET Core SignalR中的流式传输
什么是流式传输?
流式传输是这一种以稳定持续流的形式传输数据的技术。
流式传输的使用场景
有些场景中,服务器返回的数据量较大,等待时间较长,客户端不得不等待服务器返回所有数据后,再进行相应的操作。这时候使用流式传输,可以将服务器数据碎片化,当每个数据碎片读取完成之后,就只传输完成的部分,而不需要等待所有数据都读取完成。
如何在ASP.NET Core SignalR中启用流式传输
在ASP.NET Core SignalR中当一个Hub方法的返回值是ChannelReader或者Task<ChannelReader>, 这个Hub方法自动就会变成一个流式传输Hub方法。
下面我们来做了一个简单的例子
创建一个ASP.NET Core Web应用
首先我们使用Visual Studio 2017创建一个ASP.NET Core Web应用程序。

选择创建ASP.NET Core 2.1的Web Application

创建Hub
下面我们添加一个StreamHub类,代码如下
public class StreamHub : Hub
{
public ChannelReader<int> DelayCounter(int delay)
{
var channel = Channel.CreateUnbounded<int>();
_ = WriteItems(channel.Writer, 20, delay);
return channel.Reader;
}
private async Task WriteItems(ChannelWriter<int> writer, int count, int delay)
{
for (var i = 0; i < count; i++)
{
await writer.WriteAsync(i);
await Task.Delay(delay);
}
writer.TryComplete();
}
}
DelayCounter是一个流式传输方法, 它定义了一个延迟参数delay, 定义了推送数据碎片的间隔时间WriteItems是一个私有方法,它返回了一个Task对象WriteItems方法的最后一行writer.TryComplete()表明了流式传输完成
配置SignalR
首先我们在Startup类的ConfigureService方法中添加SignalR服务
services.AddSignalR();
然后我们还需要为SignalR流添加路由,我们需要在Startup类的Configure方法中添加如下代码:
app.UseSignalR(routes =>
{
routes.MapHub<StreamHub>("/streamHub");
});
添加SignalR客户端脚本库
这一步中我们需要在客户端中添加SignalR JS库。
这里我们需要借助npm来下载SignalR JS库。
npm install @aspnet/signalr
安装完成后,我们手动将signalr.js从<projectfolder>\node_modules@aspnet\signalr\dist\browser目录中拷贝到wwwroot\lib\signalr目录下
编写页面
拷贝以下代码到Index.cshtml
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
<div class="container">
<div class="row"> </div>
<div class="row">
<div class="col-6"> </div>
<div class="col-6">
<input type="button" id="streamButton" value="Start Streaming" />
</div>
</div>
<div class="row">
<div class="col-12">
<hr />
</div>
</div>
<div class="row">
<div class="col-6"> </div>
<div class="col-6">
<ul id="messagesList"></ul>
</div>
</div>
</div>
<script src="~/lib/signalr/signalr.js"></script>
<script src="~/js/signalrstream.js"></script>
JavaScript中启用流式传输
在wwwroot\js目录中创建一个新文件signalrstream.js ,代码如下
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var connection = new signalR.HubConnectionBuilder()
.withUrl("/streamHub")
.build();
document.getElementById("streamButton").addEventListener("click", (event) => __awaiter(this, void 0, void 0, function* () {
try {
connection.stream("DelayCounter", 500)
.subscribe({
next: (item) => {
var li = document.createElement("li");
li.textContent = item;
document.getElementById("messagesList").appendChild(li);
},
complete: () => {
var li = document.createElement("li");
li.textContent = "Stream completed";
document.getElementById("messagesList").appendChild(li);
},
error: (err) => {
var li = document.createElement("li");
li.textContent = err;
document.getElementById("messagesList").appendChild(li);
},
});
}
catch (e) {
console.error(e.toString());
}
event.preventDefault();
}));
(() => __awaiter(this, void 0, void 0, function* () {
try {
yield connection.start();
}
catch (e) {
console.error(e.toString());
}
}))();
代码解释
与传统SignalR不同,这里我们使用了不同的语法创建一个SignalR连接
var connection = new signalR.HubConnectionBuilder()
.withUrl("/streamHub")
.build();
对于一般的SignalR连接,我们会使用connection.on方法来添加监听器,但是在使用流式传输的时候,我们需要改用connection.stream方法, 这个方法有2个参数
- Hub方法名称, 本例中是
DelayCounter - Hub方法的参数, 本例中是
500
connection.stream("DelayCounter", 500)
.subscribe({
next: (item) => {
var li = document.createElement("li");
li.textContent = item;
document.getElementById("messagesList").appendChild(li);
},
complete: () => {
var li = document.createElement("li");
li.textContent = "Stream completed";
document.getElementById("messagesList").appendChild(li);
},
error: (err) => {
var li = document.createElement("li");
li.textContent = err;
document.getElementById("messagesList").appendChild(li);
},
});
connection.stream方法的返回对象中有一个subscribe方法,这个方法中可以注册3个事件
- next - 获得到一个数据碎片时执行
- complete - 流式传输完成时执行
- error - 流式传输异常时执行
最终效果

总结
流式传输不是一个新概念,但是对ASP.NET Core SignalR来说,这是一个非常棒的特性。流式传输保证的用户体验的流畅,也降低了服务器压力。
大部分程序员都知道SignalR不能传输过大的数据,但是使用流式传输之后,客户端不需要一次性等待服务器端返回所有数据,所以如果你的项目单次请求的数据量很大,可以考虑使用SignalR的流式传输改善用户体验,减轻服务器压力。
本篇源代码地址 https://github.com/lamondlu/StreamingInSignalR
ASP.NET Core SignalR中的流式传输的更多相关文章
- ASP.NET Core MVC中Controller的Action如何直接使用Response.Body的Stream流输出数据
在ASP.NET Core MVC中,我们有时候需要在Controller的Action中直接输出数据到Response.Body这个Stream流中,例如如果我们要输出一个很大的文件到客户端浏览器让 ...
- 【译】ASP.NET Core 6 中的性能改进
原文 | Brennan Conroy 翻译 | 郑子铭 受到 Stephen Toub 关于 .NET 性能的博文的启发,我们正在写一篇类似的文章来强调 6.0 中对 ASP.NET Core 所做 ...
- 通过重建Hosting系统理解HTTP请求在ASP.NET Core管道中的处理流程[下]:管道是如何构建起来的?
在<中篇>中,我们对管道的构成以及它对请求的处理流程进行了详细介绍,接下来我们需要了解的是这样一个管道是如何被构建起来的.总的来说,管道由一个服务器和一个HttpApplication构成 ...
- Asp.Net Core SignalR 用泛型Hub优雅的调用前端方法及传参
继续学习 最近一直在使用Asp.Net Core SignalR(下面成SignalR Core)为小程序提供websocket支持,前端时间也发了一个学习笔记,在使用过程中稍微看了下它的源码,不得不 ...
- Asp.Net Core SignalR 与微信小程序交互笔记
什么是Asp.Net Core SignalR Asp.Net Core SignalR 是微软开发的一套基于Asp.Net Core的与Web进行实时交互的类库,它使我们的应用能够实时的把数据推送给 ...
- [asp.net core]SignalR一个例子
摘要 在一个后台管理的页面想实时监控一些操作的数据,想到用signalR. 一个例子 asp.net core+signalR 使用Nuget安装包:Microsoft.AspNetCore.Sign ...
- ASP.NET Core SignalR
ASP.NET Core SignalR 是微软开发的一套基于ASP.NET Core的与Web进行实时交互的类库,它使我们的应用能够实时的把数据推送给Web客户端. 功能 自动管理连接 允许同时广播 ...
- 微服务中的健康监测以及其在ASP.NET Core服务中实现运行状况检查
1 .什么是健康检查? 健康检查几乎就是名称暗示的.它是一种检查您的应用程序是否健康的方法.随着越来越多的应用程序转向微服务式架构,健康检查变得尤其重要(Health Check).虽然微服务架构有很 ...
- ASP.NET Core MVC中的IActionFilter.OnActionExecuted方法执行时,Controller中Action返回的对象是否已经输出到Http Response中
我们在ASP.NET Core MVC项目中有如下HomeController: using Microsoft.AspNetCore.Mvc; namespace AspNetCoreActionF ...
随机推荐
- Android 博客导航
Android 博客导航 一. 基础知识 Android 常用知识点 Android 常见问题解决 Android 常用控件 控件常用属性 Material Design 常用控件 二.常用知识点 动 ...
- yum程序下载被占用
Loaded plugins: fastestmirror, refresh-packagekit, security Existing lock /var/run/yum.pid: another ...
- 如何让pandas表格直接转换为markdown表格
https://stackoverflow.com/questions/33181846/programmatically-convert-pandas-dataframe-to-markdown-t ...
- c# winform打印excel(使用NPOI+Spire.xls+PrintDocument直接打印excel)
前言 c#做winform程序要求生成并打印Excel报告,为了不安装Office相应组件,我选择了NPOI来生成Excel报告,用winform的PrintDocument控件来触发打印操作,而难点 ...
- centos7系统下搭建docker本地镜像仓库
## 准备工作 用到的工具, Xshell5, Xftp5, docker.io/registry:latest镜像 关于docker的安装和设置加速, 请参考这篇博文centos7系统下 docke ...
- 818C.soft thief
Yet another round on DecoForces is coming! Grandpa Maks wanted to participate in it but someone has ...
- [LeetCode] Loud and Rich 聒噪与富有
In a group of N people (labelled 0, 1, 2, ..., N-1), each person has different amounts of money, and ...
- oracle 创建的表为什么在table里没有,但是可以查出来
有两种的可能: 1这个表在其他用户下创建的,当前用户没有权限访问,此表不在属于当前用户 2查询时写的表名,并不是真正意义的表名,可能指向其他用户,或者就不是这个表
- Python函数式编程之装饰器
原则:对修改是封闭的,对扩展是开放的,方法:一般不修改函数或者类,而是扩展函数或者类 一:装饰器 允许我们将一个提供核心功能的对象和其他可以改变这个功能的对象’包裹‘在一起, 使用装饰对象的任何对象与 ...
- [微信小程序]编译.wxss出错,2 not found
小程序新建项目就出错:2 not found 编译.wxss文件出错(不是一般的郁闷,新建项目就报错...) 大概的情况是开发工具没有更新.或更新不到, 第一,可以删掉开发工具重新下载最新安装: 第 ...