导读:在这篇文章中,Appsmith 的联合创始人和首席技术官分享了他们如何将 Appsmith 平台转换为单体架构,以及这个决定是如何让他们的贡献者和用户受益的。作者还探讨了单体架构和微服务的历史、优缺点,并强调了选择最佳软件架构应该依据团队的需求和情况而定。最后,作者提出了关于单体架构的一些观点,包括其如何影响 Appsmith 的开源开发模式以及如何使最终用户受益。
当我在面试软件工程师候选人时,我总是会问这样一个问题:“你会怎么做来改进 Appsmith?” 候选人们往往会作出这样一个普遍的回答:“我会把 Appsmith 代码库改造成微服务架构。” 这个答案反映了当今微服务架构的普及性,以及一种误解,即所有单体代码库都应该采用这种较新的模型进行重写。在谈论了微服务及其带来的优缺点之后,我不可避免地不得不透露我们有意选择不使用微服务,并解释为什么。
实际上,我认为对于大多数规模为中小型企业的团队(有 150 名以下工程师),使用微服务是不好的选择。即使像 Facebook、亚马逊、Netflix 这样的大型科技公司使用微服务,也并不意味着你也应该使用微服务。
作为 Appsmith 的联合创始人和首席技术官,我们的平台可以帮助开发人员快速构建内部应用程序,如支持工具、仪表盘和资产跟踪应用程序。在这个过程中,我有一个独特的视角,观察了我们的架构和开发实践是如何从早期阶段发生了变化。我想借用这个三部曲的机会,概括一下我在这段时间里看到的一些情况。其中包括我的看法,“单体架构与微服务”问题,我们如何将 Appsmith 实现为单体架构,这种选择如何影响我们转向开源开发模式,以及最重要的是,单体代码库如何使 Appsmith 的最终用户受益。
单体架构与微服务
单体架构和微服务各有适用的开发和部署需求,因此,哪种架构更好这个问题本身就不是关键所在。
最佳软件架构应该是能够让你的团队以最快的速度创建最高质量的软件的那个。具体采用哪种架构会因许多因素而异。为了了解这些因素,让我们回顾一下单体架构和微服务的历史、优点和缺点。
单体架构:简单但后期成本可能很高
在互联网普及和网络连接改进之前,微服务的概念还处于起步阶段。因此,唯一的可行选项是使用单体架构,包括单一代码库(通常是单一二进制文件)和一个(通常是大型的)应用程序,在本地或私有主机上运行。所有功能都是自包含的,虽然这种架构简单易于维护,但后期成本可能会很高。
单体架构在软件生命周期的各个阶段都有优点。在开发和调试期间,能够轻松跳转到整个代码库以深入了解发生的情况非常有用。单体应用程序可以部署到一个统一的环境中,这样可以减少复杂性和安装错误。在运行期间,调用存储在本地内存中的函数而不是经过许多层远程应用程序的 API 调用,这使得应用程序运行更快。
单体架构的缺点之一是,对功能的小修改会导致部署成本高昂,并且功能无法独立扩展。如果对某个功能进行小修改,通常需要重新运行回归测试并重新部署整个应用程序。根据应用程序的大小,这可能需要很多额外的工作。
扩展也是有问题的,特别是对于 Web 应用程序。通常希望根据流量要求只扩展一个组件,同时保持其他低流量组件不变以降低成本。但是,在单体架构中,单独扩展组件并不容易实现。
直到 2000 年代初,单体架构是开发者们的主要选择。互联网的早期增长几乎完全依靠这些架构,因为 Web 技术栈相对简单,易于打包成单个单体应用程序。单体架构经得起考验,但随着互联网的增长,出现了一种新的可扩展性需求的替代方案——微服务。
微服务架构:更复杂,理论上后期可扩展性更高。
随着互联网和构建其上的服务的性质不断发展,一些公司意识到单体架构可能对连接软件有所限制。其中最早的例子之一是 Netflix,在 2009 年决定用单体架构进行扩展将过于昂贵,无法适应其应用程序的需求。
他们从根本上重新思考了其架构,考虑到不断变化的消费者景观和因更强大的互联网而带来的技术改进。这些改进使得开发分布式应用程序成为可能,从而使他们能够将业务模式从邮寄 DVD 转变为点播流媒体视频。为了实现这一目标,他们将主机从现场转移到云端,将软件架构从单体转变为分布式微服务。
这是微服务架构的最早成功实施之一。微服务的主要思想是,功能不是自包含在单个大型应用程序中,而是拆分成许多较小的应用程序,通常分布在许多不同的服务器上,并通过定义良好的 API 进行通信。
微服务架构的好处包括更有组织和模块化的功能,更容易扩展,以及在多种编程语言中进行开发的可能性。通常将不同组件拆分到每个微服务的级别,使每个微服务有一个任务。这比典型的单体方法更明确界限,有助于保持组织清晰。由于功能分布在不同的二进制文件中,这些二进制文件可以部署在不同的实例上,允许对单个微服务进行扩展,而无需对整个应用程序进行扩展。
这种架构对于流量规模较大或变化频繁的 Web 应用程序特别有益。另一个好处是,只要 API 定义清晰,每个开发微服务的团队都可以选择符合他们偏好和技能的语言,不必像在单体应用中一样被强制使用相同的语言。
当然,这种模型也有缺点,包括需要更高的网络带宽、更高的开发人员技能和知识,以及更多的潜在安全漏洞。如果你正在实现一个基于微服务的架构,你必须拥有经验丰富的团队来处理这种增加的复杂性。
Appsmith 选择了哪种架构?
自从开始,我们就考虑和尝试了许多种架构。最初,我们考虑过基于微服务的架构,因为这是我们周围所有人都在做的事情。然而,我们最终拒绝了这个选项,因为基于微服务的应用程序本地部署存在太多问题需要解决。考虑到本地部署是我们业务的重点,我们不能使部署过程变得更加复杂化,从而影响最终用户的使用。
相反,在 Appsmith 早期阶段,我们依赖于纯粹的单体架构。这使我们在项目上取得了很大进展,但最终我们遇到了单体代码库扩展问题的显著工程团队问题。
我们知道这些扩展问题将继续阻碍我们的发展,所以我们回到了起点,重新考虑了我们从第一原则上需要的东西,而不仅仅是采用当时最流行的方法。我们最终在两种主要架构类型之间达成了一个妥协:模块化单体架构。
当时我们并不知道,但我们已经独立地发现了一个解决方案,这个解决方案已经存在于社区中,让像 Shopify、Gusto、Boxfuse 和许多其他公司能够在不采用基于微服务的方法的情况下扩展他们的开发。
两全其美
模块化单体结构依赖于基于微服务架构的模块化概念,同时使用单体代码库进行结构化。
在模块化单体代码库中,源文件按照不同的功能层次结构化,特定的团队负责管理这些目录的所有权和责任。这是两种架构的很好折衷方案,因为它可以在不引入其他性能和部署缺点的情况下,分离功能、所有权和责任。
从实际角度来看,这意味着当你添加新功能时,可以为其创建一个新的目录结构并负责管理,同时可以确信不会干扰任何其他人正在进行的工作。如果你是 Appsmith 的贡献者,那么可以更快地发现需要更改的相关代码,并且无需核心开发人员提供更少的指导。
模块化单体架构还拥有单体架构的所有部署优点——用户只需部署单个二进制文件,而不是数百个独立服务。这对于 Appsmith 的终端用户来说非常重要,特别是那些本地部署应用程序的用户,因为他们只需考虑一个自包含的应用程序,而不是数百个相互通信的微服务。
为我们做出正确的选择
模块化的单体架构使我们能够维护一个大型的代码库并部署一个单一的二进制文件,同时以清晰、有组织的方式模块化功能。这使我们能够平衡我们最终客户的利益(他们希望部署过程更简单)和我们内部团队以及开源社区的利益(他们希望功能更清晰地分离并且为贡献提供更好的指导)。
总的来说,转换到模块化的单体架构是我们在 Appsmith 做出的最好决定之一,这对我们、我们的贡献者和用户都有益。我们并不总是完美的,仍在学习如何最好地使用这个架构,但我们已经学到了一些重要的教训,将在我们关于选择正确架构的系列文章的下一篇中介绍。
原文链接:
评论 1 条评论