服务路由

服务路由:分布式服务架构上线时都是集群组网,这意味着集群中存在某个服务的多实例部署,消费者如何从服务列表中选择合适的服务提供者进行调用,这就涉及到服务路由。分布式服务框架要能够满足用户灵活的路由需求。

1、透明化路由
    很多开源的RPC框架调用者需要配置服务提供者的地址信息,尽管可以通过读取数据库的服务地址列表等方式避免硬编码地址信息,但是消费者依然要感知服务提供者的地址信息,这违反了透明化路由原则。

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

         

                                        图1-1  服务注册中心工作原理图

       服务消费者和提供者通过注册中心提供的SDK与注册中心建立链路(例如Zookeeper采用长连接),服务提供者将需要发布的服务地址信息和属性列表写入注册中心。消费者根据本地引用的接口名等信息,从服务注册中心获取服务提供者列表,缓存到本地。

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

    1.2 消费者缓存服务提供者地址
        服务消费者调用服务提供者时,不需要每次调用时都去服务注册中心查询服务提供者地址列表,消费者客户端直接从本地缓存的服务提供者路由表中查询地址信息,根据路由策略进行服务选择。
        当服务提供者发生变化时,注册中心会主动将变更内容推送给消费者,由后者动态刷新本地库中缓存的路由表,保证服务路由信息的实时准确性。
        采用客户端缓存服务提供者地址的方案不仅仅能够提供提升消费者调用性能还能提高系统的可靠性。当注册中心全部宕机后,消费者可以通过缓存的地址信息和服务提供者之间进行通信,只是影响新服务的上线和老服务的下线,不影响已发布的和运行的服务

2、负载均衡
   
负载均衡策略是服务的重要属性,分布式服务框架通常会提供多种负载均衡策略,同时支持用户扩展负债均衡策略。

    2.1、随机
       
通常在对等集群组网中,采用随机算法进行负债均衡,随机路由算法消息分发还是比较均匀的。它的主要缺点:
            1)、在一个截面上碰撞的概率较高。
            2)、非对等集群组网中,或者硬件配置差异较大,会导致各节点负载不均匀。
         通常在实现上会采用JDK提供的java.util.Random或者java.security.SecureRandom在指定服务提供者列表中生成随机地址。消费者基于随机生成的服务提供者地址进行远程调用。

    2.2、轮询
       
轮询,按照公约后的权重设置轮询比率,到达边界之后,继续绕接。他的主要缺点:存在慢的提供者累积请求问题。例如第二台机器很慢,但没挂,当请求第二台机器时就卡在那。久而久之,所有请求都卡在第二台机器上。
        轮询策略实现非常简单,他就是通过权重,顺序循环遍历服务提供者列表,达到上线之后重新归零,继续顺序循环。

    2.3、服务调用时延
       
消费者缓存所有服务提供者的服务调用时延,周期性的计算服务调用平均时延,然后计算每个服务提供者服务调用时延与平均时延的差值,根据差值大小动态调整权重,保证服务时延大的服务提供者接收更少的消息,防止消息堆积。
        该策略的特点:保证处理能力强的服务接受更多的消息,通过动态的权重分配消除服务调用时延的震荡范围,使所有服务的调用时延接近平均值,实现负载均衡。

    2.4、一致性哈希
       
相同参数的请求总是发送到统一服务提供者,当某一台服务提供者宕机时,原本发往跟提供者的请求,基于虚拟节点,平摊到其他提供者,不会引起剧烈变动,平台提供默认的虚拟节点数,可以通过配置文件修改虚拟节点个数。一致性Hash环工作原理如下图1-2所示:
            
                    图1-2:一致性Hash环工作原理图

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

3、本地路由优先策略

    3.1、injvm模式
       
在一些业务场景中,本地JVM内部也发布了需要消费的服务。在改业务场景下,从性能和可靠性的角度考虑,需要优先调用本地内部服务提供者,这种本地优先的路由别成为injvm模式。
        injvm协议是一种伪协议,它不开启端口,也不发起远程服务调用,优先调用本地JVM的服务提供者,相比于其他路由策略,它的执行流程相同,只不过将远程服务调用替换成了内部调用。
        如果用户配置了injvm理由模式,则优先寻找本地JVM内的服务提供者,不存在时发起远程服务调用。对于上层用户而言,不感知底层具体的调用方法。

    3.2、innative模式
       
如果物理机或VM配置比较好,多个应用进程会选择和设。服务消费者和提供者可能会被部署到同一个机器上(VM)。服务路由时优先选择本地的服务提供者,如果找不到则发起远程服务调用,该模式被称为innative模式。
        innative模式的优点在于通信流量走本地网卡回环,不占用网络带宽。另外可靠性更强、服务调用时延也很低,更节省带宽等资源。

4、路由规则
   
以上可满足大部分业务线上的需求,但是在一些业务场景中需要设置一些过滤规则,比较常用的是基本表达式的条件路由和脚本路由。

    4.1、条件路由规则
       
