老司机避坑指南:如何快速搞定微服务架构?

本文涉及的产品
云原生网关 MSE Higress,422元/月
注册配置 MSE Nacos/ZooKeeper,118元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介:
如今,微服务架构已经成为了现代应用开发的首选。虽然它能够解决大部分的程序问题,但是它并非一颗百试不爽的“银弹”。

ccb77af32516f572688ad48f3cb6a4ed866a7a44

在采用这种架构之前,我们应当事先了解可能出现的各种问题及其共性,预先为这些问题准备好可重用的解决方案。

那么,在开始深入讨论微服务的不同设计模式之前,让我们先了解一下微服务架构的一些构建原则:

 ●  可扩展性
 ●  可用性
 ●  弹性
 ●  独立、自主性
 ●  去中心化治理
 ●  故障隔离
 ●  自动调配
 ●  通过 DevOps 实现持续交付

在遵循上述各条原则的同时,我们难免会碰到一些挑战。下面我们来具体讨论可能出现的各种问题、及其解决方案。

分解模式

按照业务功能分解

问题:微服务是有关松散耦合的服务,它采用的是单一职责原则。虽然我们在逻辑原理上都知道要将单个应用分成多个小块,但是在实际操作中,我们又该如何将某个应用程序成功分解成若干个小的服务呢?

解决方案:有一种策略是按照业务功能进行分解。此处的业务功能是指能够产生价值的某种业务的最小单位。那么一组给定业务的功能划分则取决于企业本身的类型。

例如,一家保险公司的功能通常会包括:销售、营销、承保、理赔处理、结算、合规等方面。每一个业务功能都可以被看作是一种面向业务、而非技术的服务。

按照子域分解

问题:按照业务功能对应用程序进行分解只是一个良好的开端,之后您可能会碰那些不易分解的所谓“神类”(God Classes)。这些类往往会涉及到多种服务。

例如,订单类就会被订单管理、订单接受、订单交付等服务所使用到,那么我们又该如何分解呢?

解决方案:对于“神类”的问题,DDD(Domain Driven Design,领域驱动设计)能够派上用场。

它使用子域(Subdomain)和边界上下文(Bounded Context)的概念来着手解决。

DDD 会将企业的整个域模型进行分解,并创建出多个子域。每个子域将拥有一个模型,而该模型的范围则被称为边界上下文。那么每个微服务就会围绕着边界上下文被开发出来。

注意:识别子域并不是一件容易的事,我们需要通过分析业务与组织架构,识别不同的专业领域,来对企业加强了解。

刀砍模式(Strangler Pattern)

问题:前面我们讨论的设计模式一般适用于针对那些“白手起家”的 Greenfield 应用进行分解。

但是我们真实接触到的、约占 80% 的是 Brownfield 应用,即:一些大型的、单体应用(Monolithic Application)。

由于它们已经被投入使用、且正在运行,如果我们简单按照上述方式,同时对它们进行小块服务的分解,将会是一项艰巨的任务。

解决方案:此时,刀砍模式(Strangler Pattern)就能派上用场了。我们可以把扼杀模式想象为用刀砍去缠在树上的藤蔓。

该方案适用于那些反复进行调用的 Web 应用程序。对于每一个 URI(统一资源标识符)的调用来说,单个服务可以被分解为不同的域和单独的子服务。其设计思想是一次仅处理一个域。

这样,我们就可以在同一个 URI 空间内并行地创建两套独立的应用程序。最终,在新的应用重构完成后,我们就能“刀砍”或替换掉原来的应用程序,直到最后我们可以完全关闭掉原来的单体应用。

22b22263f3049c10307fe1bb0d63e738718c9a16

集成模式

API 网关模式

问题:当一个应用程序被分解成多个小的微服务时,我们需要关注如下方面。

具体如下:

 ●  如何通过调用多个微服务,来抽象出 Producer(生产者)的信息。
 ●  在不同的渠道上(如电脑桌面、移动设备和平板电脑),应用程序需要不同的数据来响应相同的后端服务,比如:UI(用户界面)就可能会有所不同。
 ●  不同的 Consumer(消费者)可能需要来自可重用式微服务的不同响应格式。谁将去做数据转换或现场操作?
 ●  如何处理不同类型的协议?特别是一些可能不被 Producer 微服务所支持的协议。

解决方案:API 网关将有助于解决在微服务实施过程中所涉及到的上述关注点。

