01
6

用Windbg来分析.Net程序的dump

0
归档:2020年6月分类:C#和.NET

用Windbg来分析.Net程序的dump

1. 什么是Windbg

WinDbg是微软发布的一款相当优秀的源码级(source-level)调试工具,可以用于Kernel模式调试和用户模式调试,还可以调试Dump文件。
WinDbg是微软很重要的诊断调试工具: 可以查看源代码、设置断点、查看变量, 查看调用堆栈及内存情况。
Dump文件是进程的内存镜像, 可以把程序的执行状态通过调试器保存到dump文件中

2. Windbg可以解决以下问题

◆ 内存高
◆ CPU高
◆ 程序异常
◆ 程序Hang死

3. 使用windbg进行调试分析的两种方式

使用windbg调试器attach到需要调试的进程。(会暂停进程的运行)
抓取进程的dump文件,使用windbg分析dump

下载地址:https://technet.microsoft.com/en-us/sysinternals/dd996900

下载之后为压缩包,将文件解压。通过dos命令来生成dump文件。下面为一条语句为示例

procdump -ma -c 0 -s 3 -n 2 sqlservr.exe -o E:\dumps\

这条语句的意思为:当sqkservr.exe这个进程运行时间cpu的占用超过0%,时间超过3秒,则在E:\dumps下生成一个dump文件。一直到生成2个为止。下面为命令介绍

-ma 生成full dump, 即包括进程的所有内存. 默认的dump格式包括线程和句柄信息.
-c 在CPU使用率到达这个阀值的时候, 生成dump文件.
-s CPU阀值必须持续多少秒才抓取dump文件.
-n 在该工具退出之前要抓取多少个dump文件.
-o dump文件保存目录.
Sqlservr.exe可替换为进程的ID

01
6
以下文章来源于DotNET技术圈 ,作者RICKY LEEKS

1.小对象怎么处理的?

小型.NET对象被分配到小型对象堆(SOH)上。其中有3种:第0代,第1代和第2代。对象根据其寿命向上移动。

将新对象放在Gen 0上。当Gen 0充满时,.NET垃圾收集器(GC)运行,处理不再需要的对象,并将其他所有内容移至Gen1。如果Gen 1充满,则GC再次运行,也可以将Gen 1中的对象移动到Gen 2中。

当Gen 2变满时,将发生GC完全运行。这将清除不需要的Gen 2对象,将Gen 1对象移至Gen 2,然后将Gen 0对象移至Gen 1,最后清除所有未引用的内容。每次运行GC之后,都会压缩受影响的堆,以将仍在使用的内存保持在一起。

这种代代相传的方法可确保事情高效运行-耗时的压缩过程仅在绝对必要时才会发生。

注意:如果您在Gen 2中看到大量的内存,则表明内存已被保留很长时间,并且可能存在内存问题。这是内存分析工具可以派上用场的地方。

2.较大的对象会怎样?

大于85 KB的对象被分配到大对象堆(LOH)。由于复制大块内存的开销,它们没有被压缩。当发生完整的GC时,未使用的LOH对象的地址范围将记录在可用空间分配表中。

分配新对象后,将在此可用空间表中检查足以容纳该对象的地址范围。如果存在,则将对象分配到那里,如果不存在,则将对象分配到下一个可用空间。

由于对象不太可能是空地址范围的确切大小,因此对象之间几乎总是会留有小块内存,从而导致碎片。如果这些块小于85 KB,则根本没有重用的可能性。因此,随着分配需求的增加,即使碎片空间仍然可用,也会保留新的段。

此外,当需要分配大对象时,.NET还是倾向于将对象附加到末尾,而不是运行昂贵的Gen 2 GC。这对性能有好处,但是是导致内存碎片的重要原因

3.垃圾收集器可以在不同的模式下运行以优化性能

.NET通过为GC提供多种模式来解决性能与堆效率之间的权衡问题。

工作站模式为用户提供了最大的响应速度,并减少了由于GC造成的暂停。它可以作为“并发”或“非并发”运行,指的是运行GC的线程。默认值为并发,它为GC使用单独的线程,因此应用程序可以在GC运行时继续执行。

服务器模式可为服务器环境提供最大的吞吐量,可伸缩性和性能。在服务器模式下,段大小和生成阈值通常比工作站模式大得多,这反映了对服务器的更高要求。

服务器模式在多个线程上并行运行垃圾回收,为每个逻辑处理器分配一个单独的SOH和LOH,以防止线程相互干扰。

