其他分享
首页 > 其他分享> > 分布式:微服务架构核心和实现

分布式:微服务架构核心和实现

作者:互联网

1,微服务架构中职能团队的划分

传统单体架构将系统分成具有不同职责的层次,对应的项目管理也倾向于将大的团队分成不同的职能团队,主要包括:用户交互UI团队、后台逻辑处理团队与数据存取ORM团队、DBA团队等。每个团队只对自己分层的职责负责,并对使用方提供组件服务质量保证。如果其中一个模块化组件需要升级、更新,那么这个变更会涉及不同的分层团队,即使升级和变更的改变很小,也需要进行跨团队沟通:需求阶段需要跨团队沟通产品功能,涉及阶段需要跨团队沟通设计方案,开发阶段需要跨团队沟通具体的接口定义,测试阶段需要沟通业务回归等事宜,甚至上线都需要跨团队沟通应用的上线顺序。由此可见在传统的整体架构下,后期的维护成本很高,出现事故风险很大。

根据康威定律,团队的交流机制应该与架构设计机制相对应。因此,在微服务架构下,智能团队的划分方法是我们首先考虑的因素。

微服务架构按照业务的功能进行划分,每个单一的业务功能叫作一个服务,每个服务对应一个独立的职能团队,团队里包含用户交互UI设计师、后台服务开发人员、DBA、运营和运维人员。

  • 在传统的整体架构中,软件是有生命周期的,经历需求分析、开发和测试,然后交付给运维团队,这时开发团队将会被解散,这是对软件的一个“放手”。
  • 在微服务架构中,提倡运维人员也是服务项目的一员,倡导谁开发、谁维护,实施终身维护制度。在业务服务的内部实现需要升级或者变更时,团队内的各角色成员进行沟通即可,而不需要进行跨团队沟通,这大大提高了沟通效率。只有服务之间的接口需要变更时才需要跨部门沟通,如果前期在服务之间的交互上定义了良好的接口,则接口变更的概率并不大,即使接口模式有更改,也可以通过一定的设计模式和规范来解决。

2,微服务的去中心化治理

建设API网关,所有外部服务和内部服务逗游统一的API网关进行管理。在项目初期,中心化的API网关统一了所有API的入口,这看起来很规范,但从技术角度来看限制了API的多样化。随着业务的发展,API网关开始暴露问题,每个用户请求经过机房时只要有服务之间的交互,则都会从API网关进行路由,服务上量以后,由于内部服务之间的交互都会叠加在API网关的调用上,所以很大程度上放大了API网关调用TPS,API网关很快就遇到了性能瓶颈。

而服务化倡导去中心化的治理,不推荐每个微服务都使用相同的标准和技术来开发和使用服务。在微服务架构下可以使用C++开发一个服务,来对接Java开发的另一个服务,对于异构系统之间的交互标准,通常可以使用工具来补偿。开发者可以开发共用的工具,并分享给异构系统的开发者使用,来解决异构系统不一致的问题,例如:Thrift远程调用框架使用中间语言(IDL)来定义接口,中间语言是独立于任何语言的,并提供了工具来生成中间语言,以及在中间语言与具体语言之间的代码转换。

微服务架构倡导去中心化的服务管理和治理,尽量不设置中心化的管理服务,最差也需要在中心化的管理服务宕机时有替代方案和设计。

3,微服务的交互模式

这些交互的设计模式对微服务之间的交互定义契约,服务的生产者和调用者都需要遵守这些契约,才能保证微服务不出问题。

3.1,读者容错模式

读者容错模式指微服务化中服务提供者和消费者之间如何对接口的改变进行容错。从字面上来讲,消费者需要对提供者提供的功能进行兼容设计,尤其对服务提供者返回的内容进行兼容,或者解决在服务提供者改变接口或者数据的格式的情况下,如何让服务消费者正常运行。