具体如下:

 ●  API 网关是任何微服务调用的统一入口。
 ●  它像代理服务一样,能够将一个微服务请求路由到其相关的微服务处,并抽象出 Producer 的细节。
 ●  它既能将一个请求扇出(fan out,输出)到多个服务上,也能汇总多个结果,并发回给 Consumer。
 ●  鉴于通用 API 无法解决 Consumer 的所有请求,该方案能够为每一种特定类型的客户端创建细粒度的 API。
 ●  它也可以将某种协议请求(如:AMQP)转换为另一种协议(如:HTTP),反之亦然,从而方便了 Producer 和 Consumer 的处理。
 ●  它也可以将认证与授权存储库从微服务中卸载出去。

聚合器模式

问题:虽然我们已经在 API 网关模式中讨论了如何解决聚合数据的问题,不过我们仍将做进一步的讨论。

当我们将业务功能分解成多个较小的逻辑代码块时,有必要思考每个服务的返回数据是如何进行协作的。

显然,该责任不会留给 Consumer,那么我们就需要理解 Producer 应用的内部实现。

解决方案:聚合器模式将有助于解决该问题。它涉及到如何聚合来自不同服务的数据,然后向 Consumer 发送最终响应。

具体说来,我们有如下两种实现方法:

 ●  复合微服务(Composite Microservice) 将会去调用全部所需的微服务,整合各种数据,并在回传之前转换数据。
 ●  API 网关(API Gateway) 也能对多个微服务的请求进行 Partition(分区),并在发送给 Consumer 之前聚合数据。

我们建议:如果您用到了任何业务逻辑的话,请选用复合微服务;否则请采用 API 网关方案。

客户端 UI 合成模式

问题:当各种服务按照业务功能和子域被分解开发时,它们需要根据用户体验的预期效果,从一些不同的微服务中提取数据。

在过去的单体应用中,我们只要从 UI 到后端服务的唯一调用中获取所有的数据,并刷新和提交到 UI 页面上便可。如今,情况则不同了。

解决方案:对于微服务来说,UI 必须被设计成单屏、单页面的多段、多区域的结构。

每一段都会去调用单独的后端微服务,以提取数据。像 Angular JS 和 React JS 之类的框架都能够实现为特定的服务合成 UI 组件。

通过被称为单页应用(Single Page Applications,SPA)的方式,它们能够使得应用程序仅刷新屏幕的特定区域,而不是整个页面。

数据库模式

按服务分配数据库

问题:您可能会碰到如何定义数据库架构的微服务问题。

下面是具体的关注点:

 ●  服务必须是松散耦合的,以便能够被二次开发、部署和独立扩容。
 ●  各个业务交易需要在横跨多个服务时,仍保持不变。
 ●  某些业务交易需要从多个服务中查询到数据。
 ●  数据库有时需要根据规模需求被复制与分片。
 ●  不同的服务具有不同的数据存储需求。

解决方案:为了解决上述需求,我们需要通过设计为每个微服务配备一个独享的数据库模式。

即:该数据库仅能被其对应微服务的 API 单独访问,而不能被其他服务直接访问到。

例如,对于关系型数据库,我们可以使用:按服务分配私有表集(private-tables-per-service)、按服务分配表结构(schema-per-service)、或按服务分配数据库服务器(database-server-per-service)。

每个微服务应该拥有一个单独的数据库 ID,以便它们在独享访问的同时,禁止再访问其他的服务表集。

按服务共享数据库

问题:上面讨论的按服务分配数据库是一种理想的微服务模式,它一般被前面提到的 Greenfield 应用和 DDD 式的开发。但是,如果我们面对的是需要采用微服务的单体应用就没那么容易了。

解决方案:按服务共享数据库的模式虽然有些违背微服务的理念,但是它对于将前面提到的 Brownfield 应用(非新建应用)分解成较小的逻辑块是比较适用的。

在该模式下,一个数据库可以匹配不止一个的微服务,当然也至多 2~3 个,否则会影响到扩容、自治性和独立性。

命令查询职责隔离(CQRS)

问题:对于按服务分配数据库的模式而言,我们如何在微服务的架构中,实现对多个服务进行联合查询数据的需求呢?

解决方案:CQRS 建议将应用程序拆分成两个部分:命令和查询。命令部分主要处理创建、更新和删除之类的请求;查询部分则利用物化视图(Materialized Views)来处理各种查询。

