<button id="yguvc"></button>

<div id="yguvc"><tr id="yguvc"><object id="yguvc"></object></tr></div>
  • <dd id="yguvc"></dd>

    <em id="yguvc"><ol id="yguvc"></ol></em>

        <div id="yguvc"></div>

        栏目导航

        文章推荐

        热点文章

        微服务架构的各种问题及解决方案

        发表时间:2018/11/5 12:25:27  作者:ngon  
        字体大小: 【小】 【中】 【大】
        如今,微服务架构已经成为了现代应用开发的首选。虽然它能够解决大部分的程序问题,但是它并非一颗百试不爽的“银弹”。 

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

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

        可扩展性
        可用性
        弹性
        独立、自主性
        去中心化治理
        故障隔离
        自动调配
        通过 DevOps 实现?#20013;?#20132;付
        在遵循上述各条原则的同时,我们难免会碰到一些挑战。下面我们来具体?#33268;?#21487;能出现的各种问题、及其解决方案。

        分解模式

        按照业务功能分解

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

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

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

        按照子域分解

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

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

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

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

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

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

        刀砍模式(Strangler Pattern)

        问题:前面我们?#33268;?#30340;设计模式一般?#35270;?#20110;针对那些“白手起家”的 Greenfield 应用进行分解。

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

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

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

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

        这样,我们就可以在同一个 URI ?#21344;?#20869;并行地创建两套独立的应用程序。最终,在新的应用重构完成后,我们就能“刀?#22330;被?#26367;换掉原来的应用程序,直到最后我们可以完全关闭掉原来的单体应用。


        集成模式

        API 网关模式

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

        具体如下:

        如何通过调用多个微服务,来抽象出 Producer(生产者)的信息。
        在不同的渠道上(如电脑桌面、移动设备和平板电脑),应用程序需要不同的数据来响应相同的后端服务,?#28909;?:UI(用户界面)就可能会有所不同。
        不同的 Consumer(消费者)可能需要来自可重用式微服务的不同响应格式。谁将去做数据转换或现场操作?
        如何处理不同类型的协议?特别是一些可能不被 Producer 微服务所支持的协议。
        解决方案:API 网关将有助于解决在微服务实施过程中所涉及到的上述关注点。
        具体如下:

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

        它也可以将?#29616;?#19982;授权存储库从微服务中?#23545;?#20986;去。


        聚合器模式

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

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

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

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

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

        复合微服务(Composite Microservice)将会去调用全部所需的微服务,整合各种数据,并在回传之前转换数据。
        API 网关(API Gateway)也能对多个微服务的请求进行 Partition(分区),并在发送给 Consumer 之前聚合数据。
        我们建议:如果您用到了任何业务逻辑的话,请选用复合微服务;否则请采用 API 网关方案。

        客户端 UI 合成模式

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

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

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

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

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

        数据库模式

        按服务分配数据库

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

        下面是具体的关注点:

        服务必须是松散耦合的,以便能够被二次开发、部署?#25237;?#31435;扩容。
        各个业务交易需要在横跨多个服务时,仍保持不变。
        某些业务交易需要从多个服务中查询到数据。
        数据库有时需要根据规模需求被复制与分片。
        不同的服务具有不同的数据存储需求。
        解决方案:为了解决上述需求,我们需要通过设计为每个微服务配备一个?#32769;?#30340;数据库模式。

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

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

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

        按服务共享数据库

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

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

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

        命令查询职责隔离(CQRS)

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

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

        它通常配合?#24405;?#28335;源模式(Event Sourcing Pattern)一起创建针对任?#38382;?#25454;的变更?#24405;?。而物化视图则通过订阅?#24405;?#27969;,来保持更新。

        Saga 模式

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

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

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

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

        实现方式有如下两种:

        编排(Choreography)?#22909;?#26377;中央协调器,每个服务都会产生并侦听其他服务的?#24405;?,以决定是否应采取行动。

        协调(Orchestrator):由一个中央协调器(对象)负责集中处理某个?#24405;?Saga)的决策,和业务逻辑的排序。


        观测模式

        日志聚合

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

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

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

        例如:PCF(Pivotal Cloud Foundry)平台拥有一个日志聚合器,它?#29992;?#31181;元素(如:路由器、控?#30772;?#31561;)中?#21344;?#19982;应用相关的日志。而 AWS Cloud Watch 也具有相似的功能。

        性能指标

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

        解决方案:为了?#21344;?#19981;同操作的统计信息,并提供相应的报告和警告。

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

        推式?#33322;?#21508;项指标推给专门的指标服务,如:NewRelic 和 AppDynamics。
        拉式:?#21448;?#26631;服务处拉取各项指标,如:Prometheus。
        分布式跟踪

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

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

        解决方案?#20309;?#20204;需要一种具有特性的服务。

        具体特性服务如下:

        为每个外部请求分配一个唯一的 ID。
        将该外部请求 ID 传给所有的服务。
        在所有的日志消息中?#21450;?#21547;该外部请求 ID。
        在集中式服务中,记录处理外部请求的相关信息,包括:开始时间、结束时间、和执行时间。
        Spring Cloud Slueth + Zipkin Server,是一种常见的实现方式。

        健?#23548;?#26597;

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

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

        解决方案?#22909;?#20010;服务都需要有一个端点,通过诸如 /health 的?#38382;?,对应用进行健?#23548;?#26597;。

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

        Spring Boot Actuator 不但能够实现端点的健?#23548;?#26597;,还能够被定制实施。

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

        外部配置

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

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

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

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

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

        服务发现模式

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

        具体问题如下:

        由于采用了容器技术,IP 地址往往被动态地分配给不同的服务实例。因此,?#30475;?#24403; IP 地址发生变化时,Consumer 服务可能会受到影响,需要我们手动更改。
        Consumer 需要记住每个服务的 URL,这就倒?#39034;?#20102;紧耦合的状态。
        那么,Consumer 或路由器该如何获知所有可用的服务实例与位置呢?

        解决方案?#20309;?#20204;需要创建一个服务注册表,来保存每个 Producer 服务的元数据(Meta Data)。

        一个服务实例在启动时,应?#21271;?#27880;册?#22870;?#20013;;而在关闭时,需从表中被注销。

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

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

        ?#19979;?#22120;模式

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

        它一般会带来两种结果:

        ?#20204;?#27714;?#20013;?#21457;往该掉线服务,直至网络资源?#26408;?#21644;性能降低。
        用户产生不可预料的、较差的使用体验。
        那么我们该如何避免服务的连锁故障,并妥善处置呢?

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

        当出现?#20013;?#22833;败的数量超过设定阈值时,?#19979;?#22120;?#31361;帷?#36339;闸”一?#38382;?#38388;,从而导致所有调用远程服务的尝试被立即?#21368;?。

        在超过设定时间之后,?#19979;?#22120;只?#24066;?#26377;限数量的测试请求通过。而如果这些请求成功了,那么?#19979;?#22120;将?#25351;?#27491;常运行;否则判定为故障依旧,并重新开始新的定时周期。

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

        蓝绿部署模式

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

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

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

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

        当然,我们还可以采用许多其他的微服务架构模式,如:Sidecar 模式、链式微服务(Chained Microservice)、?#31181;?#24494;服务(Branch Microservice)、?#24405;?#28335;源模式(Event Sourcing Pattern)、和?#20013;?#20132;付方式等。(来源51CTO)


        河南十一选五直播