.NET框架提供了一种交叉引用机制,因此对象仍然可以在堆之间相互引用。但是,由于应用程序响应能力不是服务器模式的直接目标,因此在GC期间,所有应用程序线程都将被挂起。

4.引用不足会在性能和内存效率之间折衷

弱对象引用了GC根的替代来源,使您可以保留对象,同时在GC需要时可以收集对象。它们是代码性能和内存效率之间的折衷。创建对象需要占用CPU时间,但保持加载状态需要占用内存。

弱引用特别适用于大型数据结构。例如,假设您有一个允许用户浏览大型数据结构的应用程序,他们可能会返回其中的一些数据。您可以将任何强引用转换为他们浏览的结构为弱引用。如果用户返回到这些结构,则可以使用它们,但如果没有,GC可以根据需要回收内存。

5.对象固定可以创建在托管和非托管代码之间传递的引用

.NET使用一种称为GCHandle的结构来跟踪堆对象。GCHandle可用于在托管域和非托管域之间传递对象引用,.NET维护一个GCHandles表以实现此目的。GCHandle有四种类型,包括固定的,用于将对象固定在内存中的特定地址。

对象固定的主要问题是它可能导致SOH碎片化。如果将对象固定在GC期间,则根据定义,该对象无法重定位。根据您使用固定的方式,它会降低压缩的效率,在堆中留下间隙。避免这种情况的最佳策略是在很短的时间内锁定,然后释放。

15
10

看完Orchard Core的一些随感

0
归档:2019年10月分类:C#和.NET

这个项目的框架是微软顶级工程师的杰作,太牛了,终于理解为什么各类开发IDE都是海外工程师写出来了,框架设计优美、代码精炼、扩展性超强、考虑全面。ps:国内IT软件方面至少落后欧美10年(那些把开源改改就说国产世界第一就别拿来丢脸了),得益于开源和github,我们才有机会保持这个差距。

国内对C#和.NET总是有一些误解。比如.NET Core,海外已经用得如火如荼了,社区非常活跃,并且各种新的开发理念和工具极大提高效率。我在BAT写过代码,说实话他们的软件架构理念都是实用为主,并没有从工程学角度去发展,所以只适合特点场景,但是他们垄断中国码农的话语权,导致后来者c#大都被误解。这无形中导致国内软件开发起码落后海外10年。举一个最简单例子,现在大公司也有devops,但是放到中小型公司,他们很难运用bat的这套东西,而阿里云对devops支持太差了,反观aws和azure几乎是完美支持,这里面有国情原因,也有c#被误解的原因。

我可以毫无客气地说:国内对.net的误解,其实就是落后又自大的一个缩影。看看现在华为、国产软件时不时世界第一的“亩产万金”的论断。

10
10

历史版本

C#作为微软2000年以后.NET平台开发的当家语言,发展至今具有17年的历史,语言本身具有丰富的特性,微软对其更新支持也十分支持。微软将C#提交给标准组织ECMA,C# 5.0目前是ECMA发布的最新规范,C# 6.0还是草案阶段,C# 7.1是微软当前提供的最新规范。

这里仅仅列个提纲,由于C# 5.0是具有ECMA标准规范的版本,所以选择C# 5.0作为主要版本学习,并专题学习C# 6.0,7.0版本新特性。

C#语言规范GitHub库参见:https://github.com/dotnet/csharplang

C#语言路线图及开发中的特性参见:
https://github.com/dotnet/roslyn/blob/master/docs/Language%20Feature%20Status.md

语言版本 发布时间 .NET Framework要求 Visual Studio版本
C# 1.0 2002.1 .NET Framework 1.0 Visual Studio .NET 2002
C# 1.1\1.2 2003.4 .NET Framework 1.1 Visual Studio .NET 2003
C# 2.0 2005.11 .NET Framework 2.0 Visual Studio 2005
C# 3.0 2007.11 .NET Framework 2.0\3.0\3.5 Visual Studio 2008
C# 4.0 2010.4 .NET Framework 4.0 Visual Studio 2010
C# 5.0 2012.8 .NET Framework 4.5 Visual Studio 2012\2013
C# 6.0 2015.7 .NET Framework 4.6 Visual Studio 2015
C# 7.0 2017.3 .NET Framework 4.6.2 Visual Studio 2017
C# 7.1 2017.6 .NET Framework Visual Studio 2017 v15.3预览版
C# 8.0 待发布 .NET Framework 4.7.1 Visual Studio 2017 v15.7

C# 1.0 特性

