站内链接:

微服务

微服务和分层

微服务可以是一种分层架构的实现方式,而分层架构可以在微服务架构中用于组织和管理各个微服务内部的组件。分层架构的架构演进中提出了整洁架构、DDD 领域驱动设计等架构或风格,它们的最终目的都是为了:关注点分离、边界职责分明,在这其中,微服务以 RPA 调用的方式从代码项目和信息交流方式上彻底的进行解耦,将自治的业务独立成一个个的微服务,确保一个庞杂的应用系统更加可维护、边界更清晰。

从分层架构的角度考虑,一个个的微服务何尝不是一个个分层的小圆圈。

微服务和单体

微服务架构并非适用于所有的场景,在资源受限的情况下,采用微服务架构很多优势无法体现,性能上的劣势反而会比较明显,下面是单体架构和微服务架构在业务复杂度不断增加之后的成本估算:

struct-miscro-single

从图中可以看出,只有业务复杂度达到一定规模之后,此时使用微服务才能有比较好的成本性价比。另外,微服务的落地也不是那么轻松就能实现的,需要提前完成如:服务描述、注册中心、服务框架、服务监控、服务治理等基础设施,这些基础设施又要基于容器技术、CICD、DevOps 等技术概念去完成。微服务不仅仅是技术的升级,更是开发方式、组织架构、开发观念的转变。

定义

微服务是一种软件架构风格,将大型应用程序拆分为一组小型、独立的服务,每个服务运行在自己独立的进程中,并通过轻量级的通信机制进行交互。每个微服务负责特定的业务功能,并可以独立开发、部署和扩展。

微服务架构的基本思想就是围绕业务领域组件来创建应用,让应用可以独立的开发、管理和加速,其带来如下的好处:

  • 微服务组件都是简单灵活的,能够独立部署
  • 专注、专业、高效、可靠的小团队
  • 松耦合、高内聚、易扩展的服务
  • 微服务架构与语言工具无关,自由选择合适的语言和工具,高效的完成业务目标即可

服务要点

  • 服务发现
  • 负载均衡
  • 高可用、集群容错、弹性伸缩、
  • API 网关
  • 服务故障隔离与熔断
  • 配置管理
  • 日志采集
  • 分布式调用跟踪、监控

设计原则

基本原则

微服务的设计有一些最为基本的原则,后续的其他原则都是在这些基础原则的基础上进行的

  • 单一职责原则
  • 高内聚,低耦合原则:微服务之间通过接口进行通信,彼此解耦,可以独立进行修改和升级
  • 分布式部署:每个微服务可以独立部署,可以使用不同的编程语言和技术栈,这实际上就是高内聚、低耦合的体现
  • 独立团队:每个微服务可以由一个小团队负责开发和维护,提高开发效率和快速交付能力。
  • 可伸缩性:由于每个微服务独立运行,可以根据需求对每个服务进行水平扩展,提高系统的整体性能和容量。

其中低耦合原则有如下几种具现的判断标准:

  • 隐藏内部实现
  • 避免代码库共享
  • 避免数据过度暴露
  • 避免数据库共享
  • 最小化同步调用
  • 最小化硬件共享
  • 避免使用平台独特性技术

AKF 拆分原则

AKF 扩展立方体(参考《The Art of Scalability》),是一个叫 AKF 的公司的技术专家抽象总结的应用扩展的三个维度。理论上按照这三个扩展模式,可以将一个单体系统,进行无限扩展。

struct-miscro-akf

下面,我们分别简单的介绍下这三个维度的扩展模式.

  1. X 轴

水平复制,将单体服务水平拆分为多个无状态的实例,使用负载均衡来路由流量,该方式的开发成本近乎于零,实施非常快。

struct-akf-x

  1. Y 轴

基于不同的业务或功能拆分,当数据库的各项硬件指标达到上限后,系统的吞吐量就达到瓶颈,此时使用 X 轴的水平扩展已经无法提升性能了,因为瓶颈在数据库这边。这时就需要拆分系统功能,使得各组件的职责、分工更细,同时改造数据库,例如将数据库读写分离:

struct-akf-y-rw

如果此时数据库写的性能达到了上限,那就按照功能来拆分应用和数据库,例如把用户的个人信息、身份验证等功能拆分出一个子系统,再把文章、留言发布等功能拆分到另一个子系统,由无状态的业务层代码分开调用,并通过事务组合在一起,如下图所示:

struct-akf-y-app

当然,此时的拆分成本是非常高的,将一个整体的功能业务拆分为两个不同的业务,需要重构代码并做大量测试工作,上线部署也很复杂。其中数据模型做拆分、设计组件之间的 API 交互协议、数据迁移等都是难的事情。这个有点像数据库的垂直分表。

解决数据增长引发的性能下降问题,除了成本较高的 AKF Y 轴扩展方式外,沿 Z 轴扩展系统也很有效,它的实施成本更低一些,下面我们具体看一下。

  1. Z 轴

基于用户地理等方式的数据分区,比如一个互联网打车应用突然或了,用户量激增,集群模式撑不住了,那就按照用户请求的地区进行数据分区,北京、上海、四川等多建几个集群,这种方式其实有点类似数据库的水平分库水平分表。

struct-akf-z

前后端分离

前后端分离是一种软件架构设计模式,旨在将前端和后端的开发过程和职责进行分离。在传统的 Web 应用程序中,前端和后端的开发是紧密耦合的,前端代码通常与后端代码交织在一起。而在前后端分离架构中,前端和后端是独立开发和部署的,彼此通过 API 进行通信。前后端分离带来如下优点:

  • 更好的团队协作:前后端开发团队可以并行开发,互不干扰,提高开发效率。
  • 更好的扩展性:前端和后端可以独立扩展,满足不同的需求和负载。
  • 更好的可维护性:前后端的职责分离使得代码更易于理解、测试和维护。
  • 更好的用户体验:前端可以通过异步加载、缓存和前端路由等技术提供更流畅和快速的用户体验。

然而,前后端分离也带来了一些挑战,例如跨域问题、API 设计和维护、权限管理等。

无状态服务

无状态服务是一种软件架构设计模式,其中服务器不保存任何客户端的会话信息或状态。每个客户端请求都是独立的、自包含的,并且服务器不依赖于之前的请求状态来处理当前请求。无状态服务的设计理念是将状态和会话信息从服务器中解耦,使得服务可以水平扩展、高可用、易于维护和测试。无状态服务的特点如下:

  • 状态无关性:服务器不保存客户端的状态信息,每个请求都是独立的
  • 可伸缩性:由于无状态服务不保存任何状态信息,因此可以方便地进行水平扩展
  • 高可用性:无状态服务的无关性意味着它们可以通过多个服务器实例进行负载均衡,从而提高可用性
  • 容错性:由于无状态服务不依赖于特定的服务器实例或状态信息,因此在出现故障时可以更容易地进行故障转移和恢复

例如,将数据缓存、session 缓存从业务代码或微服务中拆分放到分布式缓存中存储,从而确保微服务可以按需扩展。

struct-miscro-nostatus

Restful 风格

REST(Representational State Transfer)是一种基于 Web 的软件架构风格,常用于设计分布式系统和构建 Web 服务。RESTful 架构是按照 REST 原则设计的应用程序或服务。其主要原则或特点如下:

  1. 资源的统一标识:每个资源都有一个唯一的标识符(URI),通过这个标识符可以对资源进行访问和操作。
  2. 资源的状态转化:客户端通过 HTTP 协议的请求方法(GET、POST、PUT、DELETE 等)对资源进行状态转化,例如获取资源、创建资源、更新资源和删除资源。
  3. 无状态的通信:服务器不保存客户端的会话状态,每个请求都包含了足够的信息来处理该请求,使得服务器能够水平扩展和实现高可用性。
  4. 使用标准的 HTTP 方法:RESTful API 使用标准的 HTTP 方法来定义资源的操作,例如使用 GET 方法获取资源,使用 POST 方法创建资源,使用 PUT 方法更新资源,使用 DELETE 方法删除资源。
  5. 使用标准的 HTTP 状态码:RESTful API 使用标准的 HTTP 状态码来表示请求的结果状态,例如 200 表示成功,404 表示资源不存在,500 表示服务器错误等。
  6. 轻量级通信:RESTful API 使用简单、轻量级的数据交换格式,通常使用 JSON 或 XML 来传输数据。