任何一个产品在设计时都无法预见将来可能增加的所有需求,服务的开发者通常通过迭代及时地增加新功能,或者让服务提供的API自然地演进。不过,服务提供者对外提供的接口的数据格式的改变、增加和删除,都会导致服务的消费者不能正常工作。

因此,在服务消费者处理服务提供者返回的消息的过程中,需要对服务返回的消息进行过滤,只提取自己需要的内容,对多余或者未知的内容采取抛弃的策略,而不是硬生生地跑错处理。

在实现过程中不推荐使用严格的校验策略,而是推荐使用宽松的校验策略,即使服务消费者拿到的消息报文发生了改变,程序也需尽最大努力提取需要的数据,同时忽略不可识别的数据。只有在服务消费者完全不能识别接收到的消息,或者无法通过识别的信息继续处理流程时,才能抛出异常。

服务的消费者的容错模式忽略了新的消息项、可选的消息项、未知的数据值及服务消费者不需要的数据项。

3.2,消费者驱动契约模式

消费者驱动契约模式用来定义服务化中服务之间交互接口改变的最佳规则。服务契约分为:提供者契约、消费者契约及消费者驱动的契约,它从期望与约束的角度描述了服务提供者与服务消费者之间的联动关系。

  • 提供者契约:提供者契约是以提供者为中心的,提供者提供了什么功能和消息格式,各消费者就会无条件地遵守这些约定,不论消费者实际需要多少功能,,消费者接受了提供者契约时,都会根据服务提供者的规则来使用服务。
  • 消费者契约:是对某个消费者的需求进行更为精确的描述,在一次具体的服务交互场景下,代表消费者需要提供者提供功能中的哪部分数据。消费者契约可以被用来标识现有的提供者契约,也可以用来发现一个尚未明确的提供者契约。
  • 消费者驱动的契约:代表服务提供者向其所有当前消费者承诺遵守的约束。一旦各消费者把自己的具体期望告知提供者,则提供者无论在什么时间和场景下,都不应该打破契约。

在现实的服务交互设计中,上面这三种契约是同时存在的,比如在支付平台里,交易系统在完成一笔支付后,需要到账务系统为商户入账,在这个过程中,服务契约表现如下:

  • 生产者契约:账务系统提供Dobbo服务化接口,参数为商户账户ID、入账订单和入账金额。
  • 消费者契约:账务系统返回DTO,包含商户账户ID、入账订单号、入账金额、入账时间、账务流水号、入账状态等,而交易系统只需使用其中的入账订单号和入账状态。
  • 消费者驱动的契约:为了保证资金安全,交易系统作为入账的发起者向账务提出要求,需要账务做幂等和滤重处理,对重复的入账请求进行拦截;账务系统在接受这个契约后,即使将来有任何改变,也不能打破这个限制,否则就会造成资金的损失,在这个金融系统中是最严重的问题

服务提供者契约是服务提供者单方面定下的规则,而一个消费者契约会成为提供者契约的一部分,多个服务消费者可以对服务提供者提出约束,服务提供者需要在将来遵守服务消费者提出的契约,这就是消费者驱动的契约。 

3.3,去数据共享模式

与SOA服务化对比,微服务是去ESB总线、去中心化及分布式的;而SOA还是以ESB为核心实现遗留系统的集成,以及基于Web Service为标准实现的通用的面向服务的架构。在微服务领域,微服务之间的交互通过定义良好的接口来实现,不允许使用共享数据来实现。

在实践的过程中,有些方案的设计使用缓存或数据库作为两个微服务之间的纽带,在业务流程的处理过程中,为了处理简单,前一个服务将中间结果存入数据库或者缓存,下一个服务从缓存或者数据库中拿出数据继续处理。

这种交互流程缺点如下:

  • 使得微服务之间的交互除了接口契约,还存在数据存储契约。
  • 上游的数据格式发生变化时,可能导致下游的处理逻辑出现问题。
  • 多个服务共享一个资源服务,对资源服务的运维难以划清职责和界限。
  • 在做双机房独立部署时,需要考虑服务和资源的路由情况,跨机房的服务调用不能使用独立的资源部署模式,因此难以实现服务自治。

