基于ASP.NET Core SignalR的流式传输

基于ASP.NET Core SignalR的流式传输

SignalR概述

SignalR是ASP.NET Core下非常流行的实现Web实时功能的库。微软再文档中列出了适合的应用场景:

适合 SignalR 的候选项:

  • 需要从服务器进行高频率更新的应用。示例包括游戏、社交网络、投票、拍卖、地图和 GPS 应用。

  • 仪表板和监视应用。示例包括公司仪表板、即时销售更新或旅行警报。

  • 协作应用。协作应用的示例包括白板应用和团队会议软件。

  • 需要通知的应用。社交网络、电子邮件、聊天、游戏、旅行警报和很多其他应用都需使用通知。

其实只要适合使用Ajax的场景都能使用,他比WebSockets更高级,实现了断线重连,广播,分组等功能。

流式传输

在介绍SingalR流式处理之前,我想先介绍一下流式处理的基本概念,

一提到流式传输,很多人往往感到比较棘手,那是因为可能用的比较少,通常我们习惯了准备数据然后一口气处理数据的编程范式,而不习惯一个个处理数据的范式。

流式处理就像一个水管,一头进水,一头出水。

例如C#中流式处理一个文本文件,我们是一次读取一行并处理他,而不是一口气读取文件中的所有行并处理他。

using (StreamReader sr = new StreamReader("TestFile.txt"))
{string line;// Read and display lines from the file until the end of// the file is reached.while ((line = sr.ReadLine()) != null)
{Console.WriteLine(line);
}
}

流式处理的好处就是数据的一部分准备好了,就可以对他立即进行处理,在内存中每次仅保留需要处理的那部分数据,这会大大优化内存的使用。

流式梳理非常适合处理大型数据集,例如文件读取,网络数据下载,IoT的数据传输,遍历并逐步处理数据库数据。

IEnumerable<T>

有必要再对这个接口重新做一个简要说明,枚举(称呼枚举或者迭代器,个人认为迭代器更合适)就是一个个列举。他是一种序列的概念,例如 1 2 3 4 5等,每列举一个我就可以处理一个。C#中的foreach就是用于处理迭代器的语法糖:

//枚举每一个Item
foreach(var item in GetNumbers())
{//处理每个item
}IEnumerable<int> GetNumbers()
{int i=0;while(i<10)
{yield return i++;
}
}//作为比对我列举一个常见的编程一口气准备好的编程范式。
List<int> GetNumbers()
{List<int> numbers = new List<int>(); //数组会分配内存,如果数据量很大,分配的内存会非常高。int i=0;while(i<10){numbers.add(i);}return numbers;
}

在C# 8.0以前,这个接口都是同步的,也就是说产生序列的方法会阻塞调用序列的方法。在异步async await 大行其道的今天,显然没有异步版本的迭代器实现是一个巨大的缺陷。(在这一以前可以用使用ValueTask<T>)

IAsyncEnumerable<T>

C# 8.0引入了这个接口,官网文档将它称为异步流。这个接口其实就是为了统一async await IEnumerable<T> . 微软在背后做了大量的工作,具体实现细节大家可以参考Async streams - C# 8.0 specification proposals | Microsoft Docs

我们来使用这个接口返回一个异步的迭代器:

public async IAsyncEnumerable<Data> GetData([EnumeratorCancellation] CancellationToken cancellationToken)
{var data = await _respository.GetDataAsync(cancellationToken);//为了演示,假设从数据库异步读取数据。foreach (var item in data)
{yield return item;
}
}
SignalR的流式传输

SignalR的流式传输使用了IAsyncEnumerable<T>接口,我直接引用微软的说法:

ASP.NET Core SignalR支持从客户端到服务器以及从服务器到客户端的流式传输。这适用于数据片段随着时间的推移而发生的情况。流式传输时,每个片段一旦变为可用,就会发送到客户端或服务器,而不是等待所有数据都可用。

当集线器方法返回 IAsyncEnumerable ChannelReader Task<IAsyncEnumerable<T>> Task<ChannelReader<T>> 时它会自动成为流式处理中心方法

