微服务框架相关技术

接下来对微服务相关技术做一个整理,并输出一个实践项目。
如果从服务构建到生产运营,包括的技术有:微服务框架,容器化,持续集成与发布,资源调度与服务编排,监控等一些大的范围。
这篇主要讲微服务框架,其中主要以Spring Cloud框架进行展开或举例,因为Spring Cloud作为微服务一个可行的落地方案,不仅服务治理功能齐全,也是目前的最佳实践之一。

首先,微服务之间要相互调用,那就从远程调用开始说起:

远程调用

调用类型 关系和特点 调用效果(同步,异步) 调用形式 协议 常用
socket远程发送 client,server2点之间 原生异步 ip+端口 TCP/IP Socket套接字编程
http远程发送 Consumer - Provider 2点之间 同步调用 IP+端口+服务url http,https HttpClient,Spring RestTemplate等工具
RPC框架 Consumer - Provider 2点之间 同步,异步+回调,单播 本地接口 Http, Https, http2, TCP自定义 dubbo , GRPC等
MQ中间件 Producer - Broker Server - Consumer 3点之间;服务调用 + 调用解耦 + 消息暂存(削峰平谷) 同步,异步+回调,单播 SDK接口 TCP的自定义 rocketMQ ,RabbitMQ , Kafka等
service mesh client - client proxy - service proxy - service 4点,非侵入式框架 均可 本地调用 Http,Https,http2,TCP的自定义 Linkerd,Nginx,Envoy, Spring Cloud Sidecar, Consul等

RPC框架或叫服务框架开始,Socket IO 基本上已经采用异步非阻塞的方式,大部分底层使用了Netty的实现,在服务的调用方式上也普遍支持同步,异步有回调/无回调 等多种方式。接下来,也会支持反应式流的规范,提高服务调用之间的可靠性。(反应式可以参考上一篇文章)

服务注册发现

上面说的是服务之间远程调用的方式,接下来,我们每一个服务是分布部署在多台机器上的,而且基于集群的云服务器、虚拟机或容器环境,他们每次部署根据资源情况被编排到的虚拟机器的IP可能随时变化,那么,客户端需要知道当前调用的服务在哪些机器上,要对应哪些IP才能调用,所以,就需要引入实时的服务注册与发现。

  • 作用 :解决服务名称和服务主机IP的对应关系,实现服务的实时注册,更新和查找
  • 服务名:服务的名称,在该服务名称下注册服务实例的多个节点。
    服务名可以简化客户端进行服务调用,一般需要通过负载均衡器,或DNS服务,根据相应的均衡算法策略,得到本次调用的服务名对应的节点IP。还有,我们要说的服务注册发现的方式。

  • 常用:Zookeeper,Etcd, Eureka(开源项目已暂停), Consul

  • 服务注册与发现的产品特性:

Feature Consul zookeeper etcd euerka
编程语言 Go Java Go Java
一致性 Gossip(发现和协调),Raft(选举和多数派) paxos raft
cap Gossip-ap,Raft-cp cp cp ap
健康检查 支持 长连接,keepalive 连接心跳 支持
多数据中心 支持
kv存储服务 支持 支持 支持
使用接口(多语言能力) http和dns 客户端 http/grpc http(sidecar)
watch支持 全量/支持long polling 支持 支持 long polling 支持 long polling/大部分增量
自身监控 metrics metrics metrics
安全 acl /https acl https支持(弱)
spring cloud集成 支持 支持 支持 支持(项目停止)

配置中心

  • 作用:统一的配置Server,因为微服务粒度更小,数量更多,所以配置信息需要统一进行维护。另外,配置可以支持动态更新。
    常用: Spring Cloud Config ,Spring Cloud Consul Config

  • 配置中心的产品特性:

Feature Spring Cloud Config Consul
存储 git consul server
动态配置 依赖消息中间件 定时任务
配置增删改查 rest api, git rest api,命令,web console

服务调用

  • 服务调用需要关注哪些:
    服务调用方式,负载均衡,超时与重试,熔断与降级,限流等

    服务框架一般提供了上面的解决方案,如Dubbo, GRPC, Spring Cloud。

    服务调用方式

  • 文章最开始介绍了远程调用的几种方式,当前微服务之间的主流方式,还是模拟本地接口的RPC调用方式
    Spring Cloud 提供了OpenFeign组件, 来实现服务之间RPC的调用效果。
    Feign 描述如下:
    Declarative REST Client: Feign creates a dynamic implementation of an interface decorated with JAX-RS or Spring MVC annotations.
    参考:
    https://github.com/spring-cloud/spring-cloud-openfeign

