Fork me on GitHub

微服务关键技术纵览

Nine Characteristics of a Microservice Architecture

Posted by Kaelzhang on September 28, 2017

微服务关键技术纵览

本文源自公司内训的《微服务训练营》中的关键技术课件,内容格式沿用PPT展示风格。

服务注册与发现

缘由

  1. 服务实例IP分配动态化
  2. 服务实例的动态改变
    • 弹性
    • 故障
    • 升级

Note: 在传统的运行在物理机上的应用,服务的网络地址是相对静态的。然而,在现代的基于云计算的微服务应用中,服务实例会被动态地分配网络地址。并因为自动伸缩、故障和升级,服务实例会动态地改变。故客户端代码需要用一种更加精密的服务发现机制。 先从注册谈起:

服务自注册模式

在自注册模式中,服务实例自身负责在服务注册表中注册和注销。如果需要的话,一个服务实例发送心跳请求防止注册过期。

第三方注册模式

在第三方注册模式中,服务实例不会自己在服务注册表中注册,而由服务管理器负责。服务管理器通过轮询部署环境或订阅事件去跟踪运行中的实例变化。当它发现一个新的可用的服务实例时,就会到注册表中去注册。服务管理器也会注销已停止的服务实例。

NOTE:

优势:

与自注册模式相比,服务代码复杂程度更低,因为其无需实现自动注册。

注册工具可对服务实例执行健康检查,并根据检查结果注册或者注销该实例。

弊端:

第三方注册模式可能只了解服务实例的一些表层状态,例如其是否正在运行,因此 无法了解其是否能够处理请求。不过,之前提到的Netflix Prana等注册工具能够通过执行健康检查来判断当前服务实例的可用性。

除非该注册工具属于基础设施的一部分,否则我们需要对其进行安装、配置与维护。另外,因为它是关键系统组件,因此需要保证其具有高度可用性。

客户侧服务发现模式

当使用客户端服务发现的时候,客户端负责决定可用的服务实例的网络地址,以及围绕他们的负载均衡。客户端向服务注册表(service registry)发送一个请求,服务注册表是一个可用服务实例的数据库。客户端使用一个负载均衡算法,去选择一个可用的服务实例,来响应这个请求。

Note:

这种模式相对直接,但是除了服务注册表,没有其它动态的部分了。而且,由于客户端知道可用的服务实例,可以做到智能的,应用明确的负载均衡决策,比如一直用hash算法。这种模式的一个重大缺陷在于,客户端和服务注册表是一一对应的,必须为服务客户端用到的每一种编程语言和框架实现客户端服务发现逻辑

服务侧服务发现模式

客户端通过负载均衡器向一个服务发送请求,这个负载均衡器会查询服务注册表,并将请求路由到可用的服务实例上。通过负载均衡器的服务发现,服务实例在服务注册表上被注册和注销。

Note:

这种模式相对直接,但是除了服务注册表,没有其它动态的部分了。而且,由于客户端知道可用的服务实例,可以做到智能的,应用明确的负载均衡决策,比如一直用hash算法。这种模式的一个重大缺陷在于,客户端和服务注册表是一一对应的,必须为服务客户端用到的每一种编程语言和框架实现客户端服务发现逻辑

容错设计

依赖管理

一个大型的系统是由众多的微服务组成,服务之间可能存在复杂的依赖和关联关系。

1->N扇出

NOTE:

在IO型服务中,假设服务A依赖服务B和服务C,而B服务和C服务有可能继续依赖其他的服务,继续下去会使得调用链路过长,技术上称1->N扇出。 用户看到的一次请求响应, 通常会触发多个系统间的RPC调用和存储操作, 这个现象叫做扇出.

雪崩效应

如果在A的链路上某个或几个被调用的子服务不可用或延迟较高,则会导致调用A服务的请求被堵住,堵住的请求会消耗占用掉系统的线程、io等资源,当该类请求越来越多,占用的计算机资源越来越多的时候,会导致系统瓶颈出现,造成其他的请求同样不可用,最终导致业务系统崩溃。

