译 | .NET Core 基础架构进化之路(一)

原文:Matt Mitchell

翻译:Edi Wang

随着 .NET Core 3.0 Preview 6 的推出,我们认为简要了解一下我们基础设施系统的历史以及过去一年左右所做的重大改进会很有用。

如果您对构建基础结构感兴趣,或者想要了解我们如何构建与 .NET Core 一样大的产品,那么此帖子将很有趣。它不描述应在下一个应用程序中使用的新功能或示例代码。如果您喜欢这些类型的帖子,请告诉我们。我们有几个类似计划,但希望知道此类信息是否对你有帮助。

640?wx_fmt=gif

一点历史

.NET Core 项目始于 3 多年前,与传统的微软项目相比,这是一个重大转变。

  • 在 GitHub 上公开开发

  • 多个集成在一起的独立 Git 仓库,而不是一个单独的庞大仓库

  • 面向多个平台

  • 其组件可能以多个"车辆"的形式发布(例如,Roslyn 作为 Visual Studio 和 SDK 的组件发布)

我们早期的基础设施决策是围绕必要性和权宜之计做出的。我们使用 Jenkins 进行 GitHub PR 和 CI 验证,因为它支持跨平台 OSS 开发。我们的官方版本位于 Azure DevOps(当时称为 VSTS)和 TeamCity(由ASP.NET核心使用),其中存在签名和其他关键运输基础结构。

我们使用手动更新包依赖项版本和有点自动化的 GitHub PRs 的组合将存储库集成在一起。团队独立构建了包装、布局、本地化和所有其他工具所需的工具,这些在大型开发项目中出现的任务。

虽然并不理想,但从某种意义上说,这在早期就足够有效了。随着项目从 .NET Core 1.0 和 1.1 发展到 2.0 及之后,我们希望投资一个更加集成的开发栈、更快的发布节奏和更简单的服务。我们希望生成一个新的带有最新运行时的 SDK,每天发布多次。我们希望在不降低独立存仓库的开发速度的情况下进行所有这些工作。

.NET Core 面临的许多基础结构挑战源于仓库结构的隔离、分布式性质。虽然多年来它变化很大,但该产品由 20-30 个独立 Git 仓库(ASP.NET Core 直到最近还拥有更多)组成。一方面,有许多独立的开发孤岛往往使这些孤岛的开发非常高效:开发人员可以在库中快速迭代,而不必担心技术栈的其余部分。另一方面,它使整个项目的创新和集成效率降低得多。一些示例:

  • 如果我们需要推出新的签名或打包功能,那么在使用不同工具的众多独立存储库中执行此操作的成本非常高。

  • 跨栈移动更改速度很慢且成本高昂。对于"低"位置栈中的修复和功能(例如 corefx 库)可能在几天内在 SDK(栈的"顶部")中看不到。如果我们在 dotnet/corefx 中进行修复,则必须构建该更改,并将新版本流入引用它的任何上栈组件(例如 dotnet/core 设置和ASP.NET Core),在那里将测试、提交和构建该更改。然后,这些新组件将需要将这些新输出进一步向上流,依此类推,直到达到头。

译者注:[栈] 的原文为 Stack,不是指栈数据结构,而是描述组成整个.NET Core的各种组件,它们一起,是一个栈。

在所有这些情况下,在许多层面上都有失败的机会,进一步减缓了这一进程。随着 .NET Core 3.0 规划的认真开始,很明显,如果不对我们的基础结构进行重大更改,我们就无法创建我们想要的范围的产品发布。

640?wx_fmt=gif

三管齐下的方法

我们开发了一个三管齐下的方法来减轻我们的痛苦:

  • 共享工具(又名Arcade) – 在我们的存储库中投资共享工具。

  • 系统整合 (Azure DevOps) - 抛弃 Jenkins 并拥抱集成 GitHub CI 的 Azure DevOps。将我们的官方版本从经典 VSTS 时代的流程移动到现代配置即代码。

  • 自动依赖项流和发现 (Maestro) – 显式跟踪依赖项,并快速更新它们。

Arcade