第1个版本,编程语言最基础的特性。

  • Classes:面向对象特性,支持类类型
  • Structs:结构
  • Interfaces:接口
  • Events:事件
  • Properties:属性,类的成员,提供访问字段的灵活方法
  • Delegates:委托,一种引用类型,表示对具有特定参数列表和返回类型的方法的引用
  • Expressions,Statements,Operators:表达式、语句、操作符
  • Attributes:特性,为程序代码添加元数据或声明性信息,运行时,通过反射可以访问特性信息
  • Literals:字面值(或理解为常量值),区别常量,常量是和变量相对的

C# 2特性 (VS 2005)

  • Generics:泛型
  • Partial types:分部类型,可以将类、结构、接口等类型定义拆分到多个文件中
  • Anonymous methods:匿名方法
  • Iterators:迭代器
  • Nullable types:可以为Null的类型,该类可以是其它值或者null
  • Getter/setter separate accessibility:属性访问控制
  • Method group conversions (delegates):方法组转换,可以将声明委托代表一组方法,隐式调用
  • Co- and Contra-variance for delegates and interfaces:委托、接口的协变和逆变
  • Static classes:静态类
  • Delegate inference:委托推断,允许将方法名直接赋给委托变量

C# 3特性 (VS 2008)

  • Implicitly typed local variables:
  • Object and collection initializers:对象和集合初始化器
  • Auto-Implemented properties:自动属性,自动生成属性方法,声明更简洁
  • Anonymous types:匿名类型
  • Extension methods:扩展方法
  • Query expressions:查询表达式
  • Lambda expression:Lambda表达式
  • Expression trees:表达式树,以树形数据结构表示代码,是一种新数据类型
  • Partial methods:部分方法

C# 4特性 (VS 2010)

  • Dynamic binding:动态绑定
  • Named and optional arguments:命名参数和可选参数
  • Generic co- and contravariance:泛型的协变和逆变
  • Embedded interop types (“NoPIA”):开启嵌入类型信息,增加引用COM组件程序的中立性

C# 5特性 (VS 2012)

  • Asynchronous methods:异步方法
  • Caller info attributes:调用方信息特性,调用时访问调用者的信息

C# 6特征 (VS 2015)

  • Compiler-as-a-service (Roslyn)
  • Import of static type members into namespace:支持仅导入类中的静态成员
  • Exception filters:异常过滤器
  • Await in catch/finally blocks:支持在catch/finally语句块使用await语句
  • Auto property initializers:自动属性初始化
  • Default values for getter-only properties:设置只读属性的默认值
  • Expression-bodied members:支持以表达式为主体的成员方法和只读属性
  • Null propagator (null-conditional operator, succinct null checking):Null条件操作符
  • String interpolation:字符串插值,产生特定格式字符串的新方法
  • nameof operator:nameof操作符,返回方法、属性、变量的名称
  • Dictionary initializer:字典初始化

C# 7 特征 (Visual Studio 2017)

  • Out variables:out变量直接声明,例如可以out in parameter
  • Pattern matching:模式匹配,根据对象类型或者其它属性实现方法派发
  • Tuples:元组
  • Deconstruction:元组解析
  • Discards:没有命名的变量,只是占位,后面代码不需要使用其值
  • Local Functions:局部函数
  • Binary Literals:二进制字面量
  • Digit Separators:数字分隔符
  • Ref returns and locals:引用返回值和局部变量
  • Generalized async return types:async中使用泛型返回类型
  • More expression-bodied members:允许构造器、解析器、属性可以使用表达式作为body
  • Throw expressions:Throw可以在表达式中使用

C# 7.1 特征 (Visual Studio 2017 version 15.3)

  • Async main:在main方法用async方式
  • Default expressions:引入新的字面值default
  • Reference assemblies:
  • Inferred tuple element names:
  • Pattern-matching with generics:

C# 8.0 特征 (Visual Studio 2017 version 15.7)

  • Default Interface Methods 缺省接口实现
  • Nullable reference type NullableReferenceTypes 非空和可控的数据类型
  • Recursive patterns 递归模式
  • Async streams 异步数据流
  • Caller expression attribute 调用方法表达式属性
  • Target-typed new
  • Generic attributes 通用属性
  • Ranges
  • Default in deconstruction
  • Relax ordering of ref and partial modifiers
01
10

微信图片_20191014143438

这几天把.NETCONF所有的视频都看了一遍,感叹微软的开放力度和对C#及.NET的推进力度。我写过Delphi、VB、C、C++、C#、Object-C、PHP、JAVA、Python,这一路过来总觉得太分散了,很难真正投入到推动一门语言的地步,现在看来,C#是初恋也是最爱,真不能在乱“移情别恋”了。未来最多使用Python进行一些ML(机器学习)涉及的方面的数据工作。

