QCon 演讲火热征集中,快来分享你的技术实践与洞见! 了解详情
写点什么

SignalR Core 尝鲜

A developer's tour of SignalR Core Alpha

  • 2018-03-19
  • 本文字数:5153 字

    阅读完需:约 17 分钟

要点

  • SignalR Core 改用 Microsoft.AspNetCore.Sockets,不再依赖 HTTP。
  • 使用 MessagePack 序列化格式,支持二进制协议。
  • TypeScript 客户端移除了第三方依赖包。
  • 支持 WebSocket 原生客户端,可以使用自己构建的客户端连接到 SignalR 服务器。
  • 伸缩方式更灵活,可以通过自己实现的方式进行横向扩展。

几个月前,SignalR Core 团队发布了一个非官方版本的ASP.NET Core SignalR 。为此,开发人员有机会了解其工作原理以及ASP.NET SignalR 与Signal Core 新架构之间的区别。

SignalR Core 中移除了哪些特性

通过对比两个版本的 SignalR 可以发现,新版本不再支持一些重要的特性。首先是移除了对 jQuery 和其他第三方类库的依赖,因为新版本的 JavaScript 客户端是使用 TypeScript 开发的。其次是自动连接后的消息重放功能,移除该功能主要是出于性能方面的考虑。服务器需要为每一个连接维护一个缓冲区,用于保存消息,以便后续重新发送。当客户端断开连接,可以尝试重新恢复连接,然后将未发送的消息发送给客户端。可以想象,如果有很多客户端断开连接,而且每个客户端都发送大量的消息,对于服务器来说是个很大的负担。另一个被 SignalR 团队移除特性是多 Hub 端点,所以,在新版本里,每个连接只有一个 Hub。

新版本的 SignalR Core 不再支持横向扩展(Scale Out)模型,原因是 MessageBus 被当成了横向扩展的“万灵丹”,但它实际上只支持 Azure Service Bus 、Redis 和 SQL Server。在实际的协作场景当中(客户端到客户端),随着客户端和消息数量的增长,通过以上三种方式进行横向扩展会有瓶颈问题。

不过,我认为,移除横向扩展功能这一决定有点太过激进,因为在某些场景下,MessageBus 仍然十分有用。例如,在将 SignalR 作为一个广播服务器时,它可以控制发送消息的数量。而在 SignalR Core 的 alpha 版本中,开发者可以根据实际情况选择是否进行横向扩展,如业务需求、系统约束或基础设施,这种设计更加“可插拔”。SignalR Core 团队提供了一个使用 Redis 进行横向扩展的示例。其他扩展方式可能会被包含在 SignalR Core 的最终版中。

最后一个被移除的功能是多服务器间的双向复制(backplane),因为这个功能会在服务器场生成太多的流量。ASP.NET SignalR 通过 MessageBus 在服务器间复制每一个消息,因为客户端无法直接连接到服务器场,而现在,SignalR 使用粘性会话来避免在所有服务器间复制消息。这样一来,SignalR Core 就可以知道哪个客户端连接到了哪台服务器上。

SignalR Core 中增加了哪些新特性

现在让我们来看一下 SignalR Core 带来了哪些新的特性。首先是使用了二进制协议来发送和接收消息。在 ASP.NET SignalR 中只能使用 JSON 格式的文本来发送和接收消息,而现在则可以使用二进制协议,该二进制协议基于 MessagePack 序列化格式,比 JSON 更快、体积更小。

主机无关性是另一个非常重要的特性,有了这个特性,就可以移除对 HTTP 的依赖。现在,我们可以在 HTTP 或 TCP 之上使用 SignalR。端点 API 也是非常重要的一个特性,它是基础的构建块,用于支持主机无关性。因为新版本是基于 Microsoft.AspNetCore.Sockets 这一底层的网络抽象层,所以可以直接使用 Socket。这么说来,SignalR Hub 其实也就是一个端点。

多格式也是一个很酷的特性,有了这个特性,我们可以处理任意格式的消息。我们可以使用多种不同的客户端连接到同一个端点,这些客户端可以使用不同的消息格式。也就是说,SignalR Core 已经实现了消息的格式无关性。这个示例在同一个端点上使用了三种格式(JSON、PIPE 和Protobuf)来读写消息,因为使用了自定义的处理器,可以无缝地处理各种格式的消息。正如之前提到的那样,可能是因为使用了Microsoft.AspNetCore.Sockets,从底层来看,消息都只是简单的二进制字节。