其他原则

  1. 单一服务内部功能高内聚低耦合:每个服务只完成自己职责内的任务,对于不是自己职责的功能交给其它服务来完成。
  2. 闭包原则(CCP):当我们需要改变一个微服务的时候,所有依赖都在这个微服务的组件内,不需要修改其他微服务。
  3. 服务自治、接口隔离原则: 尽量消除对其他服务的强依赖,这样可以降低沟通成本,提升服务稳定性。服务通过标准的接口隔离,隐藏内部实现细节。
  4. 持续演进原则: 微服务随着业务的增加需要不断的调整,服务粒度的控制需要根据实际情况来处理,必须粒度越小,整体的高可用技术架构就更加复杂,同时伴随着开发、测试、运维成本,所以在项目初始不需要过度优化。
  5. 拆分的过程尽量避免影响产品的日常功能迭代: 一边做产品功能迭代,一边完成服务化拆分,例如优先剥离比较独立的边界服务,从非核心的服务出发减少拆分对现有业务的影响,也给团队一个练习、试错的机会。
  6. 服务接口的定义要具备可扩展性: 服务接口的定义要具备可扩展性,否则在服务变更时会造成一些低级错误
  7. 避免环形依赖与双向依赖: 不要有服务之间的环形依赖或双向依赖
  8. 阶段性合并: 软件设计就是一个不断拆分和合并的过程,没有完美的架构,碰到问题就重新梳理领域边界,不断整合和纠正。3

拆分策略

拆分的本质是为了将复杂的问题简单化

拆分粒度和复杂度

  1. 弓箭原理

平衡拆分粒度可以从两方面进行权衡,一是业务发展的复杂度,二是团队规模的人数,从下面的图形图可以了解到只有当业务复杂度和团队人数足够大的时候,射出的服务拆分粒度这把剑才会飞的更远,发挥出最大的威力,否则拆分粒度过大只能拖垮整个团队。

struct-miscro-lidu

  1. 三个火枪手原则(人员配置)

一个服务或项目的开发人员配置三个人是最合理的:

  • 系统规模: 3 个人负责开发一个系统,系统的复杂度刚好达到每个人都能全面理解整个系统,又能够进行分工的粒度
  • 团队管理:3 个人可以形成一个稳定的备份,即使 1 个人休假或者调配到其他系统,剩余 2 个人还可以支撑
  • 技术提升:3 个人的技术小组既能够形成有效的讨论,又能够快速达成一致意见

该原则主要应用于微服务设计和开发阶段

功能维度

功能维度主要是划分清楚业务边界,采用的主要设计方法可以利用 DDD(领域驱动设计),通过领域模型指导微服务的拆分,整个步骤分为:

  • a. 找出领域实体和值对象等领域对象。
  • b. 找出聚合根(root),根据实体、值对象与聚合根的依赖关系,建立聚合
  • c. 根据业务及语义边界等因素,定义限界上下文
  • d. 每一个限界上下文可以拆分为一个对应的微服务,但也要考虑一些非功能因素

非功能维度

除了从功能维度考虑拆分策略之外,还需要从其他角度考虑拆分,一般来说是兼顾这两大类,然后根据实际场景做取舍:

  • 扩展性维度
  • 复用性:不同的业务里或服务里经常会出现重复的功能,比如每个服务都有鉴权、限流、安全及日志监控等功能,可以将这些通过的功能拆分出来形成独立的服务,也就是微服务里面的 API 网关
  • 高性能:将性能要求高或者性能压力大的模块拆分出来,避免性能压力大的服务影响其它服务。
  • 高可用:将可靠性要求高的核心服务和可靠性要求低的非核心服务拆分开来,然后重点保证核心服务的高可用。
  • 安全性:不同的服务可能对信息安全有不同的要求,因此把需要高度安全的服务拆分出来,进行区别部署,比如设置特定的 DMZ 区域对服务进行分区部署,可以更有针对性地满足信息安全的要求,也可以降低对防火墙等安全设备吞吐量、并发性等方面的要求,降低成本,提高效率。
  • 异构性:对于对开发语言种类有要求的业务场景,可以用不同的语言将其功能独立出来实现一个独立服务。

成本考虑

从结果论的角度考虑,你做了事情就需要体现出来该任务的合理性,那么微服务拆分之后就需要提供比拆分之前的好处, 比如

  • 维护成本或人力成本
  • 系统性能或瓶颈的上升
  • 团队的适应性变化更优
  • 系统发布效率的提升

落地实现

微服务应用平台

struct-miscro-platform

参考