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

本文涉及的产品
服务治理 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

本文作者:陈峻编译

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

相关文章
|
1天前
|
监控 负载均衡 持续交付
构建高效微服务架构:后端开发的新趋势
【5月更文挑战第5天】在数字化转型的浪潮中,微服务架构以其灵活性、可扩展性和容错性成为企业追求的技术典范。本文深入探讨了微服务的核心组件、设计原则和实施策略,旨在为后端开发者提供构建和维护高效微服务系统的实用指南。通过分析微服务的最佳实践和常见陷阱,我们揭示了如何优化系统性能、保证服务的高可用性以及如何处理分布式系统中的复杂性。
|
1天前
|
缓存 NoSQL Java
构建高性能微服务架构:Java后端的实践之路
【5月更文挑战第5天】在当今快速迭代和高并发需求的软件开发领域,微服务架构因其灵活性、可扩展性而受到青睐。本文将深入探讨如何在Java后端环境中构建一个高性能的微服务系统,涵盖关键的设计原则、常用的框架选择以及性能优化技巧。我们将重点讨论如何通过合理的服务划分、高效的数据存储策略、智能的缓存机制以及有效的负载均衡技术来提升整体系统的响应速度和处理能力。
|
1天前
|
监控 持续交付 数据库
构建高效可靠的微服务架构:策略与实践
【5月更文挑战第5天】 在当今快速发展的软件开发领域,微服务架构已成为构建可扩展、灵活且容错的系统的首选模式。本文将探讨如何通过一系列经过验证的策略和最佳实践来构建一个高效且可靠的微服务系统。我们将深入分析微服务设计的核心原则,包括服务的细粒度划分、通信机制、数据一致性以及容错处理,并讨论如何利用现代技术栈来实现这些目标。文章将提供一套综合指南,旨在帮助开发者和架构师在保证系统性能的同时,确保系统的稳健性。
16 4
|
1天前
|
负载均衡 Java 数据库
构建高效微服务架构:后端开发的新趋势
【5月更文挑战第5天】在现代软件开发中,微服务架构已经成为一种流行的设计模式。它通过将大型应用程序分解为一组小型、松散耦合的服务来提高可维护性、可扩展性和敏捷性。本文将探讨如何构建高效的微服务架构,包括选择合适的技术栈、设计良好的服务接口、确保数据一致性以及实现高可用性和负载均衡。
|
2天前
|
Kubernetes 监控 Docker
构建高效微服务架构:Docker与Kubernetes的完美搭档
【5月更文挑战第4天】在现代软件开发中,微服务架构已成为实现可扩展、灵活且独立部署服务的流行解决方案。本文将探讨如何利用Docker容器化技术和Kubernetes容器编排平台来构建一个高效的微服务系统。我们将分析Docker和Kubernetes的核心优势,并指导读者如何通过这些工具优化微服务部署、管理和扩展过程。文章还将涉及监控和日志管理策略,以确保系统的健壮性和可靠性。
|
2天前
|
监控 Java 持续交付
构建高效微服务架构:后端开发者的终极指南
【5月更文挑战第4天】在当今快速迭代和竞争激烈的软件市场中,微服务架构已成为企业追求敏捷性、可扩展性和技术多样性的关键策略。本文深入探讨了如何构建和维护高效的微服务系统,从基本概念到高级实践,为后端开发者提供一套综合指南,以支持他们在这一变革性架构风格中扮演关键角色。
|
2天前
|
负载均衡 API 数据库
构建高效微服务架构的五大关键技术
【5月更文挑战第4天】 随着云计算和容器化技术的成熟,微服务架构已成为软件开发的主流模式。本文将详细探讨实现高效微服务架构的五个关键技术点:服务拆分策略、API网关设计、服务发现与注册、熔断机制以及分布式事务管理。这些技术点是确保微服务系统可扩展性、灵活性及稳定性的基石,对于后端开发者而言,掌握它们至关重要。文章将提供具体的实施建议和最佳实践,帮助读者构建和维护高性能的微服务系统。
|
6天前
|
存储 运维 负载均衡
探索微服务架构下的服务治理
【4月更文挑战第30天】 在当今软件开发领域,微服务架构已经成为了解决复杂系统问题的重要技术手段。随着微服务的广泛应用,如何有效管理与治理这些分散的服务成为了开发和维护的关键。本文将探讨在微服务架构下,实现高效服务治理的策略与实践,重点分析服务发现、配置管理、负载均衡和故障处理等核心要素,旨在为读者提供一套系统的服务治理思路。
|
3天前
|
JavaScript Java 持续交付
构建高效微服务架构:后端开发的新范式
【5月更文挑战第3天】 在现代软件开发的浪潮中,微服务架构以其灵活性、可扩展性和技术多样性而受到重视。本文深入探讨了如何构建一个高效的微服务系统,包括关键的设计原则、技术选型、以及实现细节。我们将通过分析微服务的核心概念,提供一套实用的步骤和最佳实践,以指导开发者构建出既健壮又易于维护的分布式系统。文章将重点讨论如何在保证系统性能和稳定性的前提下,实现服务的解耦与独立部署,从而推动后端开发工作流的优化和创新。
|
3天前
|
安全 数据管理 持续交付
构建高效微服务架构的五大核心策略
【5月更文挑战第3天】在当前软件开发领域,微服务架构已成为一种流行的设计模式,它通过将应用程序拆分成一组小型、松耦合的服务来提高系统的可维护性和扩展性。本文将详细探讨构建高效微服务架构的五大核心策略,包括服务划分原则、通信机制设计、数据管理、安全性考虑以及持续集成与部署。这些策略不仅有助于确保系统的高可用性和灵活性,同时也支持快速迭代和部署,是实现现代云原生应用的基石。