微服务设计模式与开源工具应用解析

1. 服务发现模式

微服务实例启动时通常会被分配动态 IP 地址,这使得客户端向微服务发送请求变得困难。为解决该问题,可引入服务发现服务这一组件,它能跟踪当前可用的微服务及其实例的 IP 地址。

解决方案要求

  • 自动注册/注销微服务及其实例。
  • 客户端能向微服务的逻辑端点发送请求,请求将被路由到可用的微服务实例之一。
  • 对微服务的请求需在可用实例间进行负载均衡。
  • 能够检测当前不健康的实例,避免将请求路由到这些实例。

实现策略

  • 客户端路由 :客户端使用与服务发现服务通信的库,以确定要发送请求的合适实例。
  • 服务器端路由 :服务发现服务的基础设施还会公开一个反向代理,所有请求都将发送到该代理,由其代表客户端将请求转发到合适的微服务实例。

2. 边缘服务器模式

问题

在微服务系统中,通常希望将部分微服务暴露给外部,同时隐藏其余微服务,并且要保护暴露的微服务免受恶意客户端请求的影响。

解决方案

在系统中添加一个边缘服务器组件,所有传入请求都将通过该服务器。边缘服务器通常像反向代理一样工作,并可与发现服务集成以提供动态负载均衡功能。

解决方案要求

  • 隐藏不应暴露在其上下文之外的内部服务,仅将请求路由到配置为允许外部请求的微服务。
  • 暴露外部服务并保护它们免受恶意请求,使用 OAuth、OIDC、JWT 令牌和 API 密钥等标准协议和最佳实践来确保客户端的可信度。

3. 响应式微服务模式

问题

传统的 Java 开发中,常使用阻塞 I/O 实现同步通信,如基于 HTTP 的 RESTful JSON API。当并发请求数量增加时,服务器可能会耗尽操作系统中的可用线程,导致响应时间延长甚至服务器崩溃。在微服务架构中,这种问题通常会更严重。

解决方案

使用非阻塞 I/O,确保在等待另一个服务(如数据库或其他微服务)处理时不分配线程。

解决方案要求

  • 尽可能使用异步编程模型,发送消息时无需等待接收方处理。
  • 如果更喜欢同步编程模型,可使用响应式框架,通过非阻塞 I/O 执行同步请求,在等待响应时不分配线程,使微服务更易于扩展以处理增加的工作负载。
  • 微服务必须设计为具有弹性和自我修复能力。弹性意味着即使依赖的服务之一失败,也能产生响应;自我修复意味着当失败的服务恢复正常后,微服务能够恢复使用它。

4. 中央配置模式

问题

传统上,应用程序与其配置一起部署,在微服务架构中,会面临获取所有运行微服务实例的完整配置信息以及正确更新受影响实例配置的难题。

解决方案

在系统中添加一个配置服务器组件,用于存储所有微服务的配置。

解决方案要求

能够在一个地方存储一组微服务的配置信息,并为不同环境(如开发、测试、质量保证和生产)设置不同的配置。

5. 集中式日志分析模式

问题

传统应用程序将日志事件写入本地服务器的日志文件,在微服务架构中,难以全面了解系统情况、发现微服务实例的问题以及确定问题的根源。

解决方案

添加一个能够管理集中式日志记录的组件,该组件应具备检测新微服务实例、收集日志事件、以结构化和可搜索的方式解释和存储日志事件以及提供查询和分析日志事件的 API 和图形工具的能力。

解决方案要求

  • 微服务将日志事件流式传输到标准系统输出(stdout),便于日志收集器查找日志事件。
  • 微服务使用分布式跟踪设计模式中描述的关联 ID 标记日志事件。
  • 定义规范的日志格式,使日志收集器在将从微服务收集的日志事件存储到中央数据库之前将其转换为规范格式,以便进行查询和分析。

6. 分布式跟踪模式

问题

在处理外部请求时,需要跟踪微服务之间流动的请求和消息,以确定故障的根源、查找与特定实体相关的日志消息以及识别导致响应时间过长的微服务。

解决方案