新版本还支持WebSocket 原生客户端,所以开发者也可以使用除SignalR Web 客户端之外的其他客户端。之前,开发者必须使用基于JavaScript 的Web 客户端连接到SignalR 服务器上。现在,开发者可以自己开发客户端,充分利用浏览器API 提供的优势。当然,开发者也可以使用最新的TypeScript 客户端,因为TypeScript 提供了很多有用的特性。另外,客户端是通过NPM 包管理器进行发布的,这样依赖管理也就变得更简单了。

最后一点是,横向扩展变得更灵活,提供了更高的可扩展性。SignalR Core 团队简化并改进了横向扩展模型,并提供了一个基于Redis 的横向扩展示例,帮助开发者了解如何进行横向扩展。

去年9 月14 号,SignalR Core 团队发布了第一个alpha 版,10 月9 号发布了第二个alpha 版,也就是SignalR Core 2.0 官方预览版。现在,我们即将探讨这一版本中包含的主要变化。

在得知有新版本后,我在第一时间去拉取代码,并试着去构建最新的代码。不过,正如预期的那样,因为代码还在开发当中,无法立马构建成功。尽管如此,我们还是能够在第一时间看到正在发生的变更,这有助于我们了解为什么要做出这些变更。接下来,我将列出我在构建过程中遇到的问题,并告诉大家我做了哪些事情来修复这些问题。

要在项目中使用SignalR Core,必须引用Microsoft.AspNetCore.SignalR,最新版本是1.0.0-alpha2-final。

HubConnectionBuilder

在之前的版本中,如果要在服务器端连接到一个 Hub,我们会使用 HubConnection 类,比如:

var connection = new HubConnection(new Uri(baseUrl), loggerFactory);而现在,我们需要使用 HubConnectionBuilder 类(实现了 Builder 设计模式)来连接 SignalR Core Hub,这也是第一个导致代码构建失败的变化。这一变化让建立连接变得更具可扩展性,不需要使用满是参数或带有 null 参数的构造函数。我很喜欢这个变化,因为它简化了建立连接的过程。

复制代码
var connection = new HubConnectionBuilder()
                        .WithUrl(baseUrl)
                        .WithConsoleLogger()
                        .Build();

在服务器端处理连接

在之前的版本中,客户端在“On”方法中处理由 SignalR Hub 广播过来的数据,这个时候需要处理一大堆参数:

复制代码
connection.On("UpdateCatalog", new[] { typeof(IEnumerable<Product>) }, data =>
{
    var products = data[0] as List<Product>;
    foreach (var item in products)
    {
        Console.WriteLine($"{item.name}: {item.quantity}");
    }
});

可以看出,这个方法有点累赘,因为不管用不用得到方法里的参数,都必须指定这些参数和它们的类型,即使用不到,也要指定一个空数组。但问题是,处理器的参数是无类型的,所以,即使在数组中指定了类型,仍然需要遍历数组,对它们进行类型转换。

而新版本的 SignalR Core 提供了最新的泛型方法重载机制,通过这种方式指定参数类型后就不需要再进行类型转换。泛型方法对原始方法进行了包装,从而简化了开发者的工作。最终的代码更简单、可读性更好。

复制代码
connection.On<List<Product>>("UpdateCatalog", products =>
{
    // now, “products” parameter is a List<Product> type.
    foreach (var item in products)
    {
        Console.WriteLine($"{item.name}: {item.quantity}");
    }
});

命名约定

我发现 Invoke 方法名发生了变化(这个变化也导致代码构建失败):

await connection.Invoke("RegisterProduct", cts.Token, product, quanity);这是一个异步方法,为了遵循命名约定,方法名被改成了 InvokeAsync,方法参数的顺序也发生了改变,令牌参数被放在了最后:

await connection.InvokeAsync("RegisterProduct", product, quanity, cts.Token);因为遵循了命名约定和标准,开发者在使用 SignalR Core API(包括 SignalR Core 的开发者团队)时就更加直观,因为它带来了代码的统一性。例如,如果开发者在他们的 IDE 中使用了 Intellisense,就可以提前知道这个方法是异步的。