它通常配合事件溯源模式(Event Sourcing Pattern)一起创建针对任何数据的变更事件。而物化视图则通过订阅事件流,来保持更新。

Saga 模式

问题:当每个服务都有自己的数据库,而且业务交易横跨多个服务时,我们该如何确保整体业务数据的一致性呢?

例如:对于某个带有客户信用额度标识的电商应用而言,它需要确保新的订单不会超出客户的信用额度。

但是,由于订单和客户分属不同的数据库,应用程序无法简单地实现本地交易的 ACID(原子性、一致性、隔离性、持久性)特性。

解决方案:Saga 代表了一个高层次的业务流程,它是由一个服务中的多个子请求,并伴随着逐个更新的数据所组成。在某个请求失败时,它的补偿请求会被执行。

实现方式有如下两种:

 ●  编排(Choreography): 没有中央协调器,每个服务都会产生并侦听其他服务的事件,以决定是否应采取行动。
 ●  协调(Orchestrator): 由一个中央协调器(对象)负责集中处理某个事件(Saga)的决策,和业务逻辑的排序。

17ef9a038ec9e6de84bdfb9382d1d77d07193d91

观测模式

日志聚合

问题:我们来考虑这样一个用例:某个应用程序包括了那些在多台机器上运行的多个服务实例,各种请求横跨在这些多个服务实例之中。同时,每个服务实例都会生成一种标准格式的日志文件。

那么我们如何针对某个特定的请求,通过各种日志来理解该应用程序的行为呢?

解决方案:显然,我们需要一个集中化的日志服务,将各个服务实例的日志予以聚合,以便用户对日志进行搜索和分析。他们可以针对日志中可能出现的某些消息,配置相应的警告。

例如:PCF(Pivotal Cloud Foundry)平台拥有一个日志聚合器,它从每种元素(如:路由器、控制器等)中收集与应用相关的日志。而 AWS Cloud Watch 也具有相似的功能。

性能指标

问题:当各种服务组合随着微服务架构变得越来越复杂时,监控交易的完整性,并能够在出现问题时及时发出警告,就显得尤为重要了。那么我们该如何收集与应用相关的性能指标呢?

解决方案:为了收集不同操作的统计信息,并提供相应的报告和警告。

我们一般会用两种模式来聚集各项指标:

 ●  推式: 将各项指标推给专门的指标服务,如:NewRelic 和 AppDynamics。
 ●  拉式: 从指标服务处拉取各项指标,如:Prometheus。

分布式跟踪

问题:在微服务架构中,横跨多个服务的请求是比较常见的。某个服务需要通过横跨多个服务去执行一到多项操作,才能处理一些特定的请求。

那么,我们该如何通过跟踪某个端到端的请求,以获知出现的问题呢?

解决方案:我们需要一种具有特性的服务。

具体特性服务如下:

 ●  为每个外部请求分配一个唯一的 ID。
 ●  将该外部请求 ID 传给所有的服务。
 ●  在所有的日志消息中都包含该外部请求 ID。
 ●  在集中式服务中,记录处理外部请求的相关信息,包括:开始时间、结束时间、和执行时间。

Spring Cloud Slueth + Zipkin Server,是一种常见的实现方式。

健康检查

问题:我们在实施微服务架构的过程中,可能会碰到某个服务虽已启动,但是无法处理交易的情况。

那么,我们该如何通过负载均衡的模式,来确保请求不会“落入”失败的实例中呢?

解决方案:每个服务都需要有一个端点,通过诸如 /health 的参数,对应用进行健康检查。

该 API 需要能够检查主机的状态,其他服务与基础设施的连接性,以及任何特定的逻辑关系。

Spring Boot Actuator 不但能够实现端点的健康检查,还能够被定制实施。

横切关注点模式(Cross-Cutting Concern Patterns)

外部配置

问题:通常情况下,一个服务需要去调用其他的服务和数据库。在诸如开发、QA(Quality Assurance,质量保证)、UAT(User Acceptance Test,用户验收测试)、和生产环境中,端点的 URL、或某些配置的属性会有所不同。

因此,有时候我们需要对这些服务的各种属性进行重构、和重新部署。那么我们如何避免在配置变更中修改代码呢?

解决方案:外部化(externalize)所有的配置,包括各个端点的 URL 和信任凭据,以保证应用程序在启动时、或运行中能够加载它们。

