近日,GitHub 前 CTO Jason Warner 在推特上表示,“我确信过去十年中,最大的架构错误之一就是全面使用微服务。”从单体应用到微服务的规划顺序,Warner 的建议是:单体>应用程序>服务>微服务。
Warner 表示,这是一种思维方式而非规则。“任何构建过大型分布式系统的人都知道他们并不真的那样工作,但还必须适应它。”其次,Warner 表示认为,公司所处的阶段很重要。如果是一家 5-50 人的公司,只需坚持使用单体。
Warner 先对服务和微服务的定义进行了阐释。服务支持应用程序/单体,是核心基础设施,被大量需要,为核心合规功能,可能不是应用程序团队编写的(基础设施团队维护);微服务则有几百行代码,大部分是一次性的,可能或应该是库、SDK 等。对于为什么不太看好微服务,Warner 给出的理由如下:
Warner 还指出,当涉及几十个微服务或更大规模时,企业遇到通常并非技术问题,而是组织上的挑战。
首先,基础设施几乎不会被优先考虑(除非公司由非常随和的 CEO 领导);其次,过多的服务常常会导致所有权和边界问题;再者,为处理过多的微服务会引入更多的工具;更重要的是,本来应该是库、SDK 或其他东西的微服务都会引入生产风险。代码过多是开销,服务过多是客户面临的产品/体验风险,两者都有开销和风险,但百分比分布不同。
因此,Warner 鼓励企业根据自己的情况来选择,而不是盲目跟随大厂的做法,他给出的建议是:
对于 Warner 的观点,有开发者评价道,“我认为他提出了一些很好的观点,尤其是关于有多少东西真的应该是库。”也有开发者表示,微服务的主要问题很简单,就是大多数人不了解如何正确设计它们。一个设计糟糕的单体架构几乎总好过设计糟糕的微服务架构。单体保护企业免受不良设计影响的底线要高得多。最大的错误是人们倾向于创建太小或太多的服务。
Warner 曾在 Heroku 担任副总裁/工程主管三年多,并在担任 Ubuntu Desktop 工程主管近四年后,在 2017 年 5 月开始担任 GitHub 的首席技术官一职。Warner 现在已成为 Redpoint Ventures 的董事总经理。
Warner 十七八岁时才真正开始编程。当时的他刚进入 IBM 主要负责打印机联网,“他们最终说,'嘿,如果你去学校学习如何编程和学习计算机科学,毕业后我们会给你一份工作。'”Warner 曾在博客中回忆道。
尽管拥有计算机科学学士和硕士学位,Warner 还是认为自己可能是一名普通的开发人员。初到 GitHub 时,Warner 将时间更多花在了产品方面,但随着开发者社区蓬勃发展,GitHub 架构面临着更大的扩展性挑战。
Warner 刚来时,GitHub 拥有约 2000 万帐户,该网站每天大约有 150 万至 200 万活跃用户,注册量达 1 万人。但到 2021 年 7 月 Warner 离开时,这一数字已跃升至每天 50,000 人注册,日活跃用户也达到了 700 万。
显然,微服务架构成为当时 GitHub 减轻扩展限制的选择之一。微服务潮流曾被 Heroku 大力推动,或许 Heroku 任职的经历也让 Warner 支持 GitHub 进行微服务改造。“我实际上可以坐在那里倾听并真正为整体架构方法做出贡献。”Warner 曾在采访中提到。
一直以来, GitHub 是基于 Ruby on Rails 的单体架构,直到 2021 年,为了让超过一半的开发人员在单体代码库之外富有成效地开展工作,GitHub 以赋能为出发点开始了向微服务架构的迁移。
GitHub 团队认为,良好的架构始于模块化。拆分单体的第一步是考虑基于特性功能分割代码和数据。这个过程可以在真正在微服务环境中拆分之前在单体中完成。
正确地拆分数据是从单体架构转向微服务的基础。GitHub 的做法是先在现有的数据库模式中识别功能边界,并按照这些边界将实际的数据库表分组。GitHub 研发团队将生成的功能分组称为模式域,并记录在 YAML 定义文件中。在数据库模式中添加或删除表,都要更新这个文件。
接下来,对于每个模式域,团队找了一个分区键。这是一个共享字段,将一个功能组中的所有信息联系在一起。最终,创建数据库模式功能组帮助团队将数据拆分到微服务架构所需的不同服务器和集群上。GitHub 在单体中实现了一个查询监视器来帮助检测,并在发现跨域查询时发出告警信息。
GitHub 有超过 5000 万用户和 1 亿个存储库,在这样的规模下,功能组可能会变得非常大。这时,分区键就派上了用场。例如,一种简单的方法是根据数值范围将不同的用户分配到不同的数据存储。更常见的可能是根据每个数据集的特性(如区域和大小)所做的逻辑分组。
GitHub 如何从单体中抽取服务呢?GitHub 认为,依赖方向只能从单体内到单体外,不能反过来,否则最终会得到一个分布式单体。即当从单体中抽取服务时要从核心服务入手,然后逐步到特性层面。
接下来,找出开发人员在单体环境中开发时所使用的助力工具。最后在新服务上线运行后,务必要删除旧的代码路径。GitHub 通过名为 Scientist 的工具来识别谁在调用这个服务,并规划好如何将流量全部导向新服务,这样就不用总是支持两套代码了。
GitHub 首先抽取的核心服务是身份验证和授权。GitHub 在单体外部将身份验证重写为一个镜像服务。GitHub 的 Rails 应用程序(单体)使用 Twirp(这是一个 gRPC 风格的服务到服务通信框架)和它通信,依赖方向是由内到外。
下一步,找一些简单的小特性从单体中迁移出来,例如那些没有复杂依赖和共享逻辑的特性。GitHub 是从 webhook 推送和语法高亮开始的。GitHub 通过查找经常一起更改和部署的代码和数据,来确定耦合度较高的特性或功能,并以此为基础,自然地划分成可以独立于其他部分单独迭代和部署的分组。GitHub 根据产品和业务价值来确定微服务的大小。
此外,为了支持从单体到微服务的转型,节省时间、加速向微服务的过渡,GitHub 也做了必要的运营改变。例如,GitHub 创建了一个自助服务运行时平台,用于微服务的打包交付,目的是大幅减轻每个团队创建微服务时的运营负担。
如今,GitHub 已经成为基于“单体-微服务混合”的环境。
微服务正在统治世界,甚至有可能正在成为新的默认选项。但这几年,无数的中小团队在微服务上陷入了挣扎,很多公司在放弃微服务,其中包括一些大型企业。
2020 年,Uber 放弃了微服务,转而使用宏服务。Uber 支付体验平台的工程经理 Gergely Oros 表示,“Uber 最早通过构建微服务来完成很小的需求或功能,以至于出现了很多由一个人构建维护的微服务。这些微服务的存在带来了新的复杂性和挑战,例如监控、测试、持续集成 / 持续交付(CI/CD)、服务级别协议(SLA)、跨所有微服务的库版本(安全和时区问题)等等。”
因此在创建新平台的时候,Uber 支付体验团队对新服务进行了更加深思熟虑的规划:不再只是完成一件事,而是使其服务于一项业务功能,由 5-10 个工程师负责维护。Orosz 把这样的服务规划称之为宏服务。
同样,从事 SEO 优化的公司Botify在运行了不到四年的微服务后也放弃了。
Botify 平台通过 Django 应用程序的负载均衡集群提供服务。2016 年底,Botify 工程团队想让工程师和产品经理拥有更多的局部所有权,从而可以快速将他们的产品和技术栈投入使用。为此,团队决定将他们的 Django 应用程序拆分为微服务。当时,他们的团队大约为 15 人,也是从身份验证和授权入手实现第一个微服务,将 Django 应用程序当前的一部分功能转移到微服务中,微服务模块也需要和其他的 Django/Python 单体模块进行通讯。
Botify 平台的主要难点是对客户数据进行分析。处理用户相关数据的微服务架构旨在服务于高流量的 B2C 平台,而 Botify 的挑战在于动态地聚合数以 GB 的 SEO 数据,使其在几秒钟内可用。对大约一万名客户的元数据以毫秒为单位进行响应,这项任务不需要高度可伸缩的微服务架构,但 Botify 的后端到后端通信减慢了这些简单的检索过程,花费了更多的时间。
鉴于每天都要在 JavaScript 身份验证后端和 Django 模块之间频繁地来回切换,在权衡了架构的优缺点以及潜在的迁移成本后,Botify 将身份验证后端重新加入到 Django 单体中,并于 2020 年 2 月停用了微服务。
微服务有好处也有弊端和风险。正如 Warner 所说,企业应该根据自己的情况来选择,而不是一味追随潮流。
参考链接:
https://www.infoq.cn/article/zYGF4FpIVVt5U2omioUu
https://thenewstack.io/what-a-former-github-cto-learned-about-scaling/
领取专属 10元无门槛券
私享最新 技术干货