Dubbo-Router条件路由、脚本路由使用

上篇文章介绍了服务目录 Directory。服务目录在刷新 Invoker 列表的过程中,会通过 Router 进行服务路由,筛选出符合路由规则的服务提供者。服务路由包含一条路由规则,路由规则决定了服务消费者的调用目标,即规定了服务消费者可调用哪些服务提供者。Dubbo 目前提供了三种服务路由实现,分别为条件路由 ConditionRouter、脚本路由 ScriptRouter 和标签路由 TagRouter

在这里插入图片描述
如图所示,经过Router过滤后的Invoker列表实际是Directory中Invoker列表的子集,起到过滤的作用

本文目的不在于分析Router实现的原理,个人认为学会Router路由规则的使用对实际工作更加有效果。本文会结合Dubbo keeper和实际例子来说明Router路由规则中条件路由脚本路由的使用

插播我自己瞎想面试题

  1. 如何屏蔽掉部分提供者或者消费者?
  2. 如何在Dubbo中实现读写分离(读写接口调用不同机器)?

希望读者能有目的性的去看文章

条件路由ConditionRouter

什么是条件路由

条件路由即基于条件表达式的路由规则,如:host = 10.20.153.10 => host = 10.20.153.11

什么意思呢?

  • => 之前的为消费者匹配条件,所有参数和消费者的 URL 进行对比,当消费者满足匹配条件时,对该消费者执行后面的过滤规则。
  • => 之后为提供者地址列表的过滤条件,所有参数和提供者的 URL 进行对比,消费者最终只拿到过滤后的地址列表。
  • 如果匹配条件为空,表示对所有消费方应用,如:=> host != 10.20.153.11
  • 如果过滤条件为空,表示禁止访问,如:host = 10.20.153.10 =>

所以上面条件路由例子的意思就是:所有消费者ip为10.20.153.10的请求全部交由ip为10.20.153.11的提供者处理

更多的规则说明可以参考Dubbo官方文档之路由规则

下面结合Dubbo keeper运维平台来说明条件路由规则使用,首先定义如下接口,就是简单三个方法,在这里插入图片描述

服务提供者同时部署在192.168.207.89(我本地机器),192.168.112.12测试机器上,相当于有2个提供者
在这里插入图片描述

假设我调用sayHello方法,则会同时路由到2台机器上,调用结果如下,既会调用到本地机器,也会调用到测试机器,具体调用哪个由配置的负载均衡策略决定
在这里插入图片描述

屏蔽部分提供者

比如192.168.112.12这台机器要上线,需要暂时切断到这台机器的dubbo调用,怎么做呢?

基本思路肯定是过滤掉提供者ip为192.168.112.12的Invoker

DubboKeeper中找到对应服务,点击按钮新建路由规则
在这里插入图片描述
在这里插入图片描述
路由详情中如下填下
在这里插入图片描述
此时再调用sayHello方法,效果图如下,192.168.112.12这台机器便不会被调用到
在这里插入图片描述

如果192.168.112.12发布、升级成功,可以被调用了,则点击路由规则的禁用按钮或者删除按钮即可

读写分离

有的时候,读写接口调用次数不平衡(读多写少的场景),所有请求全部打到一台机器上,可能会影响写入接口性能。基于Dubbo的Router也可以实现读写分离机制。即读接口调用A机器,写接口调用B机器,这样互不影响,减少了读接口对写接口的影响。

具体怎么做呢,还是以上面的读写接口为例
在这里插入图片描述

读接口read方法配置如下规则,等价于如下表达式:method = read* => host = 192.168.207.89,即读方法全部走本机调用在这里插入图片描述

调用读方法可以看到全部由我本机执行
在这里插入图片描述

写方法类似,等价于 method = write* => host = 192.168.112.12,即写方法全部调用测试机
在这里插入图片描述
在这里插入图片描述

脚本路由 ScriptRouter

什么是脚本路由?