雪崩缘由

  • 某几个机器故障:例如机器的硬驱动引起的错误,或者一些特定的机器上出现一些的bug(如,内存中断或者死锁)。

  • 服务器负载发生变化:某些时候服务会因为用户行为造成请求无法及时处理从而导致雪崩,例如阿里的双十一活动,若没有提前增加机器预估流量则会造服务器压力会骤然增大而挂掉。

  • 人为因素:比如代码中的路径在某个时候出现bug

依赖管理原则

  • 延迟连接:一个微服务启动时不关心其所依赖的服务是否启动,仅在需要时进行连接。
  • 重试机制:连接失败的情况下需具备重试机制,不应该直接程序异常或者进程终止。
  • 快速失败:具备熔断能力,在依赖服务不可用时,可以快速失败,防止拖垮整个系统。

解决方案

  • 服务隔离
  • 服务限流
  • 服务降级

服务隔离

服务隔离的方式一般使用两种:

  1. 线程池隔离模式
  2. 信号量隔离模式

NOTE:

这种模式就像对系统请求按类型划分成一个个小岛的一样,当某个小岛被火烧光了,不会影响到其他的小岛。例如可以对不同类型的请求使用线程池来资源隔离,每种类型的请求互不影响,如果一种类型的请求线程资源耗尽,则对后续的该类型请求直接返回,不再调用后续资源。这种模式使用场景非常多,例如将一个服务拆开,对于重要的服务使用单独服务器来部署,再或者公司最近推广的多中心。

线程池隔离模式

使用一个线程池来存储当前的请求,线程池对请求作处理,设置任务返回处理超时时间,堆积的请求堆积入线程池队列。这种方式需要为每个依赖的服务申请线程池,有一定的资源消耗,好处是可以应对突发流量(流量洪峰来临时,处理不完可将数据存储到线程池队里慢慢处理)

信号量隔离模式

使用一个原子计数器(或信号量)来记录当前有多少个线程在运行,请求来先判断计数器的数值,若超过设置的最大线程个数则丢弃改类型的新请求,若不超过则执行计数操作请求来计数器+1,请求返回计数器-1。这种方式是严格的控制线程且立即返回模式,无法应对突发流量(流量洪峰来临时,处理的线程超过数量,其他的请求会直接返回,不继续去请求依赖的服务)

服务限流

每个系统都有最大的处理能力,一旦请求超过了系统最大处理能力,必须进行限流,否则会压垮整个系统。

影响到用户体验,谨慎使用

NOTE:

区分正常流量和超预期流量:限流标准来自压力测试、折算 读少限,写多限 客户端配合限流 不同分组的限流阈值 各层限流手段

限流方法
  • 设定最大访问请求速率,对于超出的部分则丢弃。
  • 按比例丢弃请求,比如一次性随机丢失20%的请求。
  • 直接关闭某些入口流量,比如关闭WAP的流量。

NOTE:

  • 前置限流,快速失败:比如通过提供给调用方的JSF客户端,封装限流逻辑。
  • Nginx层限流:自主研发的模块;几个规则:账户,IP,系统调用流程。
  • 应用限流:减少并发数线程数;读少限,写多限;DB限流;连接数。

服务降级

服务降级指人为的关闭一些不重要服务,或者把一些不重要的处理步骤旁路。降级的目的是防止非核心服务和流程占用的带宽和资源影响核心服务/主流程,从而保证核心服务,核心流程有充足的带宽和资源。

NOTE:

  • 保证用户的核心需求
  • 降级需要有预案和开关:确定系统和功能级别,是否可降,影响如何;降级需要有开关
  • 非关键业务屏蔽:购物车的库存状态
  • 业务功能模块降级:实时价格更新不及时;peking库,保订单管道、生产,暂停统计相关
  • 数据降级:动态降级到静态;远程服务降级到本地缓存:采销岗服务