确保所有相关请求和消息都标记有共同的关联 ID,并且该关联 ID 是所有日志事件的一部分。基于关联 ID,可使用集中式日志服务查找所有相关日志事件。同时,需要收集请求、响应和消息进入和离开每个微服务的时间戳,以便分析调用链中的延迟。

解决方案要求

  • 为所有传入或新的请求和事件分配唯一的关联 ID,并将其放置在知名位置,如具有标准化名称的标头中。
  • 微服务在发出请求或发送消息时,必须将关联 ID 添加到请求和消息中。
  • 所有日志事件必须以预定义的格式包含关联 ID,以便集中式日志服务能够从日志事件中提取关联 ID 并使其可搜索。
  • 为请求、响应和消息进入和离开微服务实例创建跟踪记录。

7. 断路器模式

问题

使用同步通信的微服务系统可能会面临连锁故障。如果一个微服务停止响应,其客户端可能也会出现问题,并且故障可能会递归传播,导致系统的大部分部分出现故障。

解决方案

添加一个断路器,当检测到调用的服务出现问题时,阻止调用方发出新的外部请求。

解决方案要求

  • 若检测到服务问题,立即打开断路器并快速失败(无需等待超时)。
  • 定期进行故障纠正探测(半开电路),即允许单个请求通过,以查看服务是否恢复正常运行。
  • 若探测发现服务恢复正常运行,则关闭断路器,使系统具有弹性和自我修复能力。

8. 控制循环模式

问题

在包含大量微服务实例的系统中,手动检测和纠正微服务实例的问题(如崩溃或挂起)非常困难。

解决方案

在系统中添加一个控制循环组件,该组件会不断观察系统的实际状态,并将其与操作员指定的期望状态进行比较。如果两者不同,它将采取行动使实际状态与期望状态一致。在容器环境中,通常使用 Kubernetes 等容器编排器来实现此模式。

解决方案要求

持续监控系统的实际状态,并与期望状态进行比较,当两者存在差异时采取相应措施使实际状态符合期望状态。

9. 集中式监控和警报模式

问题

当观察到的响应时间或硬件资源使用情况变得不可接受时,很难发现问题的根源,需要能够分析每个微服务的硬件资源消耗情况。

解决方案

在系统中添加一个监控服务组件,该组件能够收集每个微服务实例级别的硬件资源使用指标。

解决方案要求

  • 能够从系统使用的所有服务器(包括自动扩展服务器)收集指标。
  • 能够检测新启动的微服务实例并开始收集其指标。
  • 提供用于查询和分析收集指标的 API 和图形工具。
  • 能够定义在指定指标超过指定阈值时触发的警报。

10. 软件使能工具

有许多优秀的开源工具可帮助应对微服务带来的挑战,以下是设计模式与对应开源工具的映射表:
| 设计模式 | Spring Boot | Spring Cloud | Kubernetes | Istio |
| — | — | — | — | — |
| 服务发现 | | Netflix Eureka 和 Spring Cloud LoadBalancer | Kubernetes kube - proxy 和服务资源 | |
| 边缘服务器 | | Spring Cloud Gateway 和 Spring Security OAuth | Kubernetes Ingress 控制器 | Istio 入口网关 |
| 响应式微服务 | Project Reactor 和 Spring WebFlux | | | |
| 中央配置 | | Spring Config Server | Kubernetes ConfigMaps 和 Secrets | |
| 集中式日志分析 | | Elasticsearch、Fluentd 和 Kibana(可与 Kubernetes 一起部署和配置) | | |
| 分布式跟踪 | Micrometer Tracing 和 Zipkin | | | Jaeger |
| 断路器 | | Resilience4j | | 异常检测 |
| 控制循环 | | | Kubernetes 控制器管理器 | |
| 集中式监控和警报 | | | | Kiali、Grafana 和 Prometheus |

11. 其他重要考虑因素

DevOps 的重要性

微服务架构的一个好处是能够实现更短的交付时间,甚至实现新版本的持续交付。为了实现这一目标,需要建立一个开发和运维紧密合作的组织,遵循“你构建它,你运行它”的原则。此外,团队还需要自动化交付链,即构建、测试、打包和部署微服务到不同环境的步骤,这被称为设置交付管道。

组织方面和康威定律