服务调用组件一般需要依赖:负载均衡 ,熔断降级 组件,对应Spring Cloud 的是 Ribbon , hystrix,并且,具有超时与重试的机制。

负载均衡:

  • 将工作负载分布到多个服务器,提高性能和可靠性。在请求的每一个层次都有对应的负载均衡器。
客户端负载均衡
  • 由客户端实现负载均衡策略,如:
    Dubbo, spring cloud Ribbon
    客户端负载均衡一般需要依赖服务注册中心,从注册中心获取服务的所有地址,并实现了不同的负载均衡算法,根据算法获得一个IP来路由转发。
服务器端负载均衡
  • 服务器端负载均衡常用的有:
    Dns server , LVS , Nginx 等

  • 服务端负载均衡一般有以下几种类型:

类型 特点 功能 常用
DNS Server 域名查找IP的服务 DNS名字 配置多个IP,也支持基于地址的域名解析,解析成距离用户最近的服务器地址 DNS服务商,内部DNS Server
二层负载均衡 MAC 提供一个VIP(虚IP),集群中不同的机器采用相同IP地址,但是机器的MAC地址不一样,改写报文的目标MAC地址的方式将请求转发到目标机器实现负载均衡
三层负载均衡 VIP 提供一个VIP(虚IP),但是集群中不同的机器采用不同的IP地址。根据不同的负载均衡算法,获得并转发至不同的真实服务器IP LVS
四层负载均衡 TCP,UDP 四层协议中除了包含源IP、目标IP以外,还包含源端口号及目的端口号。在三层负载均衡的基础上,用ip+port接收请求,再转发到对应的机器。 F5硬件负载均衡,LVS,Haproxy
七层负载均衡 http,代理 应用层协议较多,常用http、radius、dns,根据请求内容负载均衡 Nginx,Haproxy
http重定向 302跳转 根据请求信息,下发一个新的地址,实现负载均衡 逻辑根据业务场景实现,一般用在CDN厂商文件下载,根据用户位置,CDN情况,文件热点等,动态的下发一个可用的CDN文件下载地址
  • 其它
名称 类型 特点 建议
全局负载均衡器GSLB Http 多数据中心支持 基于DNS、重定向、路由协议实现。用于CDN 就近性,健康检查策略来动态分配流量
数据层负载均衡 数据的均衡 均衡可以通过数据的均衡与请求的均衡实现
微服务有关的负载均衡
  • 一般从请求入口到后台每一层用到的负载均衡:
类型 特点 建议
DNS Server 支持基于地址的域名解析,解析成距离用户最近的服务器地址 一般用作第一级负载均衡
TCP 负载均衡 (四层) 海量流量转发 LVS,一般作为前端接入的负载均衡
Http 负载均衡 (七层) Http Nginx负载均衡,可根据request head中Host配置路由规则(反向代理), 也可以和注册中心集成
微服务网关负载均衡 Http Spring Cloud Gateway:路由配置,限流,安全,熔断等,通过注册中心实现路由动态配置,并接入后端微服务
服务层负载均衡 客户端负载均衡 ; 或服务端负载均衡(dns,vip) 和微服务框架有关
实际上服务之间(服务层)的负载均衡有几种实现方式:
类型 特点 常用
集中式 由统一的负载均衡服务器来路由 DNS Server->Load Balancer Server
进程内 服务和负载均衡是一个进程,一般由服务框架提供的本地jar包实现 Dubbo, spring cloud Ribbon
独立进程 服务所在主机独立一个进程做负载均衡,也称作sidecar 或proxy模式负载均衡 service mesh
负载均衡算法
名称 说明
轮询算法 顺序循环,DNS Server
随机算法 随机分配,不适合机器配置不同的场景
加权算法 考虑到机器的差异性,分配给机器不同的权重;可以应用在轮询,随机,最少链接,Hash等算法。
哈希法 同一IP地址的请求,同一会话期内,转发到相同的服务器
一致性哈希 减少集群机器变化带来的影响
最少连接算法 优先选择连接数最少的服务器,动态分配,需要监控服务器请求连接数

超时与重试:

超时:

