心得

1. 单体架构转向微服务,必须更注重 模块化

良好的架构始于模块化。拆分单体的第一步是考虑基于特性功能分割代码和数据。这个过程可以在真正在微服务环境中拆分之前在单体中完成。使代码库易于管理,通常都是一种良好的架构实践。确保每个服务都有自己的数据,并且能够控制对这些数据的访问,而且只能通过明确定义的 API 契约访问。

我看到,在很多情况下,人们会首先抽出代码逻辑,但仍然使用单体的共享数据库。这往往会导致分布式单体,这是最糟糕的单体,同时也是最糟糕的分布式。没有获得任何好处(比如,单独快速地向生产环境中部署一组特性),却还要应对微服务的复杂性。

2. 单体架构转向微服务,必须对 数据进行拆分

正确地拆分数据是从单体架构转向微服务的基础。这里将稍微详细地介绍下 GitHub 的做法。

首先,我们在现有的数据库模式中识别功能边界,并按照这些边界将实际的数据库表分组。例如,我们将所有存储库相关的表分到一起,所有用户相关的分到一起,所有项目相关的分到一起。我们将生成的功能分组称为模式域,并记录在 YAML 定义文件中。现在,这个文件就成了事实来源。在数据库模式中添加或删除表,都要更新这个文件。我们通过一种静态分析测试方法来提醒开发人员,在修改数据库模式时,要更新这个文件。

接下来,对于每个模式域,我们找了一个分区键。这是一个共享字段,将一个功能组中的所有信息联系在一起。例如,存储库模式域(其中包含所有与存储库相关的数据,如问题、pull 请求、评审意见)使用存储库 ID 作为分区键。最终,创建数据库模式功能组帮助我们将数据拆分到微服务架构所需的不同服务器和集群上。

对于当前的跨域查询,我们做了修复,以防数据拆分对产品造成破坏。在 GitHub,我们在单体中实现了一个查询监视器来帮助我们检测,并在发现跨域查询时发出告警信息。我们会根据域边界,把这些查询拆分并重写成多个,并在应用程序层实现必要的连接。在划分完功能组后,我们开始通过一个类似的过程,进一步将数据分片到相应的租户组。

GitHub 有超过 5000 万用户和 1 亿个存储库,在这样的规模下,功能组可能会变得非常大。这时,分区键就派上用场了。例如,一种简单的方法是根据数值范围将不同的用户分配到不同的数据存储。更常见的可能是根据每个数据集的特性(如区域和大小)所做的逻辑分组。Tenantizing 是一个很好的方法,可以将数据存储故障的爆炸半径限制在客户的一个子集里,而不是一下子影响到所有人。

心得引文:《GitHub 如何从单体架构迁移到微服务架构?》,对于Github有超过1000名的内部开发人员,内部快速增长的团队规模,让所有人都在同一个单体代码库上进行开发,不再是扩展Github最高效、最优化的方法。

3. 单体架构转向微服务, 标签语义方式是多么的重要

参照K8s的实践,通过标签来处理逻辑。而不是真正的IP地址,url地址。

4. 单体架构转向微服务, 从核心服务、共享资源入手

我们已经花了很多时间讨论数据拆分的重要性。现在,我们换个话题,介绍下从单体中抽取服务的基础工作。一定要记住,依赖方向只能从单体内到单体外,不能反过来,否则,我们最终会得到一个分布式单体。也就是说,当从单体中抽取服务时,要从核心服务入手,然后逐步到特性层面。

接下来,找出开发人员在单体环境中开发时所使用的助力工具。随着时间的推移构建一些共享工具以方便单体开发,这是很常见的。例如,我们的特性标识,可以让单体开发者安心地将新特性从测试环境转到生产环境,因为在这个过程中,他们可以通过这个标识控制谁能看到这些特性。将助力工具转移出来,让开发人员在单体之外也可以使用这些工具。

最后,在新服务上线运行后,务必要删除旧的代码路径。通过工具来识别谁在调用这个服务,并规划好如何将流量全部导向新服务,这样你就不用老是为两套代码提供支持了。在 GitHub,我们使用一个名为 Scientist 的工具帮我们处理这种上线,我们可以用它并排运行和比较新旧代码路径。

心得引文《GitHub 如何从单体架构迁移到微服务架构?》

5. 单体架构转向微服务, 要解决 身份验证和授权

6. 单体架构转向微服务, 要解决 实现异步性和弹性代码

7. 深度理解 康威定律 的核心内涵,很重要

Conway’s law 最初来自于Conway在1967年发表的论文

从康威定律的角度来看,如果一个系统的每个部分都是独立的,那么这个系统就是一个系统。

设计系统的组织其产生的设计等价于组织间的沟通结构。

如果系统架构不支持,你无法建立一个高效的组织。

第一定律:组织沟通方式会通过系统设计表达出来;

对于复杂的,需要协作完成的系统开发,沟通是必须要持续提升的问题。每个团队由5-10人组成(沟通成本 = n(n-1)/2 - 《人月神话》),在团队内部进行频繁的、细粒度的沟通。对于团队外部,定义好接口,契约,只进行粗粒度的沟通。这样可以降低沟通成本,同时也符合高内聚,低耦合原则(代码和人员管理有些时候真是相通的)。

任何组织设计的系统,其结构都是对组织沟通结构的复制。反之亦然,单体架构会导致更大规模的涉众会议,更复杂的决策过程,因为交织的逻辑和共享的数据会影响所有团队。

例如,建立具有系统级所有权的特性团队,通过清晰定义的 API 契约确立职责边界。在遵循 API 契约的前提下,团队有充分的自由选择最适合自己的技术栈。代码库更小意味着阅读更容易、启动速度更快、问题排查更简单。开发人员不用为了提高生产力去理解一整个庞大的代码库的内部运行机制。最重要的是,服务现在可以根据各自的需求单独扩展。

第二定律:时间再多一件事情也不可能做的完美,但总有时间做完一件事情

这就是我们在用kanban管理迭代时几乎都有一列是BAU(Business As Usual ),其中会包括一些日常修复的Bug Story。敏捷开发中将迭代引入,做到持续交付,快速验证,迅速反馈,持续改进。

第三定律: 线型系统和线型组织架构间有潜在的异质同态特性

大白话就是,你想要架构成为什么样,就将团队分成怎样的结构。比如前后端分离的团队,架构就是基于前后端分离。在基于微服务设计的团队里,一个很好的理念是自管理。团队内部对于自己所负责的模块高度负责,进行端对端的开发以及运维。

第四定律: 大的系统组织总是比小系统更倾向于分解

合久必分,分久必合,团队以及架构都是在不断优化的。一个团队随着人员的增加,沟通以及管理成本一定会增加。