4,微服务的分解和组合模式

使用微服务架构划分服务和团队是微服务架构实施的重要一步,良好的划分和拆分使系统达到松耦合和高内聚的效果,然后通过微服务的灵活组装可以满足上层各种各样的业务处理需求。

在微服务架构的需求分析和架构设计过程中,通常是用领域的动词和名词来划分微服务的。例如,对于一个电商后台系统,可以分解为订单、商品、商品目录、库存、购物车、交易、支付、发票、物流等子系统,每个名词和动词都是一个微服务,将这几个微服务组合在一起,就实现了电商平台用户购买商品的整个业务流。

这样拆分后,系统具有敏捷性、灵活性、可伸缩性等,拆分后有多个高度自治的微服务。而微服务的组合方式有如下几种:

4.1,服务代理模式

服务代理模式是最简单的服务组合模式,它根据业务的需求选择调用后端的某个服务。在返回给使用端之前,代理可以对后端服务的输出进行加工,也可以直接把后端服务的返回结果返回给使用端。

典型的案例是做平滑的系统迁移:​​​​​​​

  • 在新老系统上双写。
  • 迁移双写之前的历史遗留数据。
  • 将读请求切换到新系统。
  • 下调双写逻辑,只写新系统。

4.2,服务聚合模式

服务聚合模式是最常用的服务组合模式,它根据业务流程处理的需要,以一定的顺序调用依赖的多个微服务,对依赖的微服务返回的数据进行组合、加工和转换,最后以一定的形式返回给使用方。

这里,每个被依赖的微服务都有自己的缓存和数据库,聚合服务本身可以有自己的数据存储,包括缓存和数据库等,也可以是简单的聚合,不需要持久化任何数据。

这里体现了DRY原则的设计理念,在设计或者构造应用时,最大限度地重用了现有的实现。加入一块业务逻辑由三个独立的逻辑块组成,每个独立的逻辑块可能有多个使用方,则DRY原则推荐将三个独立的逻辑块封装成三个独立运行的微服务,然后使用服务聚合模式开发聚合服务,将三个独立的逻辑块聚合在一起提供给上层组合服务。这样做的好处:

  • 三个独立的自服务可以各自独立开发、敏捷开发。
  • 聚合服务封装下层的业务处理服务,由三个独立子服务完成数据持久化等工作,项目结构清晰。
  • 三个独立的自服务对于其他使用方仍然可以重用。

4.3,服务串联模式

服务串联模式类似于一个工作流,最前面的服务1负责接收请求和响应使用方,串联服务后再与服务1交互,随后服务1与服务2交互,最后,从服务2产生的结果经过服务1和串联服务逐个处理后返回给使用方。

服务串联模式之间的调用通常使用同步的RESTful风格远程调用,在串联服务没有完成并返回之前,所有服务都会阻塞和等待,一个请求会占用一个线程来处理,因此在这种模式下不建议服务的层级太多,如果能用服务聚合模式代替,则优先使用服务聚合模式,而不是这种服务串联模式。

相对于服务聚合模式,服务串联模式有一个优点,即串联链路上再增加一个节点时,只要不是在串联服务的正后面增加,那么串联服务是无感知的。

4.4,服务分支模式

服务分支模式是服务代理模式、服务聚合模式和服务串联模式相结合的产物。

分支服务可以拥有自己的数据库存储,调用多个后端的服务或者服务串联链,然后将结果进行组合处理再返回给客户端。分支服务也可以使用代理模式,简单地调用后端某个服务或者服务链,然后将返回的数据直接返回给使用方。

但在实际的业务平台建设中,由于业务的复杂性,抽象的微服务可能有多层的依赖关系,依赖关系并不会太简单,进程呈现树形的分支结构。

