SoundCloud成立于13年前,在整个历史中,公司和大部分技术堆栈都发生了变化。我们在Rails App上启动了一条单片Ruby,从那时起,已致力于在许多子系统中提取,隔离和重用逻辑,然后在单独的微服务中。我们介绍了不同的存储机制和数据分析应用程序,我们的前端也从一个网站扩展到移动应用程序,与第三方,Xbox App,最近,一个全面的PWA在Chromebook上运行。
在内部,我们致力于为团队提供灵活性,以便选择他们认为最佳的任何工具,以解决日常产品开发的技术挑战。虽然这意味着很多自主权,但它也逐渐导致了一种碎片化的语言环境,框架,流程和文件,使跨团队协作和更难的责任。因此,我们迁移到提供合理的建议和支持自以为是的技术堆栈的模型。
从那种意义上讲,复杂性变得选择:通常,图书馆,工具和工程经验应该尽可能简单,没有配置,但仍然可以提供支持更复杂的用例的能力,具体取决于每个问题 - 要求放。
随着时间的推移,我们在我们解决方案的实施中确定了一些相似之处和自然反复选择。与给定的编程语言经验丰富的工程师更有可能为以下项目选择相同的可能性,而在其他情况下,可以易于集成,例如,特定数据库引擎推动了一个新的集成商再次决定。我们建立了更多的工具来帮助开发这些现有应用程序,我们写了更多的文档来涵盖框架的“Gotchas”,标准旨在组织系统架构等。
这个周期结束了将一些想法巩固为我们的不同学科的事实上的金色路径:Web开发,数据科学,基础设施等。工程师仍然可以自由选择他们的工具,但积极的加固周期已经设置:为什么花费时间重新调整时间批量已经投资建设成熟解决方案时?从金色的道路漂移存在,但他们越来越少见。
我们解决了在JVM生态系统中开发后端应用程序的金色路径。自从采用Twitter的FinAgle以来,我们还在顶部建立了广泛的工具,达到了我们可以推导出我们自己的库的一点:JVMKIT,它可以通过提供SoundCloud中使用的常用模式和协议的实现来帮助构建JVM应用程序。
由于Scala应用程序在SoundCloud构成绝大多数应用程序,因此JVMKIT主要针对它们,但也可以从任何其他JVM语言中使用 - 我们目前在Java,Clojure和JRuby中运行服务,即使我们也是如此试着把每个人都转向金色的道路。它提供了选择加入模块,用于提供BFF的形式的公共流量,以及与MySQL,Kafka,Memcached和Prometheus的集成以及其他功能。
通过如此广泛的受众,JVMKIT在很大程度上采用了SoundCloud,它毫不奇怪,它为我们提供的用户提供了大部分 - 它成为我们技术堆栈的关键组成部分,具体取决于它和数千个实例在生产中运行它。
JVMKIT的存储库看到了活动开发:新功能,错误修复和安全补丁每周都在陆地。与开源项目不同,在SoundCloud组织中,依赖项目未知,我们可以在所有呼叫者到JVMKIT的API上绘制透明视图,以查看哪个应用程序滞后在后面,并且在直线边缘上。
然后我们问自己:我们究竟如何确保整个公司遵循JVMKIT的发布周期?作为JVMKIT的平台团队和开发人员,我们希望将每个团队路线图的中断最大限度地减少到新的API,更换不推荐的电话等。换句话说,我们希望尽量减少我们在同事板上的工作他们可以专注于更高的目标。为此,我们建立了自动化,在我们所有的存储库中执行批量升级 - 理想情况下,JVMKIT升级完全透明。
要确定哪些服务取决于JVMKIT,我们依靠这些关系在代码中明确定义。作为前面引入了后端开发的Golden Path概念的一部分,我们通过构建脚本来执行每个存储库的一部分。对于我们的大部分后端存储库,这意味着SBT构建。相同的想法适用于我们的其他金色路径,Android和NPM的Gradle和Web开发,例如:
Val JVMKitVersion =" 15.0.0" Lazy Val Root =(文件中的项目("。")).settings(librarydependencies ++ = seq(" com.soundcloud"%" jvmkit-admin-server& #34;%JVMKitVersion," com.soundcloud"%%" jvmkit-json-play"%jvmkitversion,......))
每五分钟,通过Zoekt,基于正则表达式匹配的搜索引擎索引GitHub上托管的每个SoundCloud的存储库的默认分支的更改(阅读本文以获取更多深度信息)。在框中,Zoekt提供了一个Web界面,然后我们可以为所有SoundCloud工程师提供,但我们还添加了通过简单的脚本API查询结果的能力。
通过组合Zoekt的API和整个公司的标准化,我们可以写一个简单的查询,如" JVMKIT" f:^ build.sbt并列出依赖于jvmkit的所有项目 - 换句话说,我们可以构建一个级别的反向依赖图,其中jvmkit是根。
使用这些工具手头,我们现在可以开始思考我们如何自动验证JVMKit的更改是否与其他项目顺利集成。为此示例,让我们参加名为喜欢的任意服务,这负责管理哪些跟踪SoundCloud上的用户。如果我们要在伪代码中编写这样的算法,这就是它的样子:
1.克隆"喜欢" repository2。查找build.sbt3。用最新的快照4替换JVMkitVersion值。编译"喜欢"
如果算法中的所有步骤成功,我们保证了JVMKIT的新变化并没有引起类似的原因。我们可以概括这个想法并运行我们之前被标识为JVMKIT的反向依赖性的每个存储库的算法 - 如果所有这些都成功,新的JVMKIT版本很好!
我们的工作完成了,对吗?并不真地。还有其他情况下我们的算法目前不考虑。例如,如果我们计划释放JVMKIT版本并在那一天巧合,那么喜欢的原因,那么喜欢的主要分支被打破了?我们不应该认为这是jvmkit版本的凹凸的错。要隔离那种情况,我们可以编译目标项目,没有任何变化。
如果JVMKIT的新版本引入了一个API破坏性的变化,因此需要一个主要版本颠簸 - 我们允许通过直接在存储库源代码中直接执行一组处理器来自动修复升级的工程师自动修复破坏性更改,awk,棉绒等。
现在,当发布是一个次要或补丁变化时,肯定是项目将编译,但它表现得像我们期望的?我们还应该运行自动测试,以确保我们的业务逻辑仍仍然尊重。这里再一次,我们可以利用Golden Path提供的标准化:使用SBT构建的所有Scala项目都将使用相同的命令执行其测试:SBT测试。
1.克隆"喜欢" repository2。编译"喜欢" 3。运行测试"喜欢" 4。查找build.sbt5。用最新的快照6替换JVMKITversion值。运行自动API修复的源代码处理器7。编译"喜欢" 8。运行测试"喜欢"
有了这个,它变得更加清晰,失败的罪魁祸首是算法停止的这一序列的步骤。如果成功完成,我们也更加对平滑集成。
每次我们决定发布JVMKit的新版本时,我们都可以进一步开发自动化并利用持续的集成系统来开发自动化,而不是通过临时流行,而不是通过临时流程进行手动和临时时尚。存储库。
如前所述,在SoundCloud,我们拥有数百个依赖JVMKIT的项目,它使得编译和运行其测试耗时的任务。要加快JVMKIT的开发和反馈循环,我们决定将自动化分为两个:
一夜,长期运行的管道,可以验证新的JVMKIT针对组织中的所有项目
一个更快的管道,限制为一个小的项目项目,在每个拉拉请求上运行
这是通过从zoekt获取项目列表的脚本来实现的,并将它们转换为可以被我们的CD系统摄取的管道描述符。生成的流水线包括运行测试的步骤,具体取决于这些目标是否存在于每个目标项目上。
最重要的是,在现实世界中,并非所有项目都表现良好。我们可能希望跳过验证算法的具体步骤的原因:当测试套件花费太长时,如果已知是片状的,或者项目需要特定的环境配置或没有提供的特殊依赖项盒子。对于这些情况,我们还推出了一种校舍,用于输入CI管道发生器脚本的输入。
观察运行管道是一种参与活动。每天,我们都可以了解我们的项目在前进时的行为如何,并且当发生故障时,它也很容易定位原因:如果单个项目由于API变化而失败,我们可以潜入其实现的可能性缺点或使用JVMKIT的API,其最初是最初的意图。如果许多(或全部)项目失败,那么问题很明显,问题与JVMKIT本身,我们可以快速行动提供修复程序。
经过多年的投资后我们在JVMKIT的开发实践中达到了一个有趣的观点:在整个过程中的工具和自动化良好,我们在释放对图书馆的更改时,我们有信心为数十名队伍,数百个系统发布,数千个实例,数百万用户。我们无所畏惧地释放JVMKIT比以往任何时候都没有影响或中断我们的团队计划的路线图,同时还可以确保SoundCloud的技术标准化。