条件路由应用场景:
            1)、通过IP条件表达式配置黑白名单访问控制:consumerIP != 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*=>provicerIP=192.168.1.*。
        基于表达式的路由规则主要用于地址二次过滤,它由两部分组成:规则和表达式。具体如下:
        条件规则主要由消费者规则和服务提供者规则组成,可以用分隔符将其分隔开,例如:=>、:等。分隔符之前的为消费者规则,将消费者URL与其比较符合规则的就可以执行后面的过滤规则。分隔符之后为提供者地址过滤规则,所有参数与提供者URL进行比较,消费者只能拿到过滤后的地址列表。
        对空条件规则支持:如果配置条件为空,表示对所有消费放都是用;但服务提供者配置条件为空时,相反所有的服务提供者都禁止被访问。

        表达式由一下三部分组成:
             ①、参数:主要包括服务提供者的属性信息、消费者的属性信息。例如:method、IP等。
             ②、条件:就是Java表达式,例如(!=、=、&、|)。
             ③、值:包括常量,例如:192.168.1.1,模糊匹配*,变量$+变量名,例如$parameter。     

    4.2、脚本路由规则       
        脚本路由的特点就是通常都支持动态编译,修改后可以实时生效,不需要重启系统,这在线上动态调整路由规则非常有效。
        经常系统的脚本语言有JavaScript、Groovy、MVEL等。

5、路由策略定制
   
平台除了提供默认的路由策略之外,在架构上还需要支持业务扩展路由算法,实现业务自定义路由。
    自定义路由的主要应用场景如下:
        1)、灰度升级,用户需要按照业务规则进行灰度路由:例如按照用户省份进行路由、按照请求终端类型(ISO、安卓等);不同的用户按照路由规则路由到不同的集群环境中,例如没有同步升级的用户路由到升级前的环境,同步配套升级的消费者请求路由到灰度升级后的新版本中。
        2)、服务故障、业务高峰期引流:通过自定义路由,将异常的峰值流量导流到几台或者一台服务器上,防止整个集群负载过重导致整个系统雪崩。
        3)、业务领域强相关的非通用路由定制需求。

    路由扩展策略如下:
         ①、平台提供路由扩展接口供用户实现。
         ②、平台提供理由配置XML Schema定义,支持路由扩展定义。
         ③、业务发布自定义路由策略,对于通过Spring Bean方式的服务发布,用户定义扩展路由bean,然后在服务者配置路由规则的时候,引入相关配置的bean即可。      

        通过扩展的方式定制分布式服务框架路由策略,可以非常容易地实现平台和基线,基线和业务定制的分离,通过扩展的方式增加新功能,可以保证微内核架构的相对稳定性,防止平台架构腐化。新增理由策略不需要平台发布新的版本,业务可以实现更加敏捷的项目交付。

6、配置化路由
   
路由配置策略设计如下:
      1)、本地配置:包括服务提供者和服务消费者、默认全局配置三种。
      2)、统一注册管理:无论是服务提供者还是消费者,本地配置的路由策略统一注册到注册中心,进行集中化配置管理。       
      3)、动态下发:运维人员通过服务智力Portal修改路由规则,更新后的路由规则被持久化到服务注册中心。服务注册中心将变更后的服务路由策略或规则下发给服务提供者和消费者,消费者更新本地路由规则库,按照新的路由规则执行,实现路由动态刷新。

     路由配置优先级:客户端配置——>服务端配置——>全局配置。路由策略支持在服务端和客户端同时配置,为什么这么设计呢?下面对路由配置优先级的设计原理进行详细讲解。

     服务提供者更了解服务内部的运行状态,服务提供者通常会和消费者签订《服务质量等级协议(SLA)》,服务提供者需要对服务的调用Caps(每秒建立的呼叫数量)、服务调用延迟、能够支持的最大并发数、流控策略等向消费者做出承诺。基于此,服务相关的属性应该由服务提供者配置最合理。

     另外一个原因是消费者众多,不是每个消费者都愿意配置服务的路由策略,如果在客户端配置一个全局的路由策略,配置虽然简单,但是实用性很差,因为不是每个服务路由策略都一样。此时消费者希望由熟悉服务质量属性的服务提供者统一配置路由策略,客户端直接使用,降低开发和维护工作量。

     消费者配置路由策略的原因:站在消费者角度,更清楚自己想要达到的路由效果,例如同一个服务提供者,本机、同一个机框和同一个机房的消费者获得的服务调用时延差异都很大,这种差异服务提供者并不清楚,需要消费者根据自己的实际使用效果动态调整,这是客户端配置就需要覆盖服务端配置。

7、最佳实践——多机房路由
    
随着业务的不断发展,单个机房的容量已经不足以支撑未来业务的发展,业务开始跨机房部署。

     跨机房服务调用会带来时延增加、网络故障概率变大等问题,如何避免跨机房服务调用需要从路由策略上进行考虑。

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

      如果仅仅依靠随和、轮询等路由策略,消息将被路由到两个机房,达不到不跨机房的目标,如何解决跨机房的调用问题?利用第4节条件路由规则,可以解决。

      在原有的负载均衡策略基础上,配置条件路由策略,由于不同机房的网段不同,可以根据网段条件匹配来实现地址过滤,具体配置策略如下:app= web*,consumerIP = 192.168.1.*。对机房A中所有的Web前台应用,只访问本机房内部的服务提供者,不跨机房调用。

       还有一种解决策略,就是虚拟分组:将整个集群系统的服务提供者(跨机房)逻辑分成若干个组,某个消费者只访问一个虚拟分组的服务提供者,防止跨服务调用。如果多机房部署,虚拟分组可以对应1个机房;如果是单机房,则虚拟分组可以对应1个机框。通过拆分和分组,防止某个消费者看到集群中的所有服务提供者,既可以减少竞争提升性能,也可以增强故障隔离能力。

       需要指出的是,当某个机房发生大面积宕机或者服务提供者无法正常工作时,需要跨机房访问其他健康的服务提供者,访问某个机房故障导致业务中断。

总结:服务路由是分布式服务框架的重要特性,作为基础平台,既要内置丰富的路由策略,同时还要具体灵活的扩展能力,方便用户做二次定制。

猜你喜欢

转载自blog.csdn.net/zhengzhaoyang122/article/details/80842859