从服务调用的发展来看,或从基础到上层框架来看,有如下一些超时控制:

  • TCP超时有一系列控制,其中和连接,发送有关的是:

    Connection-Establishment Timer    建立连接超时
    Retransmission Timer    发送等待ACK,没有等到就会重传,重传到最大次数超时
    
  • Socket BIO,阻塞式IO:

    连接        三次握手超时,connect(SocketAddress endpoint, int timeout)
    写        缓冲区等待,TCP发送超时重传(超时时间操作系统可配置)
    读        setSoTimeout,一次读取数据的等待超时时间
    
  • HttpClient:

    老版本:
    setConnectTimeout:建立连接超时时间
    setSocketTimeout: 读取数据的等待超时时间(或两个数据包之间的最大间隔时间),对应BIO-
    新版本增加了:
    setConnectionRequestTimeout:从连接池获取可用连接的等待超时时间
    
  • Socket NIO:

    连接,写,读 不需要阻塞,所以没有超时时间
    
  • Netty ,异步非阻塞IO ,框架层在NIO基础上增加了:

    连接超时: Scheduled定时器,超时后关闭连接。 提供方法可以设置。
    
  • Dobbo 等RPC框架,在Netty基础上增加了:

    同步发送并等待response数据的超时时间。并提供外部配置,以及超时后的重试次数。
    
  • Spring Cloud RPC Feign ,在Netty基础上主要提供了:连接,同步发送等待response返回的超时时间:

    ConnectTimeout:请求连接的超时时间
    ReadTimeout:请求处理的超时时间
    

    Feign 可以单独设置 ,默认会读取ribbon的两个时间设置。建议用ribbon的。

重试
  • 重试是配合调用超时,或网络抖动异常时,进行再一次的发送,一般可以配置重试的策略和次数
  • Spring Cloud Ribbon:
    OkToRetryOnAllOperations:对所有操作请求都进行重试
    MaxAutoRetriesNextServer:切换实例的重试次数 , 默认2次(不包括首次)
    MaxAutoRetries:对当前实例的重试次数
    
    超过最大重试还超时,则由框架抛出异常或进行熔断处理
  • 注意: 超时时间不能太短, 重试次数不能太多,不合理的设置会造成请求堆积不断增长,造成服务器雪崩

熔断与降级:

熔断
  • 熔断器
    断开保护开关,如果服务方已经不可用了,调用者默认的重试策略又增大了流量,导致资源瓶颈后调用者也不可用,最终导致了服务的雪崩效应。
    熔断器的目的是让服务调用拥有自我保护和恢复的能力.

  • 资源隔离:
    通过将每个依赖服务分配独立的资源(线程池,或信号量)进行资源隔离, 从而避免服务雪崩.

  • 熔断器模式:

  1. 当熔断器开关关闭时, 请求被允许通过熔断器. 如果当前健康状况高于设定阈值, 开关继续保持关闭. 如果当前健康状况低于设定阈值, 开关则切换为打开状态.
  2. 当熔断器开关打开时, 请求被禁止通过,并进行降级处理。
  3. 经过一段时间后, 熔断开关为半打开状态,部分流量流入,当请求成功时,开关关闭。若该请求失败, 熔断器继续保持打开状态, 接下来的请求被禁止通过.

降级处理后,根据业务级别,在服务端恢复后,进行补偿逻辑处理。

  • spring cloud:
    hystrix : 熔断器 
    可以配置熔断超时时间 timeoutInMilliseconds
    

Hystrix 超时时间控制的是总的请求处理时间,所以Hystrix超时时间要包括 ribbon超时时间 * 重试次数 。
Hytrix,ribbon可以分服务单独设置timeout。

Circuit Breaker 断路器行为配置:
    requestVolumeThreshold 滑动窗口的大小(检测的数据范围),默认为20 
    sleepWindowInMilliseconds 多久以后开始尝试是否恢复,默认为5000ms
    errorThresholdPercentage 错误率,默认50%
  • Hystrix Metrics
    Hystrix 提供了实时的监控,包括请求成功,失败(客户端抛出的异常),超时和线程拒绝。

  • Hystrix Dashboard : Hystrix Metrics的展示
    监控单实例服务,需要通过访问实例的/actuator/hystrix.stream接口来实现(依赖actuator)
    监控集群服务,需要通过stream/mq 发送到Turbine进行聚合,再用Dashboard 进行统一展示。

降级:
  • 降级方法: 当熔断器打开后,快速失败,进行降级处理。
    比如: 非关键步骤可以跳过,或可以先保存下来,之后等到对方恢复了,再进行补偿。

  • spring cloud:

    hystrix可以配置 fallbackMethod 熔断后的降级方法。
    @HystrixCommand(fallbackMethod = "") 
    @FeignClient(value="", fallback = )
    

