微服务架构特征
Last updated
Last updated
我们不能说存在这种微服务架构风格的正式定义,我们可以尝试去描述我们所看到的对于这种架构风格通用、共同的特征。并不是所有的微服务架构都会拥有所有的特征,但是我们希望大多数微服务架构都可以展现出我们所描述的大多数特征。我们并不会给出一些强制遵循的标准,微服务只是一种风格,而不是标准。给出指导原则,但不是强制的。
我们希望系统的构建方式,是通过不同的组件之间来可插拔搭配完成的,这种方式和现实世界中所看到的很多事物都是类似的。
当我们谈论组件的时候,其实陷入了一种很困难的定义,到底是什么构成了一个组件。我们的定义:组件就是一个软件单元,它是独立的、可替换的、可升级的。
微服务架构会使用到库,但是它们主要的组件化方式是将软件分解成一个个的服务形态。我们将库定义成组件,它们可以连接到程序中,也可以通过内存当中的函数调用来进行调用。而服务形态是进程外的组件,跨进程的,它们通信机制是通过 web service、remote procedure call(远程过程调用)。与面向对象程序中的服务对象来比,是一个完全不同的概念。
使用服务作为组件,而非库的一个主要原因是,服务是可以独立部署的。如果你拥有一个应用,它是由一个进程当中的多个库组成的。对于任何一个组件的修改,都会导致整个应用需要重新部署。但是如果这个应用是被分解成多个服务的话,就可以期望很多单个服务的改变,只会需要重新部署被修改的单个服务。这并不是绝对的,一些更新还会改变服务接口本身,比如这个服务对外提供的接口从三个参数变成了四个参数,这就会影响到所有调用这个服务的其他微服务,也需要跟着修改,这种修改可能会很麻烦,会涉及到很多其他微服务的改动、部署。只有只修改了服务内部实现,而服务对外提供的接口或者锲约没有发生什么改变,这种情况才只需要重新部署被修改的微服务本身就可以了。但是一个好的微服务架构目标是,通过明确的服务边界和演化机制来把这种改变最小化。
将服务作为组件的另一个结果更加的显示组件接口。大多数语言都没有提供一个良好的机制用来定义一个显示的发布接口的一种方式。通常这并不仅仅是文档和原则性的问题,来去防止客户端去破坏一个组件的封装原则。而且这会导致过于紧密的耦合,使组件间的耦合性过于紧密。通过服务的方式可以使得这个问题变得容易,避免这一点,方式是使用显示的远程调用机制。
按照这种方式来使用服务也有一些缺点,远程调用通常要比进程内的调用成本更高。而且远程 API 需要粗粒度的设计,通常用起来感觉很笨拙。如果你需要改变组件间的职责分配,如果要跨越进程边界,行为的移动是更加困难的,比如把很多逻辑从 A 服务放置到 B 服务里。
我们可以观察到服务是可以映射成运行期的进程,但是这只是一种大致的描述,一个服务是可以包含多个进程。这些进程总是一起被开发,部署的,这样的应用进程和数据库只是被这个服务自己所独有的。
当我们去观察一个大型应用拆分若干个部分的时候,通常管理会关注在技术层面上,会导致UI团队、服务端逻辑团队、数据库团队等等。当这些团队按照服务线的方式进行软件架构风格,即使是一个小小的改变,都会导致一个跨团队的项目的沟通、批准。一个很聪明的团队通常会对其进行优化,减少这种情况的发生,会强制这种逻辑放置到他们应该能访问到的应用当中。
如上图,如果我们把架构设计成单体应用,大家都各辞其职。UI 做 UI、中间件做中间件、DBA 做 DBA。在某一个业务当中,这个服务可能同时涉及到 UI、中间件、DBA 这几种工作。会糅合多种人员在同一个业务内,会使得团队与团队之间的沟通不像以前那么简单而又直接,可能会出现一些修改一个地方涉及到多个部门通力配合的情况。
微服务的方式则和上面的单体应用不一样,它会将服务按照业务能力来组织。这种服务会接受一个针对业务领域的软件实现,包括用户界面、持久层存储、额外的一些协作。结果导致团队是跨越功能,包含用户界面、数据库、项目管理等完整技能的团队。
微服务架构是围绕业务能力展开的,和单体应用差距很大。
大型的单体应用总是可以围绕业务进行模块化,虽然这并不是常见的情况。我们可以督促一个大型团队按照业务线来分解、构建单体应用。主要的问题是他们趋向于围绕着太多的上下文。如果单体应用可以按照模块化来拆分的话,那么对于每一个团队成员来说都很难去短期适应组织。此外我们还会看到模块化这种方式需要很好的一个纪律去遵循,这种必要性会增加服务组件边界清晰化的困难。