微服务设计模式是一种指导微服务架构设计和开发的一系列原则和实践。微服务设计模式的目的是为了解决微服务架构中遇到的一些常见的问题和挑战,比如服务划分、服务通信、服务治理、服务测试等。微服务设计模式可以帮助我们构建出高效、可靠、可扩展、可维护的微服务系统。
本文将介绍以下十种微服务设计模式:
API 网关(Api Gateway Pattern)
服务发现与注册(Service Registration and Discovery Pattern)
断路器(Circuit Breaker Pattern)
隔板模式(Bulkhead Pattern)
命令和查询职责分离(CQRS Pattern)
事件驱动模式(Dvent Driven Pattern)
Saga 模式(Saga Pattern)
扼杀者模式(Strangler Pattern)
边车模式(Sidecar Pattern)
BFF 模式(Backend for Frontend Pattern)
API 网关是访问任何微服务的入口点,位于客户端和微服务之间,负责处理诸如鉴权、限流、重试、负载均衡、服务发现等通用功能,以及根据客户端的需求进行数据过滤、映射和聚合等操作。这种模式可以简化客户端的逻辑,减少网络开销,保护后端服务,以及实现不同级别的 API 接口。在 Spring Cloud 中我们可以使用 Zuul 或 Spring Cloud Gateway 来实现这一点。
应用场景
对于任何微服务调用 API 网关是唯一入口,简化客户端逻辑。
可以根据不同的规则将请求路由到不同的微服务上。
可以与 Eureka、Nacos、Ribbon 等注册中心集成,实现服务发现和负载均衡。
可以聚合后端的请求结果再发送给前端。
可以在不同通信协议的服务之间做协议转换。
可以把授权/认证功能从微服务中迁移到 API 网关上。
可以记录请求日志。
可以做服务限流以及服务熔断。
服务注册与发现是一种用于管理微服务实例地址变化的技术。它可以让微服务之间通过一个统一的服务名来进行通信,而不需要知道对方的具体位置。它主要包括两个组件:服务注册中心和服务发现客户端。
服务注册中心:服务注册中心是一个中心化的组件,负责存储所有微服务实例的位置信息(如 IP 地址和端口号),以及提供查询和更新这些信息的接口。每个微服务实例在启动时都会向服务注册中心注册自己的位置信息,并定期发送心跳消息来维持自己的在线状态。如果某个微服务实例停止或下线了,它会从服务注册中心注销自己的位置信息,或者由服务注册中心检测到并删除它。
服务发现客户端:服务发现客户端是一个分布式的组件,负责从服务注册中心获取所需微服务实例的位置信息,并根据负载均衡策略选择一个合适的实例进行调用。每个客户端或其他微服务在需要调用某个微服务时都会使用其服务名来请求服务发现客户端,由服务发现客户端返回一个可用的实例地址,并建立连接。
引入服务注册与发现可以给微服务架构带来很多好处,例如,
动态发现:我们可以动态地发现新加入或退出的微服务实例,而不需要手动配置或修改地址信息。
负载均衡:我们可以根据不同的算法(如轮询、随机、最少连接等)来选择最合适的微服务实例来处理请求,从而提高系统的性能和效率。
容错处理:我们可以检测并排除不可用或故障的微服务实例,从而提高系统的可靠性和稳定性。
常见的服务注册中心如下,
Eureka:Eureka 是 Netflix 开源的一款服务发现组件,是 Spring Cloud 老版本的核心组件之一,现在已经处于维护期。它提供了服务注册、服务发现和负载均衡等功能,具有高可用、高可靠、易于扩展的特点。适用于需要高可用、易于部署和维护的微服务架构。
Consul:Consul 是由 HashiCorp 开源的一款服务网格解决方案,提供了服务注册、服务发现和健康检查等功能,同时还支持多数据中心部署和分布式一致性协议。适用于需要多数据中心部署和强一致性的微服务架构。
Zookeeper:Zookeeper 是 Apache 开源的一款分布式协调服务,提供了命名服务、配置管理、分布式锁等功能。Zookeeper 具有高可用、高可靠、支持集群和多数据中心等特点。适用于需要分布式锁、分布式配置管理等功能的微服务架构。
Nacos:Nacos 是阿里巴巴开源的一款动态服务发现、配置管理和服务管理平台,支持 DNS-based 和 RPC-based 两种模式。Nacos 具有简单易用、高性能、高可扩展等特点。适用于需要动态配置管理和服务治理的微服务架构。
Etcd:Etcd 是一个分布式键值存储系统,基于 Raft 协议实现了强一致性和容错性。Etcd 可以作为服务注册中心使用,也可以作为配置中心或分布式锁等其他场景使用。适用于需要强一致性和高性能的微服务架构。
断路器是一种处理远程调用失败或超时的模式。由于微服务之间需要通过网络进行通信,因此可能会遇到网络故障、超时、拥塞等问题,导致远程调用失败或延迟。如果不及时处理这些问题,可能会造成雪崩效应(Cascading Failure),即一个失败的调用会引起其他调用的失败,最终导致整个系统崩溃。电路断路器模式可以避免这种情况,它类似于电路中的保险丝,当检测到某个远程调用出现故障时,就会切断该调用,防止进一步的损害,并尝试恢复该调用。在 Spring Cloud 中,我们可以使用 Hystrix 或 Resilient4J 来实现断路器。
断路器模式通常有三种状态:闭合(Closed)、打开(Open)和半开(Half-Open)。闭合状态表示远程调用正常工作,打开状态表示远程调用出现故障,半开状态表示远程调用正在恢复。电路断路器根据一定的条件和策略来切换这三种状态,比如故障次数、故障比例、故障时间等。电路断路器还可以提供一些备选方案来处理失败的调用,比如重试、降级、缓存等。
断路器行为类似于电路中的断路器。当连续的请求失败的次数超过阈值时,断路器将跳闸一段时间,并且在跳闸的这段时间内,所有远程服务调用的尝试都将立即失败。当超过了断路器跳闸时间之后,断路器将允许有限数量的测试请求通过。如果这些请求成功,则断路器将恢复正常操作。否则如果有一个请求失败,则断路器再次跳闸。对于一个应用试图尝试调用另一个远程服务或者获取共享资源,并且该操作很容易的失败的情况来说, 这个模式非常适用。
隔板模式(Bulkhead Pattern)通过根据需要调用的服务数量划分线程池,帮助处理与线程池相关的容错问题。例如我们在服务 A 中定义了一个 50 线程池,服务 A 会向服务 B 和 C 发出请求。因此服务 A 应该将 50 线程池分为 2 个(服务 B 25 个,服务 C 25 个),如果服务 C 不可用或需要较长时间来处理请求,则不会影响服务 B 调用,因为它有自己的线程池来执行作业。在 Spring Cloud 中,我们可以使用 Resilient4J 来实现这一点。
隔板模式类似于船体中一个个被隔离的分区。根据使用者负载和可用性要求,这些分区服务实例被分割到不同的组里面。这种设计模式有助于隔离故障(isolate failures), 并允许即使在故障期间仍可为某些使用者提供服务功能。
命令和查询职责分离(Command Query Responsibility Segregation,CQRS)模式,是一种将数据存储的读取操作和更新操作分离的模式。这种模式的目的是提高微服务的性能、可扩展性和安全性。CQRS 的基本思想是,对于不同的操作,可以使用不同的模型、服务和数据库。例如,读取操作可以使用一个优化了查询效率的数据库,而更新操作可以使用一个保证了数据一致性的数据库。这样就可以根据不同的需求来选择合适的技术和架构。
优点
可以根据读写比例来调整资源分配,提高系统的效率和响应速度。
可以针对不同的场景使用不同的数据模型和验证规则,提高系统的灵活性和可维护性。
可以降低数据冲突和并发问题的风险,提高系统的可靠性和安全性。
缺点
需要维护多个数据源之间的同步和一致性,增加了系统的复杂度和开发成本。
需要处理数据延迟和最终一致性的问题,可能影响用户体验和业务逻辑。
微服务中的事件驱动模式是一种让微服务之间通过发布和订阅事件来进行异步通信的模式。事件是一种表示系统中发生了某种变化或动作的消息,比如订单创建、支付完成、用户注册等。事件驱动模式的优点是可以降低微服务之间的耦合度,提高系统的可扩展性、性能和可靠性,以及支持复杂的业务逻辑和流程。事件驱动模式的挑战是需要保证事件的顺序、一致性、幂等性和可追溯性,以及处理分布式事务和异常情况。事件驱动模式中的事件一般是通过消息队列发出,例如 RabbitMQ 或 Apache Kafka。
事件驱动模式让微服务之间通过发布和订阅事件来进行异步通信,而不是直接调用或依赖其他微服务的接口。这样每个微服务只需要关注自己的业务逻辑,而不需要知道其他微服务的存在和状态。
众所周知,处理分布式系统很困难,尤其是涉及分布式事务时,二阶段提交是最好的选择,但由于其悲观锁的性质使其难以扩展、性能较低,所以就有了 Saga 模式,是一种在分布式事务场景中跨微服务管理数据一致性的方法。Saga 是一系列事务,用于更新每项服务并发布消息或事件来触发下一个事务步骤。如果某个步骤失败,则 Saga 将执行补偿事务,以抵消上一个事务的影响。这种模式可以保证数据的最终一致性,同时避免了长时间锁定资源的问题。有两种常见的 Saga 实现方法,即协调和编排。 每个方法都有自己的一组挑战和技术来协调工作流。
协调是协调 Saga 的一种方法,参与者在没有集中控制点的情况下交换事件。 通过协调,每个本地事务都会发布在其他服务中触发本地事务的域事件。。
好处
适用于只需很少参与者且不需要协调逻辑的简单工作流。
不需要额外的服务实现和维护。
不会引入单一故障点,因为责任在各个 Saga 参与者之间进行分配。
缺点
添加新步骤时,工作流可能会变得混乱,因为很难跟踪哪些 Saga 参与者侦听哪些命令。
由于 Saga 参与者必须使用彼此的命令,因此他们之间存在循环依赖的风险。
集成测试很困难,因为必须运行所有服务才能模拟事务。
编排是协调 Saga 的一种方法,在此方法中,中央控制器告诉 Saga 参与者要执行的本地事务。 Saga 编排器处理所有事务,并告知参与者根据事件执行哪项操作。 编排器执行 Saga 请求、存储和解释每个任务的状态,并通过补偿事务处理故障恢复。
好处
适用于只需很少参与者且不需要协调逻辑的简单工作流。
不需要额外的服务实现和维护。
不会引入单一故障点,因为责任在各个 Saga 参与者之间进行分配。
缺点
添加新步骤时,工作流可能会变得混乱,因为很难跟踪哪些 Saga 参与者侦听哪些命令。
由于 Saga 参与者必须使用彼此的命令,因此他们之间存在循环依赖的风险。
集成测试很困难,因为必须运行所有服务才能模拟事务。
扼杀者(Strangler Pattern)模式是一种将单体应用逐渐迁移到微服务架构的模式,它的灵感来源于一种缠绕并杀死它所依附的树木的藤蔓植物。扼杀者模式的基本思想是,通过创建单体应用的新版本来替换旧版本,同时保持外部 API 不变,让客户端感知不到任何变化。随着转换进度的推进,最终所有功能都被重构为微服务,新架构“扼杀”取代了原来的单体架构。
扼杀应用(Strangler Application)的步骤分为转换,共存和消灭三步:
转换(Transform ) — 用新方式创建一个新的平行的服务。
共存(Coexist )— 保持老服务,将老服务请求重定向到新服务,新服务逐步实现老服务的功能。
消灭(Eliminate ) — 移除老服务。
扼杀者模式的优点是可以渐进地进行微服务化,而不是一次性地重写整个应用,这样可以降低风险和成本,同时保持业务的连续性和稳定性。缺点是需要维护两套系统之间的兼容性和同步性,这可能会增加系统的复杂度和开发成本。
边车模式(Sidecar Pattern)是一种将一些与业务逻辑无关的功能(如日志、监控、配置、安全等)从主应用程序中分离出来,部署在同一个主机或容器中的一个独立的进程或服务中的模式。这样,主应用程序可以专注于自己的核心功能,而边车服务可以提供一些通用的功能,比如数据转换、网络通信、故障处理等。边车服务与主应用程序之间通过本地接口或共享内存来进行通信,从而实现高效和透明的协作。
边车模式的优点是可以降低微服务之间的耦合度,提高微服务的性能、可靠性和灵活性,以及简化微服务的开发和维护。缺点是需要额外的资源和管理成本,以及处理边车与主应用程序之间的通信和协调问题。
BFF 模式是一种为前端定制的后端的模式,它的全称是 Backend for Frontend。BFF 模式的目的是将后端的微服务根据不同的前端渠道和场景进行适配和聚合,提供给前端友好和统一的 API,从而提升前端的用户体验和开发效率。BFF 模式可以让前端与后端之间实现更好地解耦和协作。
BFF 模式的优点是可以根据不同的前端需求来定制和优化 API,避免了过度抽象或者冗余的 API,提高了网络传输和数据处理的效率。缺点是需要额外的开发和维护成本,每个前端渠道都需要一个对应的 BFF,可能会导致代码重复或者不一致。
应用场景
接口剪裁:后端提供的一个接口,对于不同的前端场景来说,需求的字段可能会不同。BFF 的剪裁能力使得后端只需要专注领域开发,不需要为前端定制不同的 API 接口,方便 API 生命周期的管理。
接口聚合:一个前端页面,往往会依赖多个后端接口,这样就需要和后端产生多次交互。BFF 可以对后端接口进行聚合,并处理依赖关系,使得前端一个页面只需要和后端交互一次。
以上的十种设计模式能帮助我们构建扩展性良好的软件系统,但是在生产实践中,我们还需要根据具体的业务场景和需求来引入合适的微服务设计模式。
最后感谢大家阅读,希望本文能对你有所帮助.
关注公众号【waynblog】每周分享技术干货、开源项目、实战经验、高效开发工具等,您的关注将是我的更新动力!