脚本路由规则 支持 JDK 脚本引擎的所有脚本,比如:javascript, jruby, groovy 等,通过 type=javascript 参数设置脚本类型,缺省为 javascript

举个例子,如下脚本等价于,所有包含get的方法全部调用192.168.0.23这台提供者

(function(invokers,invocation,context){
var methodName = invocation.methodName;
var resultInvokers=[];
for(var i=0;i<invokers.length;i++){
    var invoker = invokers[i];
    var currentConsumerUrl = invoker.url;
    var providerUrl = invoker.providerUrl;
    var consumerHost=currentConsumerUrl.host;
    var providerHost = providerUrl.host;
    var providerProtocol = providerUrl.protocol;
    var currentService = providerUrl.getParameter("interface");
    if(methodName.indexOf('get')>=0&&providerHost=='192.168.0.23'){
        resultInvokers.push(invoker);
    }
}
//该方法返回值必须是一个Invoker的集合或者数组
return resultInvokers;})(invokers,invocation,context);

脚本路由实现读写分离

下面仍然以上文读写分离的例子来说明脚本路由的使用,目标还是读方法调用我本机(192.168.207.88),写方法调用(192.168.112.12)

Dubbo keeper中点击如下按钮新增脚本路由规则
在这里插入图片描述

配置如下脚本路由规则

(function route(invokers,invocation) {
    var result = new java.util.ArrayList();
    //读方法只调用192.168.207.88
    if("read".equals(invocation.getMethodName())) { 
        for (var i=0;i<invokers.size(); i++) {    
                if(invokers.get(i).getUrl().getHost() =="192.168.207.88") {
                    result.add(invokers.get(i));
                }
        }
    }else if("write".equals(invocation.getMethodName())){
        //写方法只调用192.168.112.12
        for (var i=0;i<invokers.size(); i++) {    
                if(invokers.get(i).getUrl().getHost() =="192.168.112.12") {
                    result.add(invokers.get(i));
                }
        }
    }
    return result;
}(invokers,invocation));

在这里插入图片描述

可以看到读写分离已经起到了效果
在这里插入图片描述

最后再介绍下脚本路由中的入参说明,还是以上文脚本路由代码为例

(function(invokers,invocation,context){
var methodName = invocation.methodName;
var resultInvokers=[];
for(var i=0;i<invokers.length;i++){
    var invoker = invokers[i];
    var currentConsumerUrl = invoker.url;
    var providerUrl = invoker.providerUrl;
    var consumerHost=currentConsumerUrl.host;
    var providerHost = providerUrl.host;
    var providerProtocol = providerUrl.protocol;
    var currentService = providerUrl.getParameter("interface");
    if(methodName.indexOf('get')>=0&&providerHost=='192.168.0.23'){
        resultInvokers.push(invoker);
    }
}
//该方法返回值必须是一个Invoker的集合或者数组
return resultInvokers;})(invokers,invocation,context);

Dubbo在调用脚本的时候均会调用该函数,Dubbo会提供三个入参

1、invokers:这个是一个InvokerDelegete类的实体集合,该类有三个属性invoker(具备远程通信能力的Invoker子类对象),url(当前消费端的URL对象),providerUrl(当前invoker包装的提供端地址URL对象), 可以简单理解为包含所有提供者ip、端口信息的列表

2、invocation:由于Dubbo的服务调用是以方法为单位,那么每个方法都会匹配一批Invoker集合,该参数则指明了当前方法信息, 不过由于当前并没有进行方法调用,只是服务的订阅,所以此对象只有一个属性可用methodName

3、context:当前线程的服务调用上线文,由于当前服务并不是处于调用状态,所以该参数并没有实际用途

由第二点可知,如果想从invocation中去获取当次调用的入参,那是不可行的,只有方法名methodName可以获取到

发布了43 篇原创文章 · 获赞 134 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/hosaos/article/details/103495881
今日推荐