微服务分工模型。虽然服务是逐渐拆分出来的,但是随着业务的发展,到了某个时候,我们可能需要重新审视服务拆分是否合理。本节推荐两种业务划分方式,首先介绍如何选择业务划分方式。Selectaservicedelivermethodbasedonbusinesscomplexity根据业务复杂度划分服务,如图2-4所示。当业务复杂度足够高的时候,应该按照领域驱动来划分服务,而领域驱动本身就足够复杂,很多概念是抽象的,适用范围不是特别广,所以当业务复杂度低的时候,你可以选择基于数据驱动的Serve来划分。数据驱动更容易理解和使用。也就是说,除非业务复杂度很高,否则应该优先考虑数据驱动的服务划分。这里的业务复杂度特指业务逻辑,而不是数据量、并发等相关的复杂度。图2-4根据业务复杂度划分服务在选择时,另一个参考指标是团队之前是否开发过基于领域驱动的业务。换句话说,如果产品基于领域驱动开发开发了一段时间,团队有能力进行领域驱动开发,建议继续选择领域驱动分工服务。如果是全新的产品,可以灵活选择。选择业务划分方法时,应考虑以下条件。业务复杂性。团队对域驱动程序的熟悉程度。数据驱动的服务划分数据驱动是一种自下而上的架构设计方法。数据驱动强调数据结构,即通过需求分析,确定整体的数据结构,按照表之间的关系划分服务。通常,基于数据驱动划分服务的步骤如下。(1)需求分析。通过领域专家(或产品经理)确定目标,然后总结UserStory,确定核心业务流程;使用工具呈现一个粗略的界面并进行内部讨论;继续迭代这个链接,直到你满意为止。(2)抽象数据结构。根据需求总结UseCase,协助分析需求,从中抽象出数据结构。(3)划分服务。分析数据结构,识别服务——服务要满足高内聚、低耦合、单一职责的特点。(4)确定服务调用关系。首先分析主流程,根据请求根据需要调用的服务确定服务调用关系。如果出现问题,则需要回到(1)重新开始。(五)业务流程验证。回到用户故事,在服务的粒度上实现时序图。注意这个阶段的重点是验证服务划分是否合适,需要注意以下问题。如果更新操作跨越更多服务,一致性要求是什么。跨服务查询时,是否做关联查询,能否在一个服务内解决问题。性能是否满足要求。成本是否符合要求。(六)持续优化。领域驱动的服务划分领域驱动是一种自上而下的架构设计方法,通过与领域专家建立统一的语言,持续沟通,确定关键业务场景,逐步确定边界上下文。领域驱动更强调业务实现的效果,认为自下而上的设计可能导致技术人员无法更好地理解业务方向,从而偏离业务目标。通常,基于域驱动划分服务的步骤如下。(1)通过模型和领域专家建立统一的语言。为了更深入地了解需求,建立了一种通用语言。通用语言应该基于业务语言而不是技术语言;通用语言,如代码,需要不断重构。(2)业务分析。确定核心业务流程并逐步扩展到所有这些流程。最好通过内部讨论的工具呈现一个大概的界面。(3)寻找聚集体。显式定义领域模型的边界。最近流行的事件风暴是一种基于领域驱动的业务分析和服务划分的方法。事件风暴是将所有主要参与者召集到一个非常宽敞的房间里开会,并用便利贴来描述系统中发生的事情,如图2-5所示。图2-5事件风暴用橙色的便利贴代表领域事件,用一句话描述它上面发生了什么。用蓝色便利贴表示命令。命令的发起者可以是人,也可以是注入系统的外部事件,也可以是定时器。聚合由黄色便利贴表示。聚合是相关域对象的集合。高内聚低耦合是它的基本要求,必须保证聚合内部数据的一致性。(4)确定服务调用关系。首先分析主流程,根据一个请求需要调用的服务来确定服务调用关系。如果有水平分区,需要根据服务依赖原则来确定关系。如果出现问题,则需要回到(1)重新开始。(五)业务流程验证。实现以服务为粒度的时序图。注意这个阶段的重点是验证服务划分是否合适。主要关注以下几个问题。如果更新操作跨越更多服务,一致性要求是什么。跨服务查询时,是否做关联查询,能否在一个服务内解决问题。性能是否满足要求。成本是否符合要求。(六)持续优化。从现有的单体架构中逐步拆分服务大多数场景下,并不是一开始就采用微服务架构,而是随着业务的不断发展,逐渐将服务从最初的单体架构中拆分出来。下面逐步介绍单体架构的拆分步骤。(1)所有的微服务成功案例都始于单体架构太大,需要拆解,如图2-6所示。我们应该从单一架构开始。当系统规模足够大,团队数量足够多时,我们会逐步拆分服务。通常,前后端分离是拆分的第一步。图2-6从现有架构中逐步拆分服务(1)(2)提取公共基础服务,如单点登录。拆分可以遵循逻辑分离和物理分离两种方式。另外,随着系统压力的增加,可能会用到消息中间件、分布式缓存等服务。(3)不断对老系统进行服务抽象,优先垂直划分,如图2-7所示。图2-7从现有架构中逐步拆分服务(2)(4)当业务越来越复杂时,APIGateway做的事情太多,会成为瓶颈点,服务之间的依赖也会越来越多而且更复杂。这时需要适当的进行水平分割,如图2-8所示。图2-8从现有架构中逐步拆分服务(3)微服务??拆分策略单体架构不断抽象服务时,哪些服务先拆分,哪些服务不需要拆分?以下策略可以帮助解决拆分中的这些问题。相对独立的新业务优先采用微服务架构。从成本的角度来看,新业务采用新架构是最合理的,因为这样做对老业务的影响最小。优先抽象公共服务。因为通常通用服务的边界比较明显,耦合度低,比较容易分离。优先考虑更容易识别和具有明确界限的服务。如果原包结构比较清晰,可以按照原包结构中比较完整、边界明显的服务进行划分,这是从成本的角度考虑的。如果基于单体架构开发了一段时间,对业务的理解非常高,那么开发人员和架构师很容易抽取一些边界明显的服务。优先抽象核心服务。因为微服务的开发和运维成本比较高,并不是所有的地方都需要进行小粒度的划分。往往一些边缘化的运营和管理体系甚至不考虑拆分。另外,随着时间的推移,有些业务可能会发生变化,所以应该先抽象出核心服务。优先抽象具有独立属性的服务。服务应该按照功能变化频率、资源占用、技术栈等属性进行划分。采用扼杀者模式,让新服务随着时间的推移逐渐“扼杀”遗留系统外围的旧系统。在这种情况下,其复杂性往往体现在如何灰度发布、迁移数据、如何保证服务不中断等方面,后面的章节会详细介绍。如何衡量服务划分的合理性每个产品实施微服务架构的初始动机不同,目标也不同。因此,判断划分是否合理,首先要看目标是否达到。其次,您可以参考以下测量方法。每种测量方法不能单独作为判断标准,需要综合考虑。一个小的功能修改需要多长时间才能上线?一般情况下,微服务架构的交付周期应该以天为单位。如果修改一个小功能需要几周到几个月的时间,可能说明服务划分的粒度太大,冲突太多,需要合并代码。大多数功能修改能否在一项服务中完成?如果经常需要一个跨服务团队的联合开发团队来完成新功能的开发或者旧功能的修改,那么服务分工就存在问题。是否需要经常修改界面?频繁的接口修改可能是接口设计不合理造成的,也可能是服务划分的问题,说明服务之间的边界并不是特别清晰和稳定。响应时间能否满足要求?在一些追求极致性能的场景下,对响应时间的要求很高。服务划分的层级太多,粒度太小,可能会导致响应时间达不到要求。是否有大量的跨服更新?是否存在大量跨服务相关查询?这两个问题可能是由于划分不合理造成的。微服务划分反模式我们介绍了如何划分服务。除此之外,我们希望通过微服务划分的反模式,帮助大家少走弯路。根据代码行数划分服务代码。如果代码量过大,会导致通信效率低,交付效率低,耦合度高,繁琐。代码大小可以作为参考,但不是绝对标准。微服务架构中存在一个“大服务”很正常。根据代码行数来拆分服务,很难衡量服务的完整性,容易导致拆分粒度变小,造成不必要的复杂度。划分粒度越小越好。服务的大小并不是特别重要。可以根据团队规模、代码量、业务复杂度、技术领域、重要性、成本等因素综合考虑。关于服务粒度的大小,业界没有统一的标准,很难衡量。最接近的衡量标准是研发团队的规模。小粒度意味着更高的维护成本。后台管理和辅助系统通常粒度比较大。一次性服务划分服务划分有以下两种方式。首先是先拆分业务代码,再拆分数据库。如图2-9所示,数据库并没有拆分,而是单体抽象出了一些服务。图2-9先拆分业务代码再拆分数据库。第二种方法是业务代码和数据库同步拆分。如图2-10所示,部分业务服务拆分时,数据库也同步拆分。图2-10业务代码与数据库同步拆分使用第二种方式拆分服务时,数据库按照服务完全拆分为多个独立的数据库,每个服务独享数据库服务,服务之间只能连接通过接口传输。这看起来不错,但需要为此进行大量数据迁移。当业务处于早期阶段,需求不是很确定,开发人员对业务了解不是特别透彻,拆分后可能会发现拆分不合理,不得不重新合并,进行数据迁移再次。相对来说,业务代码拆分的成本较低,但数据迁移的成本较高,频繁、大规模的数据迁移不可取。更好的做法是以第一种方式作为过渡阶段,待业务逐渐稳定后再完成数据迁移。注意,过渡期间数据库并没有完全分离,所有的依赖都是通过接口来访问的。但这只是口头约定,业务开发人员可以直接在数据库中进行关联查询。它需要通过代码审查来避免。服务划分是一个长期的过程,需要积累大量的领域知识才能理解核心过程。最好与领域专家组成联合团队,在了解核心问题的情况下,继续拆分服务,验证拆分的合理性,久而久之,可以重新分区。可见,服务拆分是一个连续的过程。服务部门一旦完成,就不能再更改。由于业务的不断变化以及开发人员对领域知识的理解等影响因素,很难一次性做出完美的解决方案。通常在划分之后,发现某个问题难以忍受,比如划分之后响应时间减少,增加了更多的成本,可能需要重新合并服务;由于业务变化,原有的依赖发生了变化,可能会面临需要重新划分服务等类似问题。先实现组件化,再实现微服务架构很多技术人员试图先通过组件化逐步过渡到微服务架构,这是一种错误的思路。微服务架构的划分更强调业务领域的完整性,因此优先考虑垂直划分,而组件化往往通过抽象稳定的部分形成组件共享,而不强调调用次数和依赖。所以,组件化之后转为微服务架构,通常是错误的。