在 .NET Core 3.0 之前,有 3-5 种不同的工具实现分散在不同的仓库中,具体取决于您计数的方式。

  • 核心运行时仓库 (dotnet/coreclr, dotnet/corefx 以及dotnet/core-setup) 包含 dotnet/buildtools 工具。

  • ASP.NET核心的仓库 有 aspnet/KoreBuild

  • 使用  Repo Toolset 的各种仓库,如dotnet/symreader

  • 其他几个孤立的仓库具有独立的实现。

虽然在这个世界上,每个团队可以自定义他们的工具,并只构建他们需要的,但它确实有一些显著的缺点:

开发人员在仓库之间奔波的效率较低

示例:当开发人员从 dotnet/corefx 跑到 dotnet/core-sdk 时,存储库的"语言"是不同的。她键入什么来编译和测试?日志放在何处?如果她需要向回购中添加新项目,这是如何做到的?

每个必需的功能都被开发 N 次

示例:.NET Core 产生成吨的 NuGet 包。虽然有一些变化(例如,使用 dotnet/core-setup 生成的 Microsoft.NETCore.App 共享运行时包,与 Microsoft.AspNet.WebApi.Client 等"普通"软件包的构建方式不同),但生成它们的步骤相当类似。

遗憾的是,由于仓库的布局、项目结构等存在分歧,因此这些打包任务需要实现的方式不同。存储库如何定义应生成哪些包、这些包中的内容、其元数据等。如果没有共享工具,团队通常更容易实现另一个打包任务,而不是重用另一个打包任务。这当然对资源造成压力。

通过 Arcade,我们努力将所有仓库放在一个通用布局、仓库"语言"和任务集(如果可能的话)。这并非没有陷阱。任何类型的共享工具最终都解决了一些"金发(Goldilocks)"问题。如果共享工具过于规范,则任何重大规模的项目所需的自定义类型将变得困难,并且更新该工具变得非常困难。

使用新更新很容易破坏仓库。BuildTools 因此遭受损失。使用它的仓库与它紧密耦合,以至于它不仅不能用于其他仓库,而且在 BuildTools 中的任何更改通常以意想不到的方式使使用者崩溃。如果共享工具的规范性不够,则存储库在工具的使用上往往会出现偏差,而推出更新通常需要在每个单独的存储库中进行大量工作。在这一点上,为什么我们还需要共享工具?

Arcade 实际上尝试同时使用这两种方法。它将通用仓库"语言"定义为一组脚本(请参阅 eng/common)、通用仓库布局以及作为 MSBuild SDK 推出的通用生成目标集。选择完全采用 Arcade 的仓库具有可预测的行为,这使得更改易于跨仓库推出。不希望这样做的仓库可以从各种提供基本功能(如签名和打包)的 MSBuild 任务包中进行选择,这些功能在所有存仓库看起来都相同。当我们对这些任务进行更改时,我们会尽力避免重大更改。

让我们来看看 Arcade 提供的主要功能,以及它们如何集成到我们更大的基础架构中。

常规编译任务包

这些是 MSBuild 任务的基本层,可以独立使用,也可以作为 Arcade SDK 的一部分使用。他们是"付费才能玩"("Arcade"因此得名)。它们提供了大多数 .NET Core 仓库中所需的一组通用功能:

签名:

Microsoft.DotNet.SignTool

发布编译产物(跨仓库订阅源):

Microsoft.DotNet.Build.Tasks.Feed

打包:

Microsoft.DotNet.Build.Tasks.Packaging

常见的仓库目标和行为

这些是作为称为"Arcade SDK"的 MSBuild SDK 的一部分提供的。通过利用它,仓库选择加入默认的 Arcade 编译行为、项目和项目布局等。

通用仓库"语言"

一组使用依赖项流在所有 Arcade 存储库之间同步的通用脚本文件(稍后将介绍更多)。这些脚本文件引入了采用 Arcade 的仓库的通用"语言"。对于开发人员来说,在这些存储库之间移动变得更加无缝。此外,由于这些脚本在存储库之间同步,因此对 Arcade 存储库中的原始副本进行新更改可以快速将新功能或行为引入完全采用共享工具的存储库。