电路熔断器模式

  • 熔断体现了在服务不可用情况下,快速失败的原则:fail fast!
  • 快速失败后,调用方进程就不会存在进程堆积,从而不会消耗过多的资源,也不会对其他进程造成影响。从而避免了微服务雪崩的问题。
  • 熔断后,可以提供有限服务,而不是拒绝或者崩溃。

把不确定错误变为确定错误,统一处理

熔断器状态

  • 闭合(closed)态:对服务的请求能够直接引起方法的调用。熔断器维护最近调用失败的次数,如果某次调用失败,则对失败次数累加。如果最近失败次数超过了在给定时间内允许失败的阈值,则熔断器切换到断开(Open)态,并启动一个超时时钟,当该时钟超过了该时间,则迁移到半开态。
  • 半开(Half-Open)态:该超时时间的设定是给系统一次机会来修正导致调用失败的错误。
  • 断开(Open)态: 在该状态下,对服务的请求会立即返回错误响应。

配置中心

微服务的配置是和具体环境相关的,开发测试环境、staging环境、生产环境的配置是不同。这些不同的配置需要统一纳入配置中心。

提供能力

  • 满足分布式能力。
  • 支持版本化管理:支持配置信息版本化控制,可审计,安全。
  • 配置热更新。

服务监控

监控五原则

  1. 监控容器及其里面的东西。
  2. 在服务性能上做监控,而不是容器性能。
  3. 监控弹性和多地部署的服务。
  4. 监控API。
  5. 将监控映射到组织结构。

https://thenewstack.io/five-principles-monitoring-microservices/

采集模型

由一个服务把单个的操作的统计信息集成起来,通过集中式的度量服务产生报表和警告。有两种模型可以用来聚合度量信息:

  • Push模型:服务把metrics消息推送到度量服务
  • Pull模型:metrics service从服务实例上抓取

健康检查

每个service都应该有健康检查API, 比如说 HTTP /health, 来返回这个service的运行状况。这个API端点应该执行一系列的检查。

健康检查的客户端:一个监控服务、服务注册或者负载均衡器,周期性调用api来检查这个service实例的健康状态。

检查内容

  • 这个service实例所连接的基础设施服务的状态(比如数据库等)
  • 主机的状态,比如磁盘空间
  • 应用特有的逻辑

健康检查请求

健康检查示例

分布式追踪

针对一个请求可能会覆盖到多个服务,如何进行请求追踪的场景。

示例:
  • 给每个的请求一个唯一的request id。
  • 把这个request id传递给别的与处理这个请求相关的service。
  • 把这个request id加入到所有的log信息中。
  • 把请求的信息(比如开始时间,结束时间)和在处理这个请求时的行为记录下来。

Google Dapper

https://stackexchange.github.io/Dapper/

设计理念

Trace树

Span

系统Trace跟踪

服务网关

Note:

它展示了一个乐队,然后有个指挥家,下面一堆人(微型服务)演奏自己的乐器。这个指挥家(API网关)可以以某种方式来协调我们的架构如何处理请求。

API Gateway模式

将所有API共性的功能统一放在Gateway上实现。

模式特点:

  • 位于所有服务的最前端
  • 是所有请求的入口点
  • 反向代理

请求处理方式:

  • 路由到具体服务处理
  • 调度多个服务处理

提供能力:

  • 安全管理
  • 限流
  • 缓存
  • 服务组装
  • API调用、路由
  • 服务健康
  • 监控
  • 版本化管理

后端服务前端模式(BFF)

模式特点:

  • 给每种客户端定义一个单独的API Gateway。
  • API Gateway的变种

两种模式类型:

  • 每个平台一个BFF
  • 每种类型的接口一个BFF

参考资料

  1. http://www.infoq.com/cn/articles/micro-service-reliability-design
  2. https://bigbully.github.io/Dapper-translation/
  3. http://blog.didispace.com/hzf-ms-apigateway-1/
  4. http://microservices.io/patterns/microservices.html
  5. https://yq.aliyun.com/articles/7443

  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!