Spring Cloud 配置服务器提供了向 GitHub 进行属性外部化的选项,并将其作为环境属性予以加载。

此法保证了应用程序能够在启动时就被访问到,或是在不重启服务器的情况下实现刷新。

服务发现模式

问题:当微服务初具规模时,我们需要考虑如下两个关于调用服务方面的问题。

具体问题如下:

 ●  由于采用了容器技术,IP 地址往往被动态地分配给不同的服务实例。因此,每次当 IP 地址发生变化时,Consumer 服务可能会受到影响,需要我们手动更改。
   ●   Consumer 需要记住每个服务的 URL,这就倒退成了紧耦合的状态。

那么,Consumer 或路由器该如何获知所有可用的服务实例与位置呢?

解决方案:我们需要创建一个服务注册表,来保存每个 Producer 服务的元数据(Meta Data)。

一个服务实例在启动时,应当被注册到表中;而在关闭时,需从表中被注销。

Consumer 或路由器通过查询该注册表,就能够找到服务的位置。Producer 服务也需要对该注册表进行健康检查,以确保能够消费到那些可用的、且正在运行的服务实例。

我们一般有两种服务发现的类型:客户端和服务器端。使用客户端发现的例子是 Netflix Eureka;而使用服务器端发现的例子是 AWS ALB。

断路器模式

问题:有时候,某个服务在调用其他服务,以获取数据的时候,会出现下游服务(Downstream Service)“掉线”的情况。

它一般会带来两种结果:

 ●  该请求持续发往该掉线服务,直至网络资源耗尽和性能降低。
 ●  用户产生不可预料的、较差的使用体验。

那么我们该如何避免服务的连锁故障,并妥善处置呢?

解决方案:Consumer 应该通过一个代理来调用某项远程服务,就像电路中的断路器一样。

当出现持续失败的数量超过设定阈值时,断路器就会“跳闸”一段时间,从而导致所有调用远程服务的尝试被立即切断。

在超过设定时间之后,断路器只允许有限数量的测试请求通过。而如果这些请求成功了,那么断路器将恢复正常运行;否则判定为故障依旧,并重新开始新的定时周期。

Netflix Hystrix 就很好地使用了该断路器模式。它可以在断路器“跳闸”的时候,帮助您定义一种回退机制,以提供更好的用户体验。

蓝绿部署模式

问题:在微服务架构中,一个应用程序可以有多个微服务。如果我们为了部署一个增强版,而停止所有的服务,那么停机时间一旦过长,就会对业务造成影响。

况且,这对于回退来说也将会是一场噩梦。那么我们该如何避免、或减少部署过程中服务的停机时间呢?

解决方案:我们可以采用蓝绿部署的策略,以减少或消除停机时间。在蓝、绿两个相同的生产环境中,我们假设绿色环境有着当前真实的实例,而蓝色环境具有应用程序的最新版本。

在任何时候,只有一个环境能够处理所有真实的流量,并对外提供服务。如今,所有的云服务平台都能提供基于蓝绿部署的选项。

当然,我们还可以采用许多其他的微服务架构模式,如:Sidecar 模式、链式微服务(Chained Microservice)、分支微服务(Branch Microservice)、事件溯源模式(Event Sourcing Pattern)、和持续交付方式等。


原文发布时间为:2018-11-14

本文作者:陈峻编译

本文来自云栖社区合作伙伴“技术琐话”,了解相关信息可以关注“技术琐话”。

