行业

技术

组织规模

大型(1,000-9,999 名员工)

国家/地区

美国

技术

ASP.NET Core Modernization

公司

Microsoft Teams (Microsoft)

Microsoft Teams “MiddleTier” 是支持 Microsoft Teams 中各种方案的内部服务。它由 700 多个 API 组成,是由 Microsoft 的 10 多个团队维护的最大服务之一。在过去两年中,此服务下的 50 多个项目(库、测试、应用程序)已转换到 .NET Standard 2.0 和 .NET Core 3.1,具有等效的功能和性能(或更佳),目前在生产中已几乎完全在 .NET Core 3.1 上运行,并且希望下一步移动到 .NET 6。在此迁移之前,该服务使用 ASP.NET Core 2.2 MVC 管道在 .NET Framework 4.6.2 上运行。它在 Azure Service Fabric上运行,并且部署在 35 个 Azure 数据中心。

此迁移的范围很大,因为就它每天为数百名使用它的开发人员提供的功能而言,MiddleTier 是一项超大型服务。

迁移动机

团队乐于迁移到 .NET Core 3.1 的原因如下:

  • 性能和成本效益提升
  • .NET Framework 4.6.2 可能即将过期
  • 跨平台支持
  • 迁移到新式框架以获得更好的开发人员体验

迁移到 .NET Core 3.1 后的好处

迁移到 .NET Core 3.1 后,团队已注意到以下改进:

  • 25% 的 CPU 改进
  • 降低了大约 25% 的基础结构成本
  • 改进的线程池使用率
  • 降低了向年度 .NET 版本迁移的技术债务和工作量

下图显示了 .NET Framework 与 .NET Core 之间的比较。将来在 .NET 6 上,我们应会看到进一步的改进。

显示 .NET Framework 上 CPU 峰值为 57% 和 .NET Core 上 CPU 峰值为 42% 的图表
CPU 比较
显示迁移到 .NET Core 后忙碌工作线程减少的图表
忙碌工作线程比较
显示迁移到 .NET Core 后忙碌 IO 线程减少的图表
忙碌 IO 线程比较

方法

整体迁移分为三个阶段进行:

显示三个迁移阶段的活动(准备、执行、验证和推出)的图形

他们还选择将应用程序设置为多目标,即同时面向 .NET Framework 和 .NET Core,以便可以同时拥有两个可用的二进制文件,并且可以继续缓慢推出 .NET Core。

学习

OData 和其他 REST API 无法共享路由前缀

他们的服务包含几个 OData 终结点以及许多 REST 终结点。这两类终结点共享相同的路由前缀。此设置过去在 .NET Framework 中可正常工作,但由于路由更改,已停止在 .NET Core 中正常工作。他们必须将 OData API 移动到其他路由前缀才能解决此问题。

OData 客户端库的性能问题

与 .NET Framework 相比,使用 OData 客户端调用下游 OData API 的 HttpWebRequest 模式会导致更高的延迟。这是由于框架不缓存连接的 .NET Core 中的回归所导致的。此问题已在较新版本的 .NET 中得到解决。

Azure 服务总线 SDK 问题

作为此迁移的一部分,必须升级 Azure 服务总线 SDK,因为旧版本与 .NET Standard 不兼容。最新版本的 Azure 服务总线 SDK 以 JSON 格式发送请求有效负载,而旧版 SDK 则以 XML 格式发送有效负载。要继续使用 XML 有效负载,必须使用 DataContractSerializer

适用于多目标的 Service Fabric 项目中的问题

Service Fabric 项目(sfproj)本身不支持多目标。他们必须在生成管道中执行解决方法,以便为两个目标框架生成 Service Fabric 包。

关于较旧版本的 MimeKit NuGet 的问题

较旧版本的 MimeKit 可能遇到与双字节字符有关的问题,因此在此方案中建议进行特定于语言的验证。他们在向位于亚洲地区的部署推出时发现了类似的问题。

经典 ASP.NET 奇妙之处

  • 必须移除在 .NET Core 中标记为内部的某些 .NET Framework 类的用法。
  • 面向版本 3.0 和 3.1 的 ASP.NET Core 重大更改一文所述,已从操作名称中删除 MVC 异步后缀。如果任何代码路径依赖于操作名称,则可能导致行为发生更改。
  • 默认情况下,从 .NET Core 3.0 开始,已在所有服务器上禁用同步 IO 操作,如 dotnet/aspnetcore#7644 GitHub 问题中所述。
  • 将 StreamContent 作为 HTTP 请求内容发送时,Content.Headers 中未设置 Content-Length 标头。这可能会导致下游调用出错。
  • .NET framework 为字符串生成稳定的哈希代码,但 .NET Core 则不会生成。
  • System.ComponentModel.DataAnnotations 命名空间中的必需属性在 .NET Core 中的行为会有所不同。在 .NET Framework 上,此属性不会对非 null 字段执行任何模型验证,但在 .NET Core 上则会执行此操作。

未来

每个新版本的 .NET 都附带了重大的生产力和性能改进,可继续帮助实现我们构建可复原、可缩放、高性能和安全服务的目标。团队下一步将通过升级到 .NET 6 来继续利用 .NET 中所做的改进。