共享 Azure DevOps 作业和步骤模板

虽然定义公共存储库"语言"的脚本主要针对与人交互,但 Arcade 还有一组 Azure DevOps 作业和步骤模板,允许 Arcade 存储库与 Azure DevOps CI 系统进行接口。与常规编译任务包一样,步骤模板构成了一个基础层,几乎每个仓库都可以使用(例如,发送生成遥测)。作业模板形成更完整的单元,使存储库能够减少对 CI 流程细节的担心。

迁移到 Azure DevOps

如上所述,更大的团队在 2.2 版本中使用了 CI 系统的组合:

  • AppVeyor 和 Travis 用于 ASP.NET Core 的 GitHub PR

  • TeamCity 用于官方 ASP.NET 编译

  • Jenkins 用于其他 .NET Core 的 GitHub PR 和滚动验证。

  • 经典(非 YAML)Azure DevOps 工作流用于官方的非ASP.NET Core项目

许多区别只是为了必要性。Azure DevOps 不支持公共 GitHub PR/CI 验证,因此ASP.NET Core 转向 AppVeyor 和 Travis 来填补空白,而 .NET Core 则投资 Jenkins。经典 Azure DevOps 对构建业务流程没有很多支持,因此ASP.NET Core 团队转向 TeamCity,而 .NET Core 团队在 Azure DevOps 上构建了名为 PipeBuild 的工具来提供帮助。所有这些分歧都非常昂贵,即使在一些不明显的方式:

  • 虽然 Jenkins 是灵活的,但维护大量任务(6000-8000)是一项严肃的工作。

  • 在经典 Azure DevOps 之上构建我们自己的业务流程需要很多折衷。已检查的管道作业描述并非真正是人类可读的(它们刚刚导出了手动创建的生成定义的 json 描述),密钥管理很丑陋,在我们尝试处理生成要求的广泛差异。

  • 当正式编译与夜间(nightly)验证与 PR 验证过程在不同的系统中定义时,共享逻辑就变得困难。开发人员在进行流程更改时必须额外小心,因为很容易爆。我们在一个特殊的脚本文件中定义了 Jenkins PR 作业,TeamCity 有许多手动配置的作业,AppVeyor 和 Travis 使用自己的 yaml 格式,Azure DevOps 具有我们在它之上构建的模糊自定义系统。很容易在 PR 中更改生成逻辑并中断官方的 CI 构建。为了缓解这种情况,我们确实努力在正式 CI 和 PR 构建中通用的脚本中保留尽可能多的逻辑,但差异总是随着时间的推移而逐渐减少。某些差异(如在构建环境中)基本上不可能完全消除。

  • 更改工作流的做法差别很大,而且往往难以理解。开发人员了解了 Jenkins 用于更新 PR 逻辑的 netci.groovy 文件,但并未转换为用于正式 CI 构建的 PipeBuild json 文件。因此,对系统的知识通常被隔离到少数团队成员中,这在大型组织中并不理想。

当 Azure DevOps 开始推出基于 YAML 的构建管道,并在 .NET Core 3.0 开始启动时对公共 GitHub 项目的支持,我们认识到我们具有独特的机会。有了这种新的支持,我们可以将所有现有的工作流从单独的系统移动到现代 Azure DevOps 中,还可以对如何处理正式的 CI 和 PR 工作流进行一些更改。我们从以下工作大致概要出发:

  • 将所有逻辑保存在代码中,在 GitHub 中。随时随地使用 YAML 管道。

  • 有一个公开和私有项目。

    • 公开项目将通过 GitHub 存储库和 PR 运行所有公共 CI,正如我们始终拥有的

    • 私有项目将运行官方 CI 是我们需要进行的任何私人更改的场所,在存储库中匹配公共 GitHub 仓库

    • 只有私有项目才能访问受限制的资源。

  • 在官方 CI 和 PR 生成之间共享相同的 YAML。使用模板表达式来区分公共项目和私有项目,其中行为必须分,或者仅访问私有项目中可用的资源。虽然这通常使整个 YAML 定义更混乱一些,但这意味着:

    • 进行流程更改时,爆掉的可能性较低。

    • 开发人员只需更改一组位置来更改官方 CI 和 PR 流程。

  • 为常见任务构建 Azure DevOps 模板,以将样板 YAML 的重复降至最低,并启用使用依赖项流轻松推出更新(例如遥测)。