相关文章
|
21天前
|
缓存 监控 API
探索微服务架构中的API网关模式
【10月更文挑战第5天】随着微服务架构的兴起,企业纷纷采用这一模式构建复杂应用。在这种架构下,应用被拆分成若干小型、独立的服务,每个服务围绕特定业务功能构建并通过HTTP协议协作。随着服务数量增加,统一管理这些服务间的交互变得至关重要。API网关作为微服务架构的关键组件,承担起路由请求、聚合数据、处理认证与授权等功能。本文通过一个在线零售平台的具体案例,探讨API网关的优势及其实现细节,展示其在简化客户端集成、提升安全性和性能方面的关键作用。
63 2
|
5天前
|
监控 Cloud Native Java
云原生架构下微服务治理策略与实践####
【10月更文挑战第20天】 本文深入探讨了云原生环境下微服务架构的治理策略,通过分析当前技术趋势与挑战,提出了一系列高效、可扩展的微服务治理最佳实践方案。不同于传统摘要概述内容要点,本部分直接聚焦于治理核心——如何在动态多变的分布式系统中实现服务的自动发现、配置管理、流量控制及故障恢复,旨在为开发者提供一套系统性的方法论,助力企业在云端构建更加健壮、灵活的应用程序。 ####
46 10
|
5天前
|
运维 Cloud Native 持续交付
云原生架构下的微服务设计原则与实践####
【10月更文挑战第20天】 本文深入探讨了云原生环境中微服务设计的几大核心原则,包括服务的细粒度划分、无状态性、独立部署、自动化管理及容错机制。通过分析这些原则背后的技术逻辑与业务价值,结合具体案例,展示了如何在现代云平台上实现高效、灵活且可扩展的微服务架构,以应对快速变化的市场需求和技术挑战。 ####
25 7
|
5天前
|
监控 Cloud Native 持续交付
云原生架构下微服务的最佳实践与挑战####
【10月更文挑战第20天】 本文深入探讨了云原生架构在现代软件开发中的应用,特别是针对微服务设计模式的最优实践与面临的主要挑战。通过分析容器化、持续集成/持续部署(CI/CD)、服务网格等关键技术,阐述了如何高效构建、部署及运维微服务系统。同时,文章也指出了在云原生转型过程中常见的难题,如服务间的复杂通信、安全性问题以及监控与可观测性的实现,为开发者和企业提供了宝贵的策略指导和解决方案建议。 ####
29 5
|
4天前
|
Kubernetes Cloud Native 持续交付
云原生架构下的微服务设计原则与最佳实践##
在数字化转型的浪潮中,云原生技术以其高效、灵活和可扩展的特性成为企业IT架构转型的首选。本文深入探讨了云原生架构的核心理念,聚焦于微服务设计的关键原则与实施策略,旨在为开发者提供一套系统性的方法论,以应对复杂多变的业务需求和技术挑战。通过分析真实案例,揭示了如何有效利用容器化、持续集成/持续部署(CI/CD)、服务网格等关键技术,构建高性能、易维护的云原生应用。文章还强调了文化与组织变革在云原生转型过程中的重要性,为企业顺利过渡到云原生时代提供了宝贵的见解。 ##
|
11天前
|
负载均衡 监控 Cloud Native
云原生架构下的微服务治理策略与实践####
在数字化转型加速的今天,云原生技术以其高效、灵活、可扩展的特性成为企业IT架构转型的首选。本文深入探讨了云原生环境下微服务治理的策略与实践路径,旨在为读者提供一个系统性的微服务治理框架,涵盖从服务设计、部署、监控到运维的全生命周期管理,助力企业在云端构建更加稳定、高效的业务系统。 ####
|
12天前
|
运维 监控 Cloud Native
云原生架构下,微服务治理的艺术与实践####
【10月更文挑战第14天】 在数字化转型的大潮中,云原生技术以其高效、灵活与可扩展性成为企业IT架构的首选。本文深入探讨了云原生架构的核心理念,聚焦于微服务治理的策略与实践,揭示了如何通过精细化管理提升系统的响应速度、稳定性和可维护性。不同于传统的摘要概述,本文摘要旨在直接触及读者关注的核心——即如何在复杂多变的云环境中,实现微服务的高效协同与治理,为读者提供一个清晰的行动指南。 ####
28 1
|
23天前
|
Kubernetes 安全 微服务
使用 Istio 缓解电信 5G IoT 微服务 Pod 架构的安全挑战
使用 Istio 缓解电信 5G IoT 微服务 Pod 架构的安全挑战
43 8
|
7天前
|
Java API 微服务
微服务架构:解密微服务的基本概念
微服务架构:解密微服务的基本概念
22 0
|
8天前
|
运维 Kubernetes 开发者
构建高效后端服务:微服务架构与容器化技术的结合
【10月更文挑战第18天】 在数字化转型的浪潮中,企业对后端服务的要求日益提高,追求更高的效率、更强的可伸缩性和更易于维护的系统。本文将探讨微服务架构与容器化技术如何结合,以构建一个既灵活又高效的后端服务体系。通过分析当前后端服务面临的挑战,介绍微服务和容器化的基本概念,以及它们如何相互配合来优化后端服务的性能和管理。本文旨在为开发者提供一种实现后端服务现代化的方法,从而帮助企业在竞争激烈的市场中脱颖而出。
12 0