康威定律指出,任何设计系统的组织都会产生一个结构与其组织通信结构相同的设计。传统的基于技术专长组织 IT 团队的方法通常会导致大型单体应用程序,而要成功交付基于微服务架构的应用程序,组织需要转变为以一个或一组相关微服务为工作单元的团队,团队应具备这些微服务所需的技能。

单体应用程序分解为微服务

将单体应用程序分解为一组协作的微服务是一项困难且昂贵的决策。如果分解不当,可能会导致交付缓慢、性能不佳和数据不一致等问题。一种较好的方法是应用领域驱动设计及其有界上下文的概念,确保微服务有自己明确的数据模型。

API 设计的重要性

如果一组微服务公开一个公共的外部可用 API,那么该 API 应易于理解,并遵循以下准则:
- 多个 API 中使用的相同概念应在命名和数据类型方面具有相同的描述。
- API 应能够以独立但可控的方式发展,通常需要应用适当的版本控制方案,如语义化版本控制(SemVer),允许 API 的客户端按自己的节奏迁移到新的主要版本。

从本地部署到云的迁移路径

许多公司目前在本地运行其工作负载,但正在寻找将部分工作负载迁移到云的方法。由于大多数云提供商现在提供 Kubernetes 即服务,一种有吸引力的迁移方法是先将工作负载迁移到本地的 Kubernetes 中(无论是否采用微服务架构)。

综上所述,微服务架构带来了诸多挑战,但通过合理运用设计模式和开源工具,以及考虑相关的组织和技术因素,可以有效地应对这些挑战,实现高效、稳定的微服务系统。

12. 设计模式与工具的综合运用流程

为了更清晰地展示如何综合运用上述设计模式和工具来构建微服务系统,下面给出一个简化的流程图:

graph LR
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px

    A(业务需求分析):::process --> B(选择设计模式):::process
    B --> C{选择开源工具}:::process
    C -->|服务发现| D1(Netflix Eureka/Spring Cloud LoadBalancer/Kubernetes kube - proxy):::process
    C -->|边缘服务器| D2(Spring Cloud Gateway/Spring Security OAuth/Kubernetes Ingress 控制器/Istio 入口网关):::process
    C -->|响应式微服务| D3(Project Reactor/Spring WebFlux):::process
    C -->|中央配置| D4(Spring Config Server/Kubernetes ConfigMaps 和 Secrets):::process
    C -->|集中式日志分析| D5(Elasticsearch/Fluentd/Kibana):::process
    C -->|分布式跟踪| D6(Micrometer Tracing/Zipkin/Jaeger):::process
    C -->|断路器| D7(Resilience4j/异常检测):::process
    C -->|控制循环| D8(Kubernetes 控制器管理器):::process
    C -->|集中式监控和警报| D9(Kiali/Grafana/Prometheus):::process
    D1 --> E(开发与集成):::process
    D2 --> E
    D3 --> E
    D4 --> E
    D5 --> E
    D6 --> E
    D7 --> E
    D8 --> E
    D9 --> E
    E --> F(测试与优化):::process
    F --> G(部署上线):::process
    G --> H(持续监控与维护):::process
    H --> I{是否有新需求}:::process
    I -->|是| A
    I -->|否| J(保持运行):::process

这个流程图展示了从业务需求分析开始,选择合适的设计模式和开源工具,进行开发与集成,经过测试优化后部署上线,再到持续监控维护的完整流程。如果在运行过程中出现新的业务需求,将重新回到需求分析阶段,形成一个闭环的迭代过程。

13. 各设计模式的操作要点总结

服务发现模式

  • 操作步骤
    1. 部署服务发现服务(如 Netflix Eureka 或使用 Kubernetes 的 kube - proxy 和服务资源)。
    2. 微服务实例启动时,自动向服务发现服务注册自身信息(IP 地址、端口等)。
    3. 客户端通过服务发现服务获取目标微服务的可用实例信息。
    4. 客户端根据负载均衡策略选择一个实例发送请求。