到目前为止,所有主 .NET Core 3.0 仓库都在 Azure DevOps 上,用于其公共 PR 和官方 CI。一个很好的例子管道是 dotnet/arcade 自己本身的官方编译/PR管道。

译者注:Arcade 自己的编译管道 https://github.com/dotnet/arcade/blob/master/azure-pipelines.yml

(文章翻译未完待续)

640?wx_fmt=jpeg


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

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

相关文章

剑指 Offer 27. 二叉树的镜像

思路:递归 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/ class Solution { public:TreeNode* mirrorTree(TreeNode*…

Codeforces Round #626 (Div. 2) D. Present 按位贡献 + 快排新姿势

传送门 文章目录题意:思路:题意: 给你一个长度为nnn的序列aaa,让你计算 n≤4e5,a≤1e7n\le 4e5,a\le 1e7n≤4e5,a≤1e7 思路: 首先这个式子是n2n^2n2的,显然不能直接算,并且异或没有分配律&…

.NET开发框架(一)-框架介绍与视频演示

本文主要介绍一套基于.NET CORE的SPA高并发、高可用的开发框架.我们暂且称它为:(让你懂.NET)开发框架。以此为主线,陆续编写教程,讲述如何构建高并发、高可用的框架。(欢迎转载与分享)它标准化了…

译 | .NET Core 基础架构进化之路(二)

原文:Matt Mitchell翻译:Edi Wang(接上篇 译 | .NET Core 基础架构进化之路(一))Maestro 及依赖流.NET Core 3.0 基础结构难题的最后一部分就是我们所说的依赖项流。这不是 .NET Core 的唯一概念。除非它们…

在.Net Core中实现一个WebSocket路由

Net Core中使用WebSocket默认是没有路由系统的,只能通过Request.Path"/xxx"来判断请求,例如:1 2 3 4 5 6 7 8 91011121314151617181920app.Use(async (context, next) >{ if (context.Request.Path "/ws") { …

Dapper介绍--Micro-ORM

一.概述目前对于.net的数据访问ORM工具很多,EF和EF Core是一个重量级的框架。最近在搭建新的项目架构,来学习一下轻量级的数据访问ORM工具Dapper。Dapper支持SQL Server,MySQL,Sqlite,SqlCE,Fir…

剑指 Offer 31. 栈的压入、弹出序列

思路&#xff1a;模拟就完事 class Solution { public:bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {stack<int> c;int idx0;for(int v:pushed){c.push(v);while(c.size()&&c.top()popped[idx]){c.pop();idx;}…

Educational Codeforces Round 84 (Rated for Div. 2) D. Infinite Path 构建环 + 思维

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 懒得写了&#xff0c;直接贴图了。 思路&#xff1a; 遇事不决画成图&#xff0c;考虑将iii向p[i]p[i]p[i]连一个边&#xff0c;可以发现每个点入度为111&#xff0c;出度为111&#xff0c;所以画出来是若…

WSL+VSCODE体验UBUNTU环境下的开发

首先安装 WSL&#xff0c;我这里选择的是 ubuntu18.04 这个应用。切换 WSL 的默认用户为 root 用户切换成 root 用户主要是避免后续开发中遇到权限问题比较麻烦&#xff0c;直接默认 root 解决问题。找到ubuntu安装目录&#xff0c;一般在C:\Program Files\WindowsApps\Canonic…

程序员修仙之路--优雅快速的统计千万级别uv

菜菜&#xff0c;咱们网站现在有多少PV和UV了&#xff1f;Y总&#xff0c;咱们没有统计pv和uv的系统&#xff0c;预估大约有一千万uv吧写一个统计uv和pv的系统吧网上有现成的&#xff0c;直接接入一个不行吗&#xff1f;别人的不太放心&#xff0c;毕竟自己写的&#xff0c;自己…

P3391 【模板】文艺平衡树 fhq-treap 模板

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 这是学splaysplaysplay的时候的一个模板题&#xff0c;之前学过fhq−treapfhq-treapfhq−treap&#xff0c;但是没怎么用他做过题&#xff0c;今天一做还发现不少问题&#xff0c;真是眼高…

123. 买卖股票的最3佳时机 III

思路&#xff1a;动态规划&#xff0c; dp1是当前第一次买入的最大值 dp2是当前第一次卖出的最大值 dp3是当前第二次买入的最大值 dp4是当前第二次卖出的最大值 转移看代码&#xff1a; class Solution { public:int maxProfit(vector<int>& prices) {int dp1-…

微软正式开源WSL 2的内核源码

微软在今年5月举办的 Build 2019 上宣布了第二代 Windows 的 Linux 子系统 —— WSL 2。与第一代相比&#xff0c;WSL 2 重新设计了架构&#xff0c;使用真正的 Linux 内核&#xff0c;支持在 Windows 上运行 ELF64 Linux 二进制文件。按照计划&#xff0c;WSL 2 的初始版本已于…

【NOI2016】优秀的拆分【后缀数组】【ST表】【关键点】【调和级数复杂度】【差分】

传送门 题意&#xff1a;如果一个字符串可以拆分为AABB的形式&#xff0c;其中A和B是任意非空字符串&#xff0c;则我们这种拆分是优秀的。求给定串的所有子串的拆分方案数之和。 N≤30000N \leq30000N≤30000 本来是个神仙题 但明明一个O(nlogn)O(nlogn)O(nlogn)的题为啥只…

剑指 Offer 43. 1~n 整数中 1 出现的次数(数位dp)

思路&#xff1a;就是数位dp&#xff0c;dp[idx][sum][limit]代表&#xff0c;到idx位&#xff0c;前面有sum个0&#xff0c;有没有limit限制&#xff1b; class Solution { public:int dp[20][50][2];int len; int pos[20];int countDigitOne(int n) {for(int i0;i<15;i)f…

Windows新终端中玩转ASCII和Emoji游戏的正确姿势

前一段时间&#xff0c;我搬运了几个Windows Terminal中玩游戏的视频.Windows Terminal - 动图GIF作背景图Windows Terminal - 母牛说HiWindows Terminal - 字符水族箱今天我来给大家展示一下具体的玩法~Emoji版双人碰碰球目前有个现成的 .NET core 项目可以直接用&#xff0c;…

剑指 Offer 38. 字符串的排列(有重复元素的排列)

思路&#xff1a;dfs 暴搜 class Solution { public:vector<string> permutation(string s) {vector<string> a;function<bool(char,int,int)> check[&](char x,int st,int idx){if(st>idx) return false;for(int ist;i<idx;i) if(s[i]x) return …

nowcoder Forsaken的数列 fhq-treap

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 很明显的数据结构了&#xff0c;splaysplaysplay当然能写&#xff0c;但是fhq−treapfhq-treapfhq−treap更加简洁易懂。 考虑第一个操作&#xff0c;无非就是分裂出[1,pos−1][1,pos-1][1…

我与微软的不解之缘 - 我的Insider Dev Tour 2019讲师之旅

作者&#xff1a;Lamond Lu大家好&#xff0c;我是陆楠&#xff0c;来自北京盛安德科技发展有限公司青岛分公司&#xff0c;今年非常有幸作为讲师参加了微软Insider Dev Tour烟台站的活动&#xff0c;我主讲了如何使用最新的微软开发工具开发调试NodeJS项目。与微软结缘与大多数…

.NET Core 3.0之深入源码理解Kestrel的集成与应用(一)

写在前面ASP.NET Core 的 Web 服务器默认采用Kestrel&#xff0c;这是一个基于libuv(一个跨平台的基于Node.js的异步I/O库)的跨平台、轻量级的Web服务器。在开始之前&#xff0c;先回顾一下.NET Core 3.0默认的main()方法模板中&#xff0c;我们会调用Host.CreateDefaultBuilde…