前言
这里的设计模式不同于我们熟悉的java程序面向对象的23种设计模式,解决方案的元素是类。而是一种更高层的设计模式,他们的解决方案由相互协作的服务构成。
一、什么是模式语言?
微服务架构的模式语言是一组模式,可帮助架构师使用微服务架构构建应用程序。模式语言首先帮助架构师决定是否使用微服务架构。它描述了单体架构和微服务架构以及他们的利弊。然后如果微服务架构非常适合当前的应用程序,那么模式语言可以帮助架构师通过解决各种架构和设计问题来有效使用它。
二、微服务设计模式总览图
这是microservices官网的设计模式总览图。
左边的是应用程序架构模式组:单体架构和微服务架构。
其他的模式都属性微服务模式组,解决了选择微服务架构模式所导致的问题。
三、各模式说明
1、应用架构模式(Application architecture patterns)
解决的问题:您应该为应用程序选择哪一种架构?
模式:单体架构
采用单一部署单元的方式架构应用
模式:微服务架构
将应用程序构建为松耦合、可独立部署的一组服务。
微服务架构的利弊
微服务架构的好处:
- 使大型复杂应用程序可以持续交付和持续部署
通过以下三种方式实现持续交付和持续部署:
1、可测试性
2、可部署性
3、开发团队能够自主且松散耦合 - 每个服务都相对较小并容易维护
- 服务可以独立部署
- 服务可以独立扩展
相比单体的采用的X轴扩展的实例克隆,微服务架构可以让每个服务都可以部署在适合它需求特点的硬件之上。
比如有些组件是CPU运算密集型的,有些可能需要更多的内存。 - 微服务架构可以实现团队的自治
- 更容易实验和采纳新的技术
- 更好的容错性
比如某个服务内存泄露不会影响其他服务。而单体应用一个故障组件往往会拖垮整个系统。
微服务架构的弊端:
- 服务的拆分和定义是一个挑战
- 分布式系统带来的各种复杂性,使开发、测试和部署变得更困难
- 部署跨越多个服务的功能时,需要谨慎地协调更多开发团队
- 开发者需要思考到底在应用的什么阶段使用微服务架构
- 资源消耗更多
- 需要维护更高数量级的活动组件(服务、数据库、进程、容器、框架)
- 团队结构需要做出适应性调整
何时使用微服务架构
- 大规模 Web 应用开发。
- 跨团队企业级应用协作开发。
- 长期收益优先于短期收益。
- 团队拥有能够设计微服务架构的软件架构师或高级工程师。
2、微服务模式组
2.1 服务拆分相关模式(Decomposition)
解决的问题:如何把应用拆分为若干个服务?
- 根据业务能力拆分(Decompose by business capability) :根据业务能力界定服务的范围
- 根据领域的子域拆分(Decompose by subdomain):根据领域驱动设计中子域的概念界定服务的范围
2.2 通信相关模式(Communication patterns)
2.2.1 通信风格
解决的问题:应该选择怎样的通信机制来进行服务间通讯和外部客户端通讯?
- 轻量级HTTP通信 -(基于 HTTP 的 RESTful API)
- 远程过程调用(Remote Procedure Invocation) - 使用基于 RPI 协议的服务间通讯方式
- 消息(Messaging) - 使用异步消息进行服务间通讯
- 领域独用协议(Domain-specific protocol)new - 使用特定领域的通讯协议(如 SIP,等)
主流方案:基于Feign的HTTP调用
2.2.2 外部 API
解决问题:如何处理外部客户端与服务之间的通讯?
- API 网关(API gateway) - 为每一类客户端提供一个访问服务的独特接口
- 服务前端的后端(Backend for front-end) - 为每一类客户端都提供一个独立的 API 网关
主流方案:Zuul,Gateway
2.2.3 服务发现
解决问题:一个基于 RPI 的客户端如何在网络上发现服务实例的位置?
- 客户端服务发现(Client-side discovery) - 客户端通过直接查询服务注册表获取服务实例的位置
- 服务器端服务发现(Server-side discovery) - 路由模块通过查询服务注册表获取服务实例的位置
- 服务注册表(Service registry) - 一个记录了服务实例位置的数据
- 自注册(Self registration) - 服务实例自己完成向服务注册表的注册
- 第三方注册(3rd party registration) - 通过第三方模块来进行服务实例信息到服务注册表的注册过程
主流方案:
eureka,consul,nacos,zookeeper
2.2.4 可靠性
解决问题:如何避免由于服务故障或网络中断所引起的故障蔓延到其他服务?
- 断路器(Circuit Breaker) - 当远端服务返回的故障率超过一定的阀值时,客户端代理(比如 API 网关)对远程服务的调用将立刻返回失败的信息
主流方案:Hystrix, Reselience4J,Sentinel
2.3 数据管理相关模式(Data management)
解决问题: 如何实现数据一致性和查询?
- 每服务数据库(Database per Service) - 每个服务都拥有它私有的数据库
- 共享数据库(Shared database) - 服务之间共享同一个数据库
- 事件驱动架构(Event-driven architecture) - 使用事件来维护服务间的数据一致性
- 事件溯源(Event sourcing) - 以一连串事件的方式来持久化聚合
- 事务日志跟踪(Transaction log tailing) - 跟踪数据库的日志变更并由此对外发布消息
- 数据库触发器(Database triggers) - 使用触发器来捕获对数据的修改
- 应用程序事件(Application events) - 应用程序从消息队列获取事件并插入数据库中
- 命令查询职责分离(CQRS) - 维护一个或者多个重要的数据视图以供高效率的查询
- Saga - 使用异步消息来协调一系列本地事务,从而维护多个服务之间的数据一致性
主流方案:
每个服务都拥有它私有的数据库,通过API组合或命令查询职责分离(CQRS)来进行多个服务间的查询,
通过Saga保证分布式下的数据一致性。
主流的分布式事务实现方案:2PC,TCC,Saga,可靠消息最终一致,最大努力通知
主流分布式事务框架:Alibaba Seata,tcc-transaction ,Hmily,ByteTCC,LCN
2.4 服务部署的相关模式(Deployment patterns)
解决的问题:如何部署应用程序的服务?
- 单主机上部署服务的多个实例(Multiple service instances per host) - 把服务的多个实例部署在一台主机上
- 单主机上部署服务的单个实例(Single service instance per host) - 把服务的单一实例部署在它独享的主机上
- 服务实例与虚拟机一一对应(Service instance per VM) - 把服务的单一实例部署在它独享的虚拟机上
- 服务实例与容器一一对应(Service instance per Container) - 把服务的单一实例部署在它独享的容器上
- 无服务器部署(Serverless deployment) - 使用无服务器部署平台部署服务实例
- 服务部署平台(Service deployment platform)new - 使用提供服务抽象能力的高度自动化部署平台部署服务实例
主流方案:
前期都是利用开源产品jenkins快速搭建CI/CD持续集成、持续发布的平台。后期根据团队情况,自主研发高度自动化部署的服务发布平台,利用简单的界面来部署和管理服务。
2.5 可观测性的相关模式(Observability)
解决的问题:如何掌握一个运行中微服务应用的行为并进行有效的故障排错?
- 应用日志(Log aggregation)new - 聚合应用程序产生的日志文件
- 应用指标(Application metrics)new - 在代码中实现收集应用运营过程中各类指标的功能
- 审计日志(Audit logging)new - 把用户行为记录在数据库中供日后核查
- 分布式追踪(Distributed tracing)new - 在服务代码中针对每一个外部访问,都分配一个唯一的标识符,并在跨服务访问时传递这个标识符以供追踪分布式引发的问题。例如,当通过一个集中式服务处理外部请求时,记录请求本身的信息以及请求的开始和结束时间。
- 异常追踪(Exception tracking)new - 把所有服务程序代码触发的异常信息都汇聚到集中的异常追踪服务,并根据一定的逻辑对开发者或运维人员发出通知。
- 健康检查 API(Health check API)new - 一个监控服务可调用的 API,通常返回服务健康度信息,或对 ping 等心跳检查请求做出响应。
常见的实现方案:
日志收集使用ELK技术栈,调用链追踪用OpenTracing/Zipkin/Skywalking,Metrics监控使用Promethus和Grafana。Spring Cloud Slueth 以及 Zipkin server 都是通用实现。
健康检测可以痛殴Spring Boot Actuator 实现的/health 端点。
2.6 可测试性的相关模式(Testing)
解决的问题:如何更便捷的测试?
- 服务组件测试(Service Component Test)- 在隔离的环境中测试服务
- 服务集成协议测试(Service Integration Contract Test)
测试金字塔:
从上到下依次是探索性测试、端到端测试、组件测试、集成测试、单元测试
2.7 安全相关模式(Security)
解决的问题:如何向服务实例传递访问客户端的身份信息?
- 访问令牌(Access Token) - 服务实例通过访问令牌来安全地传递客户端的身份信息
主流方案:Spring Security,OAuth2.0,JWT
2.8 需要关注的边界问题(Cross cutting concerns)
解决的问题: 如何处理服务实例与外界交互的问题?
- 微服务的基底(Microservice chassis) - 一个用于服务实例与外界交互和简化服务开发的框架
- 配置信息外部化(Externalized configuration) - 把类似数据库连接、访问密钥等配置信息外部化
主流方案:
微服务基底一般是针对公司的技术储备和实际情况,自主封装的一套加快微服务架构开发落地的脚手架基础框架。可以处理许多问题,包括:外部化配置、健康检查、应用程序指标(CPU,内存,TPS,QPS)、服务发现、断路器、分布式追踪等通用性问题。
外部化配置方案:Spring Cloud Config、阿里的Nacos、携程的Apollo
2.9 UI 模式(UI patterns)
解决的问题:如何将源自多个服务的信息组织在一起生成 UI 界面或 Web 页面?
- 服务器端页面碎片化元素构建(Server-side page fragment composition) - 在服务器端通过编排由多个业务或领域相关后端服务生成的 HTML 片段来构建前端输出的页面内容
- 客户端 UI 构建(Client-side UI composition) - 在客户端通过编排由多个业务或领域相关 UI 组件生成的 HTML 片段来构建前端输出的页面内容
主流方案:
一般都是采用利用swagger + 注册中心+网关,将所有微服务的API整合到一起。
2.10 绞杀者模式(Strangler)
解决的问题:如何将一个单体应用重构成微服务架构的应用?
- 绞杀者模式(Strangler) - 单体应用逐渐又服务组成的绞杀者应用程序取代,最终,单体应用完全被绞杀者应用程序取代或成为另一个微服务。
总结
本文主要介绍了微服务架构下的核心模式,并说明每个模式解决的问题以及相应的主流技术实现方案。
希望能帮大家加深微服务架构的理解,对工作中微服务架构的实施落地、问题思考、技术选型有一点帮助。
参考:
微服务架构及其最重要的 10 个设计模式
微服务的模式语言微服务架构及设计模式