微信图片_20191014143444

针对C#和.NET我一定持续关注,而且专注于她,这是工作需要,也是自己的发展需要。

1、全面梳理C#这门语言,虽然我已经写了12年C#了。

实际行动:把微软的文档看一遍,把那几本经典的书读完。(2019到2020)

2、应用.NET Core 3.0及未来版本。

实际行动:把公司的一个项目做成兼容linux平台。(2019)

3、研究Orchard Core项目。

实际行动:把自己的blog都迁移过来。(2019到2020)

4、研究ABP项目,顺便研究微服务架构和DDD结构。

实际行动:在公司的一个项目中完成。(2019到2020)

5、机器学习ML.NET研究和Python

实际行动:自己建模完成一些数据分析和预测。(2019起步,长期工作)

6、学习Azure、AWS技术和DevOps

实际行动:考下AWS证书,并在公司一个项目中完成Azure的DevOps。(2020到2021)

另外,学习nopCommerce和CSLA .NET两个开源项目的特点,吸收特长;学习Angular和Vue前端框架。

25
9

.NET Core 3.0 正式公布

0
归档:2019年9月分类:C#和.NET

根据微软博客的介绍,开发者可以面向 Windows、MacOS 以及 Linux 等系统平台下载 .NET Core 3.0 

此外, ASP .NET Core 3.0  与  EF Core 3.0  也已经一同发布。 Visual Studio 2019 16.3  与  Visual Studio for Mac 8.3  亦同时发布,且需要更新才能确保 .NET Core 3.0 与 Visual Studio 的协同使用。.NET Core 3.0 为 Visual Studio 2019 16.3 中的组成部分,开发者可以选择直接升级至 Visual Studio 2019 16.3,从而立刻获取 .NET Core。

感谢所有为 .NET Core 3.0 做出贡献的朋友们!此次最新版本的发布源自数百位团队成员的努力,也包括技术社区的重大贡献。

发行说明:

3.0 版本,开发者需要了解什么?

在深入探究 .NET Core 3.0 中的全部新功能之前,我们首先需要强调几项关键性的改进与指导内容。以下是整理出的要点清单:

  • .NET Core 3.0 已经在 dot.net 以及 Bing.com 上托管了几个月,通过了一系列严苛的测试。众多其他微软团队也将很快在生产流程当中通过 .NET Core 3.0 部署一系列大型工作负载。
  • 多种组件的性能得到显著提升,感兴趣的朋友可以点击此处参阅 .NET Core 3.0 的性能改进说明。
  • C# 8 加入了异步流、范围 / 索引、更多模式以及可为空的引用类型。可为空意味着可以直接发现那些导致 NullReferenceException 问题的代码缺陷。框架库的最底层注释也已添加完成,以帮助了解何时为 Null。
  • F# 4.7 致力于通过隐式 yield 表达式及相关语法降低某些操作的实现难度。其中还包含对 LangVersion 的支持,提供 nameof 并可以预览形式打开静态类。F# Core 核心库现在还与 .NET Standard 2.0 相匹配。您可以点击此处参阅 F# 4.7 发布公告中的细节信息。
  • .NET Standard 2.1 增加了可与 .NET Core 以及 Xamarin 共同使用的代码类型集。.NET Standar 2.1 当中包含 .NET Core 2.1 以及之后版本中的所有类型。
  • Windows 桌面应用现已面向 Windows Forms 与 WPF(开源)得到 .NET Core 支持。其中,WPF 设计器为 Visual Studio 2019 16.3 版本中的组成部分。Windows Forms 设计器仍处于预览状态,并可通过 VSIX 下载的形式获取。
  • .NET Core 应用现在默认具备可执行文件。在以往的发行版中,应用需要通过 dotnet 命令方启动,例如 dotnet myapp.dll。现在,可以通过应用特定可执行文件实现应用启动,例如 myapp 或者./myapp,具体视使用的操作系统而定。
  • 高性能 JSON API 加入新版本,适用于 reader/writer、对象模型以及序列化场景等。这些 API 在 Span基础之上重新构建而成,且在底层使用 UTF8(而非 string 等 UTF16)。这些 API 能够将分配需求控制在最低程度,从而提高性能、减少垃圾收集器的工作量。具体请参阅.NET Core 3.0 中的 JSON 未来发展说明。
  • 默认情况下,垃圾收集器的内存占用量得到了显著削减。对于将众多应用程序托管在同一服务器之上的使用场景,这项改进可谓意义重大。垃圾收集器本身也得到了更新,能够利用 64 核及以上设备的大量计算核心。
  • .NET Core 已针对 Docker 进行了增强,以使 .NET 应用程序能够在容器中以可预测的方式高效运作。在容器配置中的内存或 CPU 资源有限时,目前的垃圾收集器与线程池更新结果也能带来更好的运作效果。.NET Core docker 镜像也变得更小,其中 SDK 镜像的瘦身效果尤其明显。
  • Raspberry Pi 与 ARM 芯片现已得到支持,可配合远程 Visual Studio 调试程序等工具实现物联网开发。开发者可部署应用以监听各传感器,同时将消息或者图像输出至显示器上,整个过程皆可通过新的 GPIO API 实现。ASP.NET 则可用于将数据公布于 API 或者以站点的形式对物网设备进行配置。
  • .NET Core 3.0 即为“当前”版本,我们计划在 2019 年 11 月推出下一代 .NET Core 3.1 版本。.NET Core 3.1 将为长期支持(LTS)版本(周期至少为 3 年)。我们建议您首先采用 .NET Core 3.0,而后更新至 3.1 版,升级过程将非常轻松。
  • .NET Core 2.2 将于今年 12 月 23 日停止服务,具体情况请参阅 .NET Core 支持策略
  • .NET Core 3.0 将通过 RHEL 8 的红帽 Applicaltion Streams 交付,这也是我们与红帽公司多年合作的最新成果。
  • 对于希望在 Windows 上使用 .NET Core 3.0 的用户,将必须升级至 Visual Studio 2019 16.3。
  • 对于希望在 Mac 上使用 .NET Core 3.0 的用户,将必须升级至 Visual Studio for Mac 8.3。
  • Visual Studio Code 用户应始终使用最新版本的 C#扩展,以确保能够正常支持最新方案,包括与 .NET Core 3.0 的匹配。
  • .NET Core 3.0 的 Azure App Serivce 部署目前正在进行当中。
  • .NET Core 3.0 的 Azure Dev Ops 部署即将推出。我们将在准备就绪之后发布更新。