边缘服务器模式

  • 操作步骤
    1. 部署边缘服务器(如 Spring Cloud Gateway 或 Kubernetes Ingress 控制器)。
    2. 配置边缘服务器,指定哪些微服务可以对外暴露,哪些需要隐藏。
    3. 集成安全机制(如 OAuth、JWT 等),对外部请求进行身份验证和授权。
    4. 边缘服务器接收所有外部请求,并将其路由到合适的微服务实例。

响应式微服务模式

  • 操作步骤
    1. 选择响应式框架(如 Project Reactor 和 Spring WebFlux)。
    2. 使用异步编程模型编写微服务代码,避免阻塞 I/O。
    3. 配置微服务的弹性和自我修复机制,如设置重试策略、熔断机制等。

中央配置模式

  • 操作步骤
    1. 部署配置服务器(如 Spring Config Server 或使用 Kubernetes 的 ConfigMaps 和 Secrets)。
    2. 将微服务的配置信息存储在配置服务器中,为不同环境设置不同的配置。
    3. 微服务启动时,从配置服务器获取所需的配置信息。
    4. 当配置发生变化时,微服务能够动态更新配置。

集中式日志分析模式

  • 操作步骤
    1. 部署日志收集和存储组件(如 Elasticsearch、Fluentd 和 Kibana)。
    2. 微服务将日志事件输出到标准系统输出(stdout)。
    3. 日志收集器(如 Fluentd)收集微服务的日志事件,并将其发送到中央存储(如 Elasticsearch)。
    4. 使用 Kibana 等工具进行日志的查询和分析。

分布式跟踪模式

  • 操作步骤
    1. 为每个请求分配唯一的关联 ID。
    2. 在请求和消息中传递关联 ID。
    3. 微服务在处理请求时,记录关联 ID 到日志事件中。
    4. 使用分布式跟踪工具(如 Zipkin 或 Jaeger)收集和分析跟踪数据。

断路器模式

  • 操作步骤
    1. 在调用微服务的客户端代码中添加断路器(如 Resilience4j)。
    2. 配置断路器的参数,如失败阈值、重试间隔等。
    3. 当断路器检测到服务调用失败次数超过阈值时,打开断路器,快速失败。
    4. 定期进行半开状态探测,若服务恢复正常,则关闭断路器。

控制循环模式

  • 操作步骤
    1. 部署控制循环组件(如 Kubernetes 控制器管理器)。
    2. 定义系统的期望状态(如微服务的副本数量、资源使用限制等)。
    3. 控制循环组件持续监控系统的实际状态,并与期望状态进行比较。
    4. 当实际状态与期望状态不一致时,控制循环组件采取相应的调整措施。

集中式监控和警报模式

  • 操作步骤
    1. 部署监控服务(如 Kiali、Grafana 和 Prometheus)。
    2. 配置监控指标的收集规则,从微服务实例和服务器收集硬件资源使用情况、响应时间等指标。
    3. 设置警报规则,当指标超过阈值时触发警报。
    4. 使用图形工具(如 Grafana)可视化监控数据,进行分析和决策。

14. 微服务架构实施的注意事项

团队协作方面

  • 开发和运维团队需要紧密合作,遵循 DevOps 原则,共同负责微服务的整个生命周期。
  • 团队成员需要具备跨领域的技能,如开发、运维、数据库管理等。

架构设计方面

  • 在分解单体应用程序为微服务时,要谨慎使用领域驱动设计和有界上下文的概念,确保微服务的边界清晰。
  • API 设计要遵循统一的规范和版本控制方案,方便客户端使用和升级。

技术选型方面

  • 选择开源工具时,要考虑其社区活跃度、文档完整性、性能和稳定性等因素。
  • 不同的设计模式和工具可能存在兼容性问题,需要进行充分的测试和验证。

安全方面

  • 边缘服务器要加强安全防护,使用标准的安全协议和最佳实践,防止恶意攻击。
  • 对微服务之间的通信进行加密,保护数据的安全性和完整性。

通过遵循这些注意事项,可以提高微服务架构实施的成功率,减少潜在的风险和问题。

总之,微服务架构是一个复杂但强大的技术体系,通过合理运用各种设计模式和开源工具,以及注重团队协作、架构设计、技术选型和安全等方面的问题,可以构建出高效、稳定、可扩展的微服务系统,满足不断变化的业务需求。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