六、服务路由

分布式服务框架上线运行时都是集群组网,这意味着急群众存在某个服务的多实例部署,消费者如何从服务列表中选择合适的服务提供者进行调用,这就涉及到服务路由。

1 透明化路由

1.1 基于服务注册中心的订阅发布

在分布式服务框架中,服务注册中心用于存储服务提供者地址信息、服务发布相关的属性信息,消费者通过主动查询和被动通知的方式获取服务提供者的地址信息,而不需要像之前那样在代码中硬编码服务提供者地址信息。消费者只需要知道当前系统发布了哪些服务,而不需要知道服务具体存在于什么位置,这就是透明化路由。它的工作原理就是基于服务注册中心(例如 ZooKeeper)的订阅发布机制。
服务注册中心的工作原理如下:

由于消费者可能由于服务提供者启动,或者系统运行过程中新增服务提供者,或者某个服务提供者宕机退出,就会导致注册中心发生服务提供者地址变更。注册中心检测到服务提供者列表变更之后,将变更内容主动推送到服务消费者,消费者根据变更列表,动态刷新本地缓存的服务提供者地址。

1.2 消费者缓存服务提供者地址

采用客户端缓存服务提供者地址的方案不仅仅能提升服务调用性能,还能保证系统的可靠性。当注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存的地址信息进行通信,只是影响新服务的注册和老服务的下线,不影响已经发布和运行的服务。

2 负载均衡

2.1 随机

缺点:

  1. 在一个截面上碰撞的概率较高。
  2. 非对等集群组网,或者硬件配置差异较大,会导致各节点负载均匀。

2.2 轮询

轮询,按公约后的权重设置轮询比率,到达边界之后,继续绕接。缺点:

  1. 存在慢的提供者累计请求问题,例如第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。

2.3 服务调用时延

消费者缓存所有服务提供者的服务调用时延,周期性的计算服务调用平均时延,然后计算每个服务提供者服务调用时延与平均时延的差值,根据差值大小动态调整权重,保证服务时延大的服务提供者接收更少的消息,防止消息堆积,

该策略的特点就是要保证处理能力强的服务提供者接收到更多的消息,通过动态自动权重调整消除服务调用时延的振荡范围,使所有服务提供者服务调用时延接近平均值,实现负载均衡。

2.4 一致性哈希

相同参数的请求总是发到同一个服务提供者,当某一台提供者宕机时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。平台提供默认的虚拟节点数,可以通过配置参数进行修改。
一致性 Hash 环工作原理如下:

2.5 粘滞连接

粘滞连接用于有状态服务,尽可能让客户端总是向同一提供者发起服务调用,除非该提供者宕机,再连接另一台。由于服务通常被强烈建议设计成无状态的,因此,粘滞连接在实际项目中很少使用。
粘滞连接的实现比较简单,客户端首次跟服务端创建链路时,将该链路标记为粘滞连接,每次路由时直接选择粘滞连接,不执行负载均衡路由接口,当链路中断时,更新粘滞连接为不可用,重新寻找下一个可用的连接,将其标记为粘滞连接。

3 本地路由优先策略

3.1 injvm 模式

在一些业务场景中,本地 JVM 内部也发布了需要消费的服务。该场景下,从性能、可靠性等角度考虑,需要优先调用本 JVM内部的服务提供者,这种本地优先的路由模式被称为 injvm模式。

3.2 innative 模式

如果物理机或者 VM配置较好,多个应用金城往往会选择合设。服务消费者和服务提供者可能会被部署到同一台机器上(VM)。服务路由时优先选择本机的服务提供者,如果找不到再重新发起远程服务调用,该模式被称为 innative模式。

  1. 首先看本进程 JVM内部是否有符合要求的服务提供者。
  2. JVM 内部没有,选择服务提供者 IP地址与本机 IP地址相同的本地合设的服务提供者进程,通过本地网卡回环调用服务提供则。
  3. 如果 VM内部没有,则发起远程调用。

4 路由规则

4.1 条件路由规则

应用场景如下:

  1. 通过 IP条件表达式进行黑白名单访问控制,例如 comsumerIP != 192.168.1.1
  2. 流量引导,只暴露部分服务提供者,防止整个集群服务都被冲垮,导致其它服务也不可用,例如 providerIP = 192.168.3*
  3. 读写分离:method=find,list,get,query=>providerIP=192.168.1.*
  4. 前后台分离:app=web=>providerIP=192.168.1.,app=java=>providerIP=192.168.2.
  5. 灰度升级,将 Web前台应用路由到新的服务版本上:app=web=>providerIP=192.168.1.

4.2 脚本路由规则

使用脚本来实现路由规则,在于动态编译,修改实时生效,常见的脚本语言有 JavaScript、Groovy、MVEL 等。

5 路由策略定制

除了提供默认的路由策略之外,在架构上还需要支持业务扩展路由算法,实现业务自定义路由。

  1. 灰度升级,用户需要按照业务规则进行灰度路由:例如按照用户省份路由、按照请求来源中断类型(IOS、Android)、按照手机号段等;不同的用户按照规则路由到不同的集群环境中,例如没有同步升级的用户路由到升级前的环境,同步配套升级的消费者请求路由到灰度升级后的新版本中。
  2. 服务故障、业务高峰期的导流:通过自定义路由,将异常的峰值流量导流到几台或者1 台服务器上,防止整个集群负载过重导致整个生产系统雪崩。

路由扩展策略如下:

  1. 提供接口。
  2. 提供配置 XML Schema定义。
  3. 通过 Spring Bean 方式的服务发布、通过 JDK 的 SPI 方式扩展,即 META-INF/services。

6 配置化路由

  1. 本地配置:包括服务提供者和服务消费者、默认全局配置三种。
  2. 统一注册管理:无论是服务提供者还是消费者,本地配置的路由策略统一注册到服务注册中心,进行集中化配置管理。
  3. 动态下发:运维人员通过服务治理 Portal修改路由规则,更新后的路由规则被持久化到服务注册中心。

路由配置优先级:客户端配置>服务端配置>全局配置。

7 最佳实践————多机房路由

为了能够相互发现对方的服务,不同机房会共用同一个服务注册中心集群(异地容灾机房除外)。假如机房1 发布了服务A,机房2 同样也发布了服务A,此时服务注册中心就会将2 个不同机房的服务A 地址信息推送给消费者,无论是机房1 还是机房2 的消费者,都将看到两个不同机房的服务。

如果仅仅依靠随机、轮询等负载均衡策略,消息将会被路由到两个机房,达不到不跨机房调用的目标。此时可以使用配置条件路由策略,通过网段条件匹配来实现地址过滤。
也可以使用虚拟分组策略,将整个集群系统的服务提供者(跨机房)逻辑分成若干个组,某个消费者只访问一个虚拟分组的服务提供者,防止跨组服务调用。

8 个人总结

服务路由需要既具备丰富的路由策略,还要具备扩展能力,是非常重要的分布式下的基础功能。