public class AsyncEnumerableHub : Hub
{public async IAsyncEnumerable<int> Counter(int count,int delay,[EnumeratorCancellation]CancellationToken cancellationToken){for (var i = 0; i < count; i++){// Check the cancellation token regularly so that the server will stop// producing items if the client disconnects.cancellationToken.ThrowIfCancellationRequested();yield return i;// Use the cancellationToken in other APIs that accept cancellation// tokens so the cancellation can flow down to them.await Task.Delay(delay, cancellationToken);}}
}

代码很清楚的说明了这一做法。ChannelReader是另一种实现方法,这篇不做讲解。

做一个例子来实现一下

当下非常流行数据监控大屏应用。监控交通状况,股票行情,企业生产数据看板,IOT传感器实时数据显示,实时销售数据分析等等。

我想做一个简单的数据更新大屏的例子(一切为了简单,只用内存中的数据来存放数据),原谅我UI能比比较差,这篇不展示UI上的东西,而只展示数据如何以流的方式发送。

第一步 准备一个类用于处理数据更新

这个类使用一个定时器来定期更新数据,并记录一个数据是否更新的状态以便于只在数据更新的时候才发送数据。同时使用一个并发字典记录待更新的数据。

public class DataTicker{private Timer _timer; private volatile bool isUpdated  = false;//标记数据是否更新private readonly ConcurrentDictionary<string, Data> _datas = new ConcurrentDictionary<string, Data>();public DataTicker(){InitData();StartUpdateData();}public bool IsUpdated{get { return isUpdated; }}public async Task<ICollection<Data>> GetData(){if (IsUpdated) //如果数据已更新才发送数据{await Task.Delay(500); //模拟返回数据的延迟。这里好的做法是异步的方式返回数据。return _datas.Values;}return null;}public void StartUpdateData(){_timer = new Timer(UpdateDate, null, TimeSpan.FromMilliseconds(3000), TimeSpan.FromMilliseconds(3000));}private void InitData(){for(int i=0;i<10;i++){Data data = new Data();data.Id = i;data.Name = i.ToString();data.UpdatedDate = DateTime.Now;_datas.TryAdd(i.ToString(), data);}}public void UpdateDate(object state){var newData = foreach (var item in _datas.Values){item.UpdatedDate = System.DateTime.Now;}this.isUpdated = true;}public void MarkAsRead(){this.isUpdated = false;}}
第二部 编写SignalR中心异步流方法
public class DataUpdateHub : Hub{private DataTicker _ticker;public DataUpdateHub(DataTicker stockTicker){this._ticker = stockTicker;}//这个方法没有使用cancellationToken,好的做法是从外部传递cancellationTokenpublic async IAsyncEnumerable<Data> GetData([EnumeratorCancellation] CancellationToken cancellationToken){while (true){var data = await _ticker.GetData(); //当有数据更新的时候方法会返回数据,否则返回空。if (data != null){foreach (var item in data){yield return item;}_ticker.MarkAsRead(); //发送完毕后,将标记数据标记为已发送。}}}}
第三步 Javascript客户端调用代码
connection.start().then(function () {connection.stream("GetData") //连接到hub的异步流方法,当有数据从中心流出时候,next方法会被调用。因为本例中心使用wihle(true)方法,数据流会一直发送,所以complete方法将不会被调用。如果中心方法返回有限数据结束后例如使用foreach,则会调用complete方法。.subscribe({next: (item) => {var li = document.createElement("li"); li.textContent = "ID:" + item.id + "Datetime:" + item.updatedDate;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(function (err) {return console.error(err.toString());
});
第四步 配置DataTicker依赖注入
builder.Services.AddSignalR();
builder.Services.AddSingleton<DataTicker>();

注意:这里的DataTicker注册的是单例的,也就是说所有发送给客户端的数据是共享的。既然是共享的,也就存在并发访问的问题,这就是为什么使用volatile关键字和并发字典的原因。

总结

ASP.NET SignalR可以很方便的使用异步流传输数据,这样客户端和服务器端是使用流的方式连接在一起的。

本文使用的是从服务器到客户端的流,当然你可以使用从客户端到服务器端的流。从.Net和Java的客户端都可以调用SignalR的中心流方法。具体可以参考微软官方文档,使用 ASP.NET Core 中的流式处理SignalR | Microsoft Docs

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/291907.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Leetcode: Palindrome Partition I II

题目一, 题目二 思路 1. 第一遍做时就参考别人的, 现在又忘记了 做的时候使用的是二维动态规划, 超时加超内存 2. 只当 string 左部分是回文的时候才有可能减少 cut 3. 一维动规. 令 cuts[i] 表示string[i, string.size()] 所需的切割数, 那么 状态转移方程为 cuts[i] min(cut…

FatMouse

时间限制&#xff1a;1 秒 内存限制&#xff1a;128 兆 特殊判题&#xff1a;否 提交&#xff1a;1431 解决&#xff1a;641 题目描述&#xff1a;FatMouse prepared M pounds of cat food, ready to trade with the cats guarding the warehouse containing his favorite food…

linux之安装Clion和运行使用总结

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到教程 1、Clion的简单介绍 CLion是以IntelliJ为基础,专为开发C及C++所设计的跨平台IDE,可以在Windows、Linux及MacOS使用,这里我是在ubuntu 16.0.4基础上安装。 2、linux平台…

Tips

1.Unity\Editor\Data\Resources\ScriptTemplates 里的文件是Unity 生成文件的模板. 2.提示文件尾不一致.用VS打开Unity\Editor\Data\Resources\ScriptTemplates里的模板文件.文件->高级保存选项->Windows(CRLF) 3.Scene视图里选择Transform工具,按V可以通过Pivot对齐 4.N…

体验 正式发布 的OSM v1.0.0 版本

2021年10月份发布了OSM 1.0 RC[1]&#xff0c;在过去的几个月里&#xff0c;OSM 的贡献者一直在努力为 v1.0.0 版本的发布做准备。2022年2月1日&#xff0c;OSM 团队正式发布 1.0.0 版本[2]。OSM 从最初的发布到现在已经走了很长的路&#xff0c;团队继续专注于社区需要的关键和…

linux c之用命名管道实现进程通信

1、命名管道相关信息介绍 不是很了解命名管道先看这个篇博客 http://blog.csdn.net/u011068702/article/details/55102379 linux c之命名管道简单使用 博客介绍了创建管道的方法,这里还需要介绍 open函数和调用阻塞 FIFO文件也可以使用open调用来打开,mkfifo函数只是创建…

vue表格刷新数据_Vue.js+Layer表格数据绑定与实现更新的实例

一&#xff1a;先使用Vue.js绑定好数据与更新事件使用v-on绑定好事件,在事件里边直接把该行数据传递进去&#xff0c;在更新方法里边就可以直接取出需要更新的数据选择用户名学号班级操作{{item.UserName}}{{item.Number}}{{item.Class}}删除更新//实例化vue.js(用来给表格提供…

数据流图的画法

数据流图的画法 数据流图也称为数据流程图date flow diagram , DFD&#xff0c;是一种便于用户理解和分析系统数据流程的图形工具&#xff0c;他摆脱了系统和详细内容&#xff0c;精确的在逻辑上描写叙述系统的功能、输入、输出和数据存储等&#xff0c;是系统逻辑模型的重要组…

linux之自己总结学习linux的资源推荐

1、学习linux前辈的网站 安卓和linux网络编程 http://www.cnblogs.com/hnrainll/ IBM学习linux技术地址: https://www.ibm.com/developerworks/cn/views/linux/libraryview.jsp 2、学习linux的途径,或者说过程 1、APUE再深读 – 尤其是进程,线程,IPC,套接字 2、 多…

01-算法简介

数据结构和算法 基于《算法图解》—Aditya Bhargava和《数据结构》—严蔚敏 ** 算法图解&#xff1a;(基于Python)* 第1章—算法简介 1.1 引言 算法是一组完成任务的指令。 1.2 二分查找&#xff08;binary_search&#xff09; 二分查找是一种算法&#xff0c;其输入是一个…

浏览器渲染机制面试_【前端面试必考题】页面渲染机制(一)

页面渲染机制这部分内容会分成两篇来进行讲解&#xff0c;这两篇里我们准备聊一下页面的渲染的过程&#xff0c;包括页面的加载、DOM 树的构建、CSSOM 树的构建、渲染树的构建和最后的渲染过程等。浏览器的渲染机制和网页的优化息息相关&#xff0c;只有知道了页面是怎么渲染出…

OpenJudge/Poj 1226 Substrings

1.链接地址&#xff1a; http://bailian.openjudge.cn/practice/1226/ http://poj.org/problem?id1226 2.题目&#xff1a; 总时间限制:1000ms内存限制:65536kB描述You are given a number of case-sensitive strings of alphabetic characters, find the largest string X, s…

MFC继承表

转载于:https://www.cnblogs.com/Lthis/p/4264967.html

linux之fdisk查看分区和mkfs.ext3删除分区和mount挂载和e2label添加卷标使用总结

一、使用fdisk、mkfs.ext3、和mount、e2lable的原因 有个分区挂载不上,然后需要格式化分区,还需要添加卷标 二、fdisk、mkfs.ext3、mount、e2lable命令介绍 1、fdisk命令介绍 1)、了解分区 分区是将一个硬盘驱动器分成若干个逻辑驱动器,分区是把硬盘连续的区块当做一个…

Task+ConcurrentQueue多线程编程

队列&#xff08;Queue&#xff09;代表了一个先进先出的对象集合。当您需要对各项进行先进先出的访问时&#xff0c;则使用队列。当您在列表中添加一项&#xff0c;称为入队&#xff0c;当您从列表中移除一项时&#xff0c;称为出队。ConcurrentQueue<T>队列是一个高效的…

怎样从一个手机上安两个不同版本的软件_怎么在一部手机上安装两个不同版本的微信?...

今天我们就向大家介绍一个非常简便的方法&#xff0c;帮助我们实现在Android智能手机将应用程序多开。1、安卓微信双卡方法&#xff1a;准备工作从上图我们可以看到&#xff0c;小编在自己的手机上已经安装了两个不同版本的微信。通常在自己的手机上安装同一应用程序时&#xf…

牛人的英语学习方法

牛人的英语学习方法&#xff1a; 总结1&#xff1a;背出来的单词 不背熟单词就去学所谓的听力阅读作文语法&#xff0c;就像没学走路就想学跑步&#xff0c;没吃饭就想拉屎&#xff0c;没脱牛仔裤就想脱内裤一样的痴心妄想。所以想学英语的人要做的第一件事&#xff0c;不是哭&…

02-选择排序

数据结构和算法 基于《算法图解》—Aditya Bhargava和《数据结构》—严蔚敏 第2章 2.1 内存的工作原理 计算机就像是很多抽屉的集合体&#xff0c;每个抽屉都有地址。需要将数据存储到内存是&#xff0c;请求计算机提供存储空间&#xff0c;计算机则分配一个地址。需要存储多…

[SAP ABAP开发技术总结]权限对象检查

20.14. 权限检查 AT SELECTION-SCREEN. DATA: BEGIN OF lt_bukrs OCCURS 0, bukrs TYPE t001-bukrs, END OF lt_bukrs. SELECT bukrs FROM t001 INTO CORRESPONDING FIELDS OF TABLE lt_bukrs WHERE bukrs IN s_bukrs. LOOP AT lt_bukrs. AUTHORITY-C…

linux c之strncpy函数和strncmp函数最简单使用总结

1.原型声明&#xff1a; char * strncpy(char *dest,const char *src, size_t n); strncmp() 用来比较两个字符串的前n个字符&#xff0c;区分大小写&#xff0c;其原型为&#xff1a; int strncmp ( const char * str1, const char * str2, size_t n ); 若str1与str2的前n…