限流:

  • 因为一切系统皆有瓶颈,所以首先要保证可以活下来。
    服务进入的流量大小一般小于出口依赖服务的能力,大于则调用超时或者异常;多个调用则以最小的能力 为准;进入时和 出口的服务端流入时, 一般需要做限流;假如调用方 ,服务提供方数量会变化,那么 ,这种流量需要通过分布式限流来控制更准确;服务的调用方往往有多个,所以,服务端需要更精确的限流策略来分配流量。
限流算法:
  • 固定、滑动时间窗口限流算法

  • 令牌桶、漏桶限流算法
    常用Google Guava提供的 RateLimit 工具

  • 分布式限流算法
    一般采用redis计数器实现,需要注意:
    数据一致性问题 , 超时问题 , 性能问题

  • 限流拒绝策略
    拒绝
    阻塞
    降级等

限流规则配置

时间粒度
接口粒度
最大限流值

服务自身限流:

首先要保护自己,那么就要保证接入的流量在该应用-服务的最大tps,qps之内。一般采用单机限流算法就可以。

服务网关限流

外部过来的请求,因为网关多实例集群(因为网关实例也会发生变化),一般采用分布式限流策略。

Reactive Stream限流:

关于回压式流,可以参考上一篇“反应式”的文章。

服务网关

微服务的统一入口,是外部请求和内部微服务之间的桥梁。主要功能包括:

  • 路由: 外部请求Path等规则到微服务的对应关系。一般包括请求规则断言和过滤器。
    断言:匹配 HTTP 请求的任何内容,例如 headers 或参数。如果断言为真,则路由匹配。
    过滤器:修改请求和响应。
    路由的配置可以支持动态更新。
  • 安全:
    鉴权、脱敏,流量清洗,后端签名,黑名单等
  • 限流:
    因为微服务的数量会变化,所以,服务网关限流一般采用分布式限流算法。限流的粒度包括:
    服务粒度
    用户粒度,用户,IP
    ORIGIN粒度
    接口粒度
    自由组合与自定义
    与监控配合,进行限流操作
  • 调用时的超时重试,熔断降级
  • 日志记录,性能监控
  • 灰度(请求规则和Ribbon规则匹配)

  • Spring Cloud Gateway
    基于反应式的异步非阻塞网关
    参考:
    https://github.com/spring-cloud/spring-cloud-gateway

调用链

调用链介绍

服务网关接入微服务的一次请求调用,请求的这个微服务可能又依赖并调用了多个微服务,或中间件等。
调用链就是追踪上报收集服务调用过程中经历了哪些组件、哪些微服务、请求总时长、每个组件所花时长等信息。用于故障定位,统计分析和监控。
调用链属于APM (Application Performance Management) 即应用性能管理监控体系的一部分,并遵循分布式追踪OpenTracing标准,具体可以参考:
https://github.com/opentracing-contrib/opentracing-specification-zh/blob/master/specification.md
http://opentracing.io/documentation/pages/translations.html

调用链一般需要由以下组件构成:
数据埋点上报 - Spring Cloud Sleuth
数据收集,保存,提供查询API - Zipkin
查询展示 - Zipkin 或其它

其它开源框架还有:
Pinpoint, Skywalking, CAT, EgleEye

Spring Cloud

Sleuth
  • 可用于跟踪集成的组件,埋点类型有:
    Runnable and Callable
    Hystrix
    RxJava
    HTTP integration
    HTTP Client Integration
    Feign
    Asynchronous Communication
    Messaging

  • 采样类型
    AlwaysSampler
    NeverSampler
    IsTracingSampler
    PercentageBasedSampler
    常用的配置:spring.sleuth.sampler.probability=

  • 数据上报的2种方式:

  1. 通过http直接上报Zipkin
  2. 通过spring cloud stream 消息中间件发送Zipkin

具体可以参考官方文档:
https://cloud.spring.io/spring-cloud-sleuth/

Zipkin
  • Zipkin Client:
    zinkin埋点客户端如Sleuth,目前客户端官方及第三方提供的支持有java,C,python,JavaScript等
  • Transport:
    传输方式,http请求 或 消息中间件
  • Database:
    可以配置为mysql、elasticsearch或Cassandra持久化。
  • Zipkin Server:
    用于由Collector收集、存储、聚合及展示跟踪数据

一般也可以使用ELK,来收集和展示,便于个性化的需求扩展实现。

官方文档可以参考:
https://github.com/openzipkin/zipkin

微服务实践

最后,分享一个用Spring Cloud Finchley 搭建的微服务实践样例:
https://github.com/masenmiao/acloud-finchley-example