平台支持

.NET Core 3.0 将在以下操作系统平台上得到支持:

  • Alpine: 3.9+
  • Debian: 9+
  • openSUSE: 42.3+
  • Fedora: 26+
  • Ubuntu: 16.04+
  • RHEL: 6+
  • SLES: 12+
  • macOS: 10.13+
  • Windows Client: 7, 8.1, 10 (1607+)
  • Windows Server: 2012 R2 SP1+

备注:Windows Forms 与 WPF 应用只适用于 Windows 操作系统。

芯片支持情况:

  • x64,Windows、macOS 以及 Linux
  • x86,Windows
  • ARM32,Windows 与 Linux
  • ARM64,Linux (kernel 4.14+)

备注:请确保 .NET Core 3.0 ARM64 部署方案采用 Linux 内核 4.14 或者更新版本。例如,Ubuntu 18.04 能够满足这一条件,但 16.04 版本无法支持。

19
7

几个月前,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的联合组织者。

17
7

关于.NET Core情况

0
归档:2019年7月分类:C#和.NET

这几天终于抽出空啦看微软的Build 2019,看到.NET Core 3.0及相关技术的详细介绍,并且了解.NET的未来规划,感叹微软的伟大。

微信图片_20190717093004

上图是.NET Core的web开发部分。

微信图片_20190717092956

 

上图是.NET 平台大统一设计。

微信图片_20190717093000

上图是.NET未来的Roadmap。

 

这就叫做技术领域的一统江湖。二十年前很多人嘲笑Windows的垄断和Microsoft的不开源(现在依然有老古董如此认为),如今,Sun寄人篱下并且靠着Java专利法残喘苟活。我一直觉得微软汇聚的是软件世界最优秀的工程师,这些人要是投身开源代码,那是很恐怖的,短短不到5年时间,微软现在成为开源代码贡献最大的公司。

如今.NET Core 在海外若日中天,当国内经济持续下滑,人力成本持续提高,你们就会想到用.NET Core了,一个公司只需要三名码农就可以搞定后端、前端、移动开发。不过很可惜,公司的人力部门都听CTO的,而这群Java养出来的没落贵族是不会自己变革的。 ​​​​

公告栏

欢迎大家来到我的博客,我是dodoro,希望我的博客能给你带来帮助。