微服务化之后普遍的垂直电商系统的架构将会变成下面这样:
图片
在这一架构中,我们的目标是将与用户、订单和商品相关的逻辑拆分成独立的服务,以取代原有的直接依赖缓存和数据库的Web工程和队列处理程序。为了迅速实现服务化拆分,我们决定召集主力研发同事来一同制定拆分计划。然而,在深入讨论后,我们发现仍有许多未解之谜,例如:
- 服务拆分原则:我们需要确定拆分服务时应遵循哪些原则,以确保每个微服务的独立性和可维护性。
- 服务边界的确定:如何明确定义每个微服务的边界,以避免微服务之间的不必要耦合?
- 服务粒度:我们需要明确微服务的粒度应该是多大,以便更好地管理和维护它们。
- 潜在问题:在实施服务化之后,我们可能会面临性能、安全性、版本管理和通信等方面的问题,需要提前考虑并准备相应的解决方案。
这些问题将直接影响我们的服务化拆分计划的效果。因此,我们需要认真思考并找到答案,以确保成功实施这一重要的架构变革。
微服务拆分的原则
以前我们维护的一体化架构就像一个错综复杂的大蜘蛛网,各种不同功能模块紧密相连,方法之间的调用关系错综复杂,因此修复一个Bug可能会触发多个新Bug。这种维护复杂度极高,同时数据库的扩展性也受到限制,制约了系统的扩展能力。出于以上原因,我们决定对架构进行拆分。
然而,拆分并不是一项轻松的任务,实际上,它相当于对整个工程进行了重构,甚至需要重写部分代码。我们需要将现有的代码拆分成若干个子工程,然后通过某种通信方式将这些子工程组装在一起。这是一项复杂的架构调整工作,需要多个团队之间的紧密协作和协同努力来完成。
所以在开始拆分之前你需要明确几个拆分的原则,否则就会事倍功半甚至对整体项目产生不利的影响。
首要原则是确保每个单一服务内部拥有高内聚性和低耦合性。这意味着每个服务应只承担其职责内的任务,不应处理不属于自身职责范围的功能。虽然这听起来可能理所当然,但在实际开发中,很多人往往会犯这方面的错误。
举例来说,在我的之前的项目中,我们有用户服务和内容服务。用户信息中包含一个“是否认证用户”的字段。有一位同事在内容服务中添加了如下逻辑:如果用户认证字段等于1,表示是认证用户,则提升内容的权重。问题在于,判断用户是否认证用户的逻辑应当内聚在用户服务内部,而不应该由内容服务进行判断。否则,一旦认证逻辑发生变化,内容服务也必须相应变更,这违反了高内聚和低耦合的原则。
幸运的是,在我们进行代码审查时及时发现了这个问题,因此我们在服务上线之前对其进行了修复。这个例子强调了确保高内聚和低耦合的重要性,以防止服务之间的过度依赖和不必要的复杂性。
第二个原则是关注服务拆分的粒度,最初应该进行粗略拆分,然后逐渐细化。在服务拆分的早期阶段,很难确定服务应该拆分成什么样子。尽管“微服务”这个术语暗示服务的粒度应该非常小,甚至有“一方法一服务”的说法,但服务数量的增加也会引发一些问题,如增加了运维成本。
此外,如果原本的请求需要调用进程内的多个方法,而现在需要跨网络调用多个RPC服务,性能可能会受到影响。因此,我建议的做法是,在拆分初期,可以将服务的粒度保持较粗,随着团队对业务和微服务理念的逐渐深入理解,再考虑逐步细化服务的粒度。例如,对于社区系统,您可以先将与用户关系相关的业务逻辑粗略拆分到用户关系服务中,然后再将例如黑名单逻辑独立拆分为黑名单服务。这样的方法有助于平衡微服务的数量和复杂性。
原则三,拆分的过程,要尽量避免影响产品的日常功能迭代。也就是说,要一边做产品功能迭代,一边完成服务化拆分。
第四个原则是要确保服务接口的定义具有可扩展性。在进行服务拆分后,由于服务独立部署在不同的进程中,服务之间的通信不再是进程内部的方法调用,而是跨进程的网络通信。在这种通信模型下,服务接口的定义必须具有可扩展性,以防止在服务发生变化时引发意外错误。
微服务化带来的问题和解决思路
微服务化只是一种架构手段,有效拆分后可以帮助实现服务的敏捷开发和部署。但是由于将原本一体化架构的应用拆分成了多个通过网络通信的分布式服务,为了在分布式环境下协调多个服务正常运行,就必然引入一定的复杂度,这些复杂度主要体现在以下几个方面:
在微服务架构中,服务接口的调用不再是同一进程内的方法调用,而是跨进程的网络调用,这可能导致接口响应时间的增加。为了解决这个问题,我们需要选择高效的服务调用框架。此外,接口调用方还需要知道目标服务部署在哪些机器上以及哪个端口上。这些信息需要存储在一个分布式一致性的存储中,这就是服务注册中心的作用。
在微服务架构中,多个服务之间存在复杂的相互依赖关系。一个服务可能会被多个其他服务所依赖,同时也会依赖于多个服务。当被依赖的服务出现性能问题,导致产生大量的慢请求时,这会占用依赖服务的工作线程池中的线程,进而导致依赖服务也出现性能问题。
在微服务架构中,一个请求的调用链涉及多个服务,因此如果该请求的响应时间增长或出现错误,很难迅速确定是哪个服务引发了问题。此外,当整个系统出现故障时,所有服务可能都在同一时间内受到影响,这使得难以确认问题的根本原因。
总的来说,微服务架构是一个广泛而深刻的话题。虽然你可以相对迅速地将服务进行拆分,但构建和维护一个健全的服务治理体系可能需要较长时间。在接下来的内容中,我们将探讨一些常用的微服务中间件的原理和使用方法。
为更好理解这些内容,建议采取以下步骤:
- 快速部署和运行中间件:首先迅速部署并运行这些中间件,以建立对它们的感性认识。这将帮助你熟悉它们的基本用法。
- 阅读文档:深入阅读中间件的文档,特别是关于基本原理和架构设计的部分。这将为你提供更深入的理解。
- 阅读源码:如有必要,尝试阅读中间件的源代码。这可以帮助你更深入地理解它们的工作原理,有助于排查中间件可能引发的故障和解决性能问题。
微服务化拆分的原则
康威定律"强调了组织结构和系统架构之间的密切关系。简而言之,你的团队组织结构会直接影响你的系统架构。如果你的团队划分为服务端开发团队、DBA 团队、运维团队和测试团队等,那么你的架构可能会更趋向一体化,所有团队成员共同管理一个大型系统,这会增加内部团队间的沟通成本。
然而,如果你的目标是实现微服务架构,那么你需要将团队按照业务边界进行划分,每个小团队负责一个自治的模块。每个小团队内部包括开发、测试、运维和DBA成员,这样沟通主要发生在小团队内部,极大降低了沟通成本。
微服务架构的一个目标是降低开发成本,包括沟通成本。因此,小团队内的成员不宜过多。根据亚马逊CEO贝佐斯的“两个披萨”的理论,一个小团队最佳的成员数量是6到8人。
如果你的团队规模不大,尚未准备好实施微服务架构,但感受到研发和部署成本较高,可以采用一个中间的策略,先着重拆分工程结构,以降低沟通成本和提高灵活性。这有助于逐步迈向微服务架构,同时减少短期内的复杂性。
举例来说,如果你使用Java语言,可以考虑根据业务边界将代码拆分到不同的子工程中。这些子工程可以作为独立的模块,通过jar包的方式相互依赖。这种做法有以下好处:
- 减少打包时间:每个子工程的代码量减少,编译和打包时间相对较短,从而提高开发效率。
- 高内聚低耦合:子工程内部可以更好地实现高内聚和低耦合,使代码更易于维护和扩展。
- 逐步拆分:这是一个保守的策略,允许你逐步将代码拆分为更小的模块,而不需要一次性完全改变架构。这可以降低风险,逐渐迈向微服务架构。