另一个与命名约定有关的变化是 MapEndpoint 方法,这个方法被改成了 MapEndPoint,遵循了 Pascal 的大小写风格

之前:

复制代码
app.UseSockets(routes =>
{
    routes.MapEndpoint<MessagesEndPoint>("/message");
});

现在:

复制代码
app.UseSockets(routes =>
{
    routes.MapEndPoint<MessagesEndPoint>("message");
});

可以看到,现在不使用“/”符号了。MapHub 方法也一样。不过,我们发现这里存在一个问题,这些方法没有使用 PathString API。不过,在下一个版本中会继续使用“/”,与其他的.Net Core API 保持一致。

命名变更

命名方面发生了很多变更,其中一个是与 Connection 类有关的 ConnectionContext。ConnectionContext 包含了与连接相关的信息,如元数据、通道等。

之前:

public override async Task OnConnectedAsync(Connection connection)现在:

public override async Task OnConnectedAsync(ConnectionContext connection)另一个命名方面的变更与 ConnectionContext 中的 Transport 有关。之前,用于管理输入和输出的属性分别叫作 Input 和 Output,而现在它们被改为 In 和 Out。

之前:

复制代码
connection.Transport.Input.WaitToReadAsync()
connection.Transport.Output.WriteAsync()

现在:

复制代码
connection.Transport.In.WaitToReadAsync()
connection.Transport.Out.WriteAsync()

TryRead 和 WriteAsync

TryRead 和 WriteAsync 方法得到了简化。之前,它们接收一个 Message 对象作为参数。

之前:

复制代码
Message message;
if (connection.Transport.Input.TryRead(out message))
{
    ...
}
connection.Transport.Output.WriteAsync(new Message(payload, format, endOfMessage));

现在:

复制代码
// message is byte[]
if (connection.Transport.In.TryRead(out var message))
{
    ...
}
// payload is byte[]
connection.Transport.Out.WriteAsync(payload);

现在他们使用字节数组作为参数,因为底层的 Socket 使用了 Channel<byte[]>。SignalR Core 团队认为,将字节数据移到上层可以让 Socket 层的逻辑更清晰。之前,SignalR Core 团队在字节数据之上使用了一个底层的数据帧协议(不过 WebSocket 已经有数据帧,所以没有在 WebSocket 上使用该协议)。

因此,Microsoft.AspNetCore.Sockets 层得到了“净化”,只允许端点处理二进制数据,而端点就可以使用任何一种协议,比如 TCP 或 HTTP。

底层的数据帧协议是在 Microsoft.AspNetCore.SignalR 层实现的,所以消息类型、数据帧都是在实现了 IHubProtocol 接口的类中处理的,比如 JsonHubProtocol 和 MessagPackHubProtocol。这种设计提供了一种可扩展的方式用于实现其他的 Hub 协议。

其他变更

我们可以直接通过 NPM 管理器来安装 signalr-client,比如,我在 package.json 文件里将它作为客户端依赖:

复制代码
{
  "version": "1.0.0",
  "name": "asp.net",
  "private": true,
  "dependencies": {
    "@aspnet/signalr-client": "^1.0.0-alpha2-final",
    "jQuery.tabulator": "^1.12.0"
  }
}

Visual Studio 会在构建解决方案时自动安装这个包。当然,我们也可以使用.NET Core 内置的新特性,它会自动把 signalr-client 文件拷贝到 wwwroot 目录,这样就不需要再使用 gulp、grunt 或其他任务执行器了。

复制代码
[
    {
        "outputFileName": "wwwroot/lib/signalr/signalr-clientES5-1.0.0-alpha2-final.min.js",
        "inputFiles": [
            "node_modules/@aspnet/signalr-client/dist/browser/signalr-clientES5-1.0.0-alpha2-final.min.js"
        ],
        "minify": {
            "enabled": false
        }
    },
    {
        "outputFileName": "wwwroot/lib/signalr/signalr-client-1.0.0-alpha2-final.min.js",
        "inputFiles": [
            "node_modules/@aspnet/signalr-client/dist/browser/signalr-client-1.0.0-alpha2-final.min.js"
        ],
        "minify": {
            "enabled": false
        }
    }
]