分支模式同时放大了服务的依赖关系,因此在现实的微服务设计中尽量保持服务调用级别的简单,在使用服务组合和服务代理模式时,不要使用服务串联模式和服务分支模式,以保持服务依赖关系的清晰明了,同时也减轻了日后维护的工作量。

4.5,服务异步消息模式

之前所有服务组合模式都使用同步的RESTful(RESTful简介)风格的同步调用来实现,同步调用模式在调用的过程中会发生阻塞线程,如果服务提供方迟迟没有返回,则服务消费方会一直阻塞,在严重情况下会撑满服务的线程池,出现雪崩效应。

因此,在构建微服务架构系统时,通常会梳理核心系统的最小化服务集合,这些核心的系统服务使用同步调用,而其他核心链路以外的服务可以使用异步消息队列进行异步化。

聚合服务同步调用服务1和服务2,而服务2通过消息队列将异步消息传递给服务3和服务4。

4.6,服务共享数据模式

服务共享数据模式其实是反模式,微服务由于去掉了数据共享,所以仅仅通过服务之间良好定义的接口进行交互和通信,使得每个服务都是自治的,服务本身和服务的团队包含全角色栈的技术和运营人员,这些人都是做专业的事,使沟通在团队内部解决,因此可以使效率最大化。

然而,去数据共享并不适用用于下面两种情况,其余任何场景都不能使用服务数据共享模式:

单元化架构

一些平台由于对性能有较高的要求,所以采用微服务化将服务进行拆分,通过网络服务进行通信,尽管网络通信的带宽已经很宽,但是还会有性能方面的损耗,在这种场景下,可以让不同的微服务共享一些资源,例如:缓存、数据库等,甚至可以将缓存和数据咋物理拓扑上与微服务部署在一个物理机中,最大限度地减少网络通信带来的性能损耗,简称“单元化架构”。

遗留的整体服务

 对于历史遗留的传统单体服务,我们在重构微服务的过程中,发现单体服务依赖的数据库表耦合在一起,对其拆分需要进行反规范化的处理,可能会造成数据一致性问题,在没有完全理解和有把握的前提下,会选择保持现状,让不同的微服务暂时共享数据存储。

5,微服务的容错模式

在使用了微服务架构以后,整体的业务流程被拆分成小的微服务,并组合在一起对外提供服务,微服务之间使用轻量级的网络协议通信,通常是RESTful风格的远程调用。由于服务于服务的调用不再是进程内的调用,而是通过网络进行远程调用,众所周知,网络通信是不稳定、不可靠的,一个服务依赖的服务可能出错、超时或者宕机,如果没有及时发现和隔离问题,或者在设计中没有考虑如何应对这样的问题,那么很可能在短时间内服务的线程池中的线程被用完、资源耗尽,导致出现雪崩效应。

5.1,舱壁隔离模式

这里用航船的设计比喻舱壁隔离模式,若一搜航船遇到了意外事故,其中一个船舱进了水,则我们希望这个船舱和其他船舱是隔离的,希望其他船舱可以不进水,不收影响。在微服务架构中,这个主要体现在如下两个方面:

微服务容器分组

支付平台应用微服务,将微服务的每个节点的服务池分为三组:准生产环境、灰度环境和生产环境。准生产环境供内侧使用;灰度环境会跑一些普通商户的流量;大部分生产流量和VIP商户的流量则跑在生产环境中。这样,在一次比较大的重构过程中,我们就可以充分利用灰度环境的隔离性进行预验证,用普通商户的流量验证重构没有问题后再上生产环境。

另外一个案例是社交平台将名人的自媒体流量全部路由到服务的核心池子中,而将普通用户的流量路由到另外一个服务池子中,有效隔离了普通用户和重要用户的负载。

线程池隔离

在微服务架构实施的过程中,我们不一定将每个服务拆分到微小的力度,这取决于职能团队和财务的状况,一般会将同一类功能划分在一个微服务中,尽量避免微服务过细而导致成本增加,适可而止

