HSF 支持用户使用 Groovy 脚本配置服务的路由规则。

路由规则的配置方式,请参见 Routing Rule Wiki,在完成路由规则的配置后,您可能会发现路由规则没生效,这些没生效一般就是指调用并没有被路由到预期的机器上,然而这些没生效有可能是其他因素引起的,例如路由规则没写对、路由规则里的 IP 没有提供服务等。

这里,就来介绍下如何排查路由规则没有生效,共包含以下几步:

1. 客户端是否为本地调用、泛化调用

如果客户端通过本地调用、泛化调用的方式消费服务的话,那么是不会使用路由规则逻辑的,自然也就出现了路由规则不生效这种表象。

  • 本地调用:指的是一个进程即是一个服务的发布者,又是这个服务的消费者,这个时候 HSF 默认优先走本地调用,即进程内的 Java 调用,而非 RPC 调用。这种情况,是不走路由规则的逻辑的。
  • 泛化调用:指的是不依赖服务的二方包,直接通过 GenericService 通过服务描述进行消费的场景,这个时候 HSF 由于没有服务的 Class,无法执行路由规则里调用业务类的逻辑,因此也不走路由规则的逻辑。

HSFOPS 上的服务测试功能走的是泛化调用,因此也不会生效路由规则。

2. 客户端是否收到路由规则

HSF 的路由规则存放在 Diamond 上,在客户端启动时,会从 Diamond 上拉取服务对应的路由规则。

hsf.log(一般的路径是 HSF 2.2:${user.home}/logs/hsf/hsf-config.logHSF 2.1:${user.home}/logs/hsf/hsf.log)中搜索服务名,如果正确收到路由规则,会看到类似如下的日志:

01 2015-10-09 13:20:06.402 WARN [com.taobao.diamond.client.Worker.default:t.hsf] [] [] [] [Metadata Component] Received rule for service [com.alibaba.dt.op.authclient.api.ResourceAPI:1.0.0.daily]: Groovy_v200907@package hqm.test.groovy
public class RoutingRule{
    Map<String, List<String>> routingRuleMap(){
        return [
            "G1":["100.69.161.201:*"]
        ]
    }

    String interfaceRoutingRule(){
        return "G1";
    }
}
			

如果没有收到路由规则,请检查:

  1. 路由规则的命名是否与服务名对应,具体请参见 Routing Rule Wiki
  2. 路由规则配置在 diamond 上的环境和客户端所在的环境是否一致。

3. 路由规则是否正确解析

hsf.log 中搜索服务名,如果 HSF 的路由规则解析正确,会看到类似如下的日志:

01 2015-10-09 13:20:06.761 INFO [com.taobao.diamond.client.Worker.default:t.hsf] [] [] [] Parse route rule successed, RouteRule:com.taobao.hsf.route.service.RouteRule@4441ec5a{
keyedRules={G1=[100.69.161.201:*]},
interfaceRule=G1,
methodRule={},
argsRule={}
}
			

如果 HSF 的路由规则解析失败,会看到相关的失败信息,请对照日志和 Routing Rule Wiki 检查您的路由规则。

4. 路由规则的内容是否正确

路由规则收到了、也解析对了,如果路由规则执行的效果还与预期不同,请依次检查以下内容:

  1. 路由的目标机器是否真的提供了该服务

    请在对应环境的 HSF 服务治理平台上查询服务,看路由的目标 IP 是否都在 Providers 列表中。

    • 在老版本的 HSF 中,如果路由规则指定的 IP 没有提供服务,会报地址找不到错误
    • 在新版本的 HSF 中(2.1.0.7 开始),如果路由规则指定的 IP 没提供服务,会提示开启空保护,那么此时路由规则就不再生效,HSF 会从 ConfigServer 存放的 Provider 地址列表中为客户端选取一个可用 IP 进行调用。在使用方法路由时,如果碰到算出的地址为空,这种情况下,在 hsf.log 中搜索服务名,会看到类似如下的日志。其中空保护开启的标志为倒数第二行的 EmptyProtection triggered [true]:

      01 2015-10-09 13:20:06.761 WARN [HSF-AddressAndRule-2-thread-1:t.hsf] [] [] [] [Address Component] isEmptyProtection: true
      01 2015-10-09 13:20:06.761 WARN [HSF-AddressAndRule-1-thread-1:t.hsf] [] [] [] [Address Component] isEmptyProtection: true
      01 2015-10-09 13:20:06.761 WARN [HSF-AddressAndRule-2-thread-1:t.hsf] [] [] [] [Address Component] newAllAvailableAddresses is emtpy for service : com.alibaba.dt.op.authclient.api.ResourceAPI:1.0.0.daily
      01 2015-10-09 13:20:06.761 INFO [HSF-AddressAndRule-2-thread-1:t.hsf] [] [] [] [AddressBucket-com.alibaba.dt.op.authclient.api.ResourceAPI:1.0.0.daily] Refresh: all amount [0], available amount [0], local preferred switch [off].Unit=UNIT
      01 2015-10-09 13:20:06.761 INFO [HSF-AddressAndRule-1-thread-1:t.hsf] [] [] [] [Address Component] route result com.alibaba.dt.op.authclient.api.ResourceAPI:1.0.0.daily, addresses remain[1], EmptyProtection triggered [true]
      01 2015-10-09 13:20:06.762 INFO [HSF-AddressAndRule-1-thread-1:t.hsf] [] [] [] [AddressBucket-com.alibaba.dt.op.authclient.api.ResourceAPI:1.0.0.daily] Refresh: all amount [1], available amount [1], local preferred switch [off].
      							
  2. 路由规则是否正确

    仔细检查您的接口路由、方法路由、参数路由,是否真的写对了。

  3. 地址计算优先级

    如果开启了同机房规则,那么同机房的地址优先于路由规则的地址,路由规则的地址在同机房规则结果的地址集合里再计算。

    路由规则分为接口级 > 方法级 > 参数级,若某一级的路由规则不存在,即其返回的 key 为 null,或在 routingRuleMap 中找不到相应值,则认为这一级对路由地址不做限制,地址取上一级的全部地址。

    总体的计算过程如下:

    • 接口级路由规则(正则式)最终地址列表会和同机房规则结果地址列表作交集
    • 方法级路由规则(正则式)最终地址列表会和接口级地址列表作交集
    • 参数级路由规则(正则式)最终地址列表会和方法级地址列表作交集

5. 路由规则是否真的被调用

如果按照以上内容排查后,都没有问题,请再确认路由规则指定的服务/方法是否真的被调用了? 如果相应的方法没有被调用,则在目标机器上肯定也搜不到调用日志。

可以通过 System.out.println() 在路由规则中打印内容来确认,同时注意返回的内容必须为一个闭包。例如:

ovy_v200907@package hqm.test.groovy
public class RoutingRule {
    Map<String, List<String>> routingRuleMap() {
        return [
            "BSeller_address_filter_Key":["10.177.75.54:*"],
            "ASeller_address_filter_Key":["10.97.94.33:*"]
        ]
    }

    Object argsRoutingRule(String methodName, String[] paramTypeStrs) {
        if (methodName.startsWith("checkUrlPermission")) {
            System.out.println("Match the checkUrlPermission method.");
            return {
                Object[] args ->
                if(args[1] % 2 == 1 ) {
                    System.out.println("Route to BSeller_address_filter_Key");
                    return "BSeller_address_filter_Key.";
                } else {
                    System.out.println("Route to ASeller_address_filter_Key");
                    return "ASeller_address_filter_Key.";
                }
            }
        }
    }
}