报错信息

使用Groovy脚本配置服务的路由规则未生效。

可能原因

上述报错信息表明调用没有被路由到预期的机器上,可能原因是规则配置错误,例如路由规则填写错误、路由规则里的IP没有提供服务等。路由规则的配置方式,请参见 Routing Rule Wiki

解决方案

您可以通过以下步骤,排查路由规则是否生效。

步骤一:客户端是否为本地调用、泛化调用

如果客户端通过本地调用、泛化调用的方式消费服务,将不会使用路由规则逻辑,因此会出现路由规则不生效的情况。

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

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

步骤二:客户端是否收到路由规则

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上的环境和客户端所在的环境是否一致。

步骤三:路由规则是否正确解析

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 ,检查您的路由规则。

步骤四:路由规则的内容是否正确

收到路由规则且解析正确后,如果路由规则执行的效果仍与预期不同,请依次检查以下内容。

  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 empty 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中找不到相应值,则认为这一级对路由地址不做限制,地址取上一级的全部地址。

    总体的计算过程如下:
    • 接口级路由规则(正则式)最终地址列表会和同机房规则结果地址列表作交集。
    • 方法级路由规则(正则式)最终地址列表会和接口级地址列表作交集。
    • 参数级路由规则(正则式)最终地址列表会和方法级地址列表作交集。

步骤五:路由规则是否被调用

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

可以通过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.";
                }
            }
        }
    }
}