这样就会导致多个功能混合部署在一个微服务实例中,这些微服务的不同功能通常使用同一个线程池,导致一个功能流量增加时耗尽线程池的线程,而阻塞其他功能的服务。

5.2,熔断模式

可以用家里的电路保险开关来比喻熔断模式,如果家里的用电量过大,则电路保险开关就会自动跳闸,这是需要人工找到用电量过大的点起来解决问题,然后打开电路保险开关。在这个过程中,电路保险开关起到保护整个家庭电路系统的作用。

对于微服务系统也一样,当服务的输入负载迅速增加时,如果没有有效的措施对负载进行熔断,则会使服务迅速被压垮,服务被压垮会导致依赖的服务都被压垮,出现雪崩效应,因此,可通过模拟家庭电路保险开关,在微服务架构中实现熔断模式。

5.3,限流模式

服务的容量和性能是有限的,然而,即使我们在设计阶段考虑到了性能压力的问题,并从设计和部署上解决了这些问题,但是业务量是随着时间的推移而增长的,突然上量对于一个飞速发展的平台来说是很常见的事情。

针对服务突然上量,我们必须有限流机制,限流机制一般会访问的并发量,例如每秒允许处理的并发用户数及查询量、请求量等。

  • 计数器:通过原子变量计算单位时间内的访问量,如果超出某个阀值,则拒绝后续的请求,等到下一个单位时间内再重新计数。
  • 令牌桶:通过一个线程在单位时间内生产固定数量的令牌,然后把令牌放入队列,每次请求调用需要从桶中拿取一个令牌,拿到令牌后才有资格执行请求调用,否则只能等待拿到令牌再执行,或者直接丢弃。
  • 信号量:限流类似于生活中的漏洞,无论倒入多少油,下面有漏管的流量是有限的,实际上我们在应用层使用的信号量也可以实现限流。
  • 失效转移模式:若微服务架构中发生了熔断和限流,应该及时处理被拒绝的请求,解决这个问题的模式叫做生效转移模式。

失效转移方法:

  • 采用快速失败的策略,直接返回使用方错误,让使用方知道发生了问题并自行决定后续处理。
  • 是否有备份服务,如果有备份服务,则迅速切换到备份服务。
  • 失败的服务有可能是某台机器有问题,而不是所有机器有问题,例如OOM问题,在这种情况下适合使用failover策略,采用重试的方法来解决,但是这种方法要求服务提供者的服务实现了幂等特性。

6,微服务的粒度

按照微服务的初衷,服务要按照业务的功能进行拆分,直到每个服务的功能和职责单一,甚至不可拆分为止,以至于每个服务都能独立部署,扩容和缩容方便,能够有效地提高利用率。拆得越细,服务的耦合度越小,内聚性越好,越适合敏捷发布和上线。

然而,拆得太细会导致系统的服务数量较多,相互依赖的关系较复杂,更重要的是根据康威定律,团队要响应系统的架构,每个微服务都要有相应的独立、自治的团队来维护,这也是一个不切实际的想法。

因此,这里倡导对微服务的拆分适可而止,原则是拆分到可以让使用方自由地编排底层的自服务来获得相应的组合服务即可,同时要考虑团队的建设及人员的数量和分配等。

有的公司把每个接口包装成一个工厂,或者把每一次JDBC调用包装成一个工程,然后号称是“微服务”,最后有成百上千的微服务项目,这是不合理的。当然,有的公司把一套接口完成的一个粗粒度的流程耦合在一起,所以需要在流程中做定制话才能实现使用方使用部分服务的需求,这也是不合理的,原因是服务粒度太粗。

总之,拆分的粒度太细和太粗都是不合理的,根据业务需要,能够满足上层服务对底层服务自由编排并获得更多的业务功能即可,并需要适合团队的建设和布局。

标签:服务,核心,使用,模式,契约,架构,团队,分布式
来源: https://blog.csdn.net/qq_42192693/article/details/120376961