默认情况下,.NET Core 启用 minify 选项,而我引用的文件已经被 minify 过,当它尝试再次 minify 这些文件时就会报错,于是我就把 minify 选项禁用了。

结论

以上就是我在升级到最新版 SignalR Core 时发现的一些变化。我把它们分享出来,让其他开发者也知道这些变更以及为什么要做出这些变更。我希望这些信息对大家有用,也鼓励大家在自己的项目中测试最新的 SignalR Core。

我花了几个小时解决在构建新版本代码时遇到的问题,而查看代码和理解这些变更又额外花了我几个小时时间,不过这些都是值得的。

关于作者

Geovanny Alzate Sandoval 是一名来自哥伦比亚麦德林的系统工程师,他喜欢所有与软件开发、新技术、设计模式和软件架构相关的事物。他已经在该领域工作了十多年,做过开发者、技术负责人和软件架构师。他乐于向社区做贡献,喜欢在博客上写与微软新技术有关的东西。另外,他还是麦德林.NET 开发者社区 MDE.NET 的联合组织者。

查看英文原文: First Hand Account of SignalR Core

2018-03-19 17:572396

评论

发布
暂无评论
发现更多内容

Presentify Mac版是视频录制、教学视频的最佳工具

理理

几何画板Sketchpad中文激活码及安装教程 Sketchpad for Mac几何教学工具

理理

【实践篇】一次Paas化热部署实践分享

京东科技开发者

Mosaic for mac 窗口布局管理工具 让你的界面看起来更加整洁清爽!

理理

永久版 Astute Graphics for Mac(全系列ai插件合集)

理理

4K Video Downloader Plus Pro for Mac(4K视频下载软件)

Mac相关知识分享

使用 NGINX 和 NGINX Plus 实现负载均衡(第 1 部分)

NGINX开源社区

nginx 负载均衡 读书笔记

数造科技:赋能数字政府与智慧城市,推动数据资产入表

数造万象

政务 DataOps 大模型 #科技 数据资产入表

如何在Python中集成京东API

代码忍者

API 接口 pinduoduo API

终端SSH仿真工具 securecrt mac破解安装教程 及securecrt密钥

理理

中间件全球数据实时同步利器,EventGrid事件流重磅发布

华为云开发者联盟

大数据 中间件 数据同步

益阳等保测评中心在哪里?电话多少?

行云管家

等保 等级保护 等保测评 益阳

LLMs 如何处理相互矛盾的指令?指令遵循优先级实验

Baihai IDP

程序员 AI LLM Prompt GenAI

前端调试实践

京东零售技术

前端

记一次老商家端应用内存突然飚高原因分析

京东科技开发者

Mac 电量管理大师 ——Power Manager for Mac,节能增效新利器

理理

新人如何做好项目管理?|京东零售技术人成长

京东零售技术

项目管理 技术成长 项目经理

SwitchResX for Mac实用的屏幕分辨率修改工具

Mac相关知识分享

娱乐业怎么定义?以及什么情况下需要用到堡垒机?

行云管家

网络安全 娱乐 等保 堡垒机 等级保护

API接口对淘宝的深远影响有什么?

科普小能手

API API 接口 API 测试

即梦AI推出“一句话改图”功能,助力用户发掘更多创意

Geek_2d6073

13.观察者模式设计思想

杨充

高校数字校园建设的数字身份管理难题

芯盾时代

数字身份 iam 身份和访问管理 数字化校园

如何在低代码平台中,通过模块化设计实现灵活配置,同时确保系统的高效运行?

天津汇柏科技有限公司

软件开发 低代码 模块化

一款高品质音乐播放器HQPlayer Desktop Mac版 v3.25.0免激活版

理理

【企业数字化转型】一文讲透DevOps理论体系的演进

京东科技开发者

Cinema 4D 2024 for mac(c4d 2024)三维动画制作软件

Mac相关知识分享

Premiere Pro 2023 for Mac(pr2023)中文版

Mac相关知识分享

Ultimate Vocal Remover(UVR终极人声去除器)v5.6激活版

理理

Lightroom Classic 2024 for Mac(LRC2024) 中文版

Mac相关知识分享

SignalR Core尝鲜_.NET_Geovanny Alzate Sandoval_InfoQ精选文章