远程过程调用RPC 5:Dubbo路由

prev:远程过程调用RPC 4:RPC服务框架Dubbo

Dubbo路由概述

dubbo2.7官方文档-旧路由规则
Dubbo在服务治理方面提供了路由功能,主要是决定一次Dubbo服务调用时调用的目标服务器。
dubbo主要支持三种路由方式:

  • 条件路由
  • 脚本路由
  • 标签路由
  • 扩展SPI实现自定义路由

条件路由

条件路由是基于条件表达式的路由规则。大致语法是以=>符号进行分隔:

  • => 之前的为消费者匹配条件,所有参数和消费者的 URL 进行对比,当消费者满足匹配条件时,对该消费者执行后面的过滤规则
  • => 之后为提供者地址列表的过滤条件,所有参数和提供者的 URL 进行对比,消费者最终只拿到过滤后的地址列表
  • 符号前后可以为空,表示对提供方/消费方进行不加限制/禁止访问

举个简单的例子:

# 表示允许IP为10.20.153.10访问IP为10.20.153.11的服务
host = 10.20.153.10 => host = 10.20.153.11
#表示允许IP为10.20.153.10访问IP为10.20.153.10、10.20.153.11之外的服务
host = 10.20.153.10 => host != 10.20.153.10,10.20.153.11
# 表示任何访问不得访问IP为172.22网段的服务
=> host != 172.22.*

此种路由方式支持对method、argument、protocol、host、port、application、organization上的参数进行区分

脚本路由

脚本路由无法在dubbo控制台上进行配置,脚本路由规则支持JDK脚本引擎的所有脚本,比如:javascript, jruby, groovy 等,通过type=javascript参数设置脚本类型,缺省为 javascript。

demo

本例中,涉及两个provider对外提供rpc服务,使用consumer对服务进行消费,使用的例子是之前远程过程调用RPC 4:RPC服务框架Dubbo的进一步深入。

结构

在这里插入图片描述
主要涉及两个provider和一个consumer。分别是三个Spring Boot的模块。

provider

provider的提供的接口是ProviderService,定义在com.mxb.provider.service下:

public interface ProviderService {
    String sayHello(String word);
}

其实现功能为:返回一个字符串,内容为hello + 参数的word。实现在com.mxb.provider.service下:

public class ProviderServiceImpl implements ProviderService{
    @Override
    public String sayHello(String word) {
        return "hello" + word;
    }
}

provider的配置文件定义在resource下的META-INF/spring文件夹下,内容为:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd        http://code.alibabatech.com/schema/dubbo        http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    <!--唯一名称,计算依赖关系的标签-->
    <dubbo:application name="provider" owner="mxb"></dubbo:application>
    <dubbo:monitor protocol="registry"/>
    <!--dubbo服务所要暴露的服务地址所对应的注册中心-->
    <dubbo:registry address="zookeeper://127.0.0.1:2181" />
    <!--当前服务发布所依赖的协议;webserovice、Thrift、Hessain、http-->
    <dubbo:protocol name="dubbo" port="20881"/>
    <!--服务发布的配置,需要暴露的服务接口-->
    <dubbo:service
            interface="com.mxb.provider.service.ProviderService"
            ref="providerService"
    />
    <!--Bean bean定义-->
    <bean id="providerService" class="com.mxb.provider.service.ProviderServiceImpl"/>
</beans>

可以看出,注册在了我本地2181端口上的zookeeper上,provider01的端口为20881,服务名为provider。
同样的,provider02的实现类似,不过返回的字符串为灰度测试hello+参数name,模拟提供灰度服务:

public class ProviderServiceImpl implements ProviderService{
    @Override
    public String sayHello(String word) {
        return "灰度测试    hello" + word;
    }
}

provider01的端口为20882,服务名为provider
启动类为:

@SpringBootApplication
public class ProviderApplication {
    public static void main(String[] args) throws IOException {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("META-INF/spring/myprovider.xml");
        context.start();
        System.in.read();
    }
}

拉起来两个服务后,dubbo控制台可以看到:
在这里插入图片描述

consumer

在这里插入图片描述
在消费端,消费这两个服务:

public class ConsumerApplication {

    public static void main(String[] args) throws IOException, InterruptedException {
        ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("consumer.xml");
        context.start();
        ProviderService providerService = (ProviderService) context.getBean("providerService");
        while (true)
        {
            String str = providerService.sayHello(" mxb");
            System.out.println(str);
            str = providerService.sayHello(" sgj");
            System.out.println(str);
            Thread.sleep(1500);
        }
    }
}

consumer.xml定义如下:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd        http://code.alibabatech.com/schema/dubbo        http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    <dubbo:application name="consumer" owner="mxb"/>
    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
    <dubbo:monitor protocol="registry"/>
    <dubbo:reference id="providerService"
                     interface="com.mxb.provider.service.ProviderService"/>
</beans>

在运行时,效果为随机执行两个服务的逻辑:
在这里插入图片描述

引入路由脚本

为了简单起见,这里我们直接向zookeeper上写入router数据,zookeeper上路由配置在/dubbo/com.mxb.provider.service.ProviderService/routers里面,从客户端可以看出目前无路由策略:
在这里插入图片描述
直接将脚本注册到zookeeper上:

import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.registry.Registry;
import com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry;
import com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistryFactory;
import com.alibaba.dubbo.remoting.zookeeper.curator.CuratorZookeeperTransporter;

public class ZkConfig {
    public static void main(String[] args) {
        URL registryUrl = URL.valueOf("zookeeper://127.0.0.1:2181");
        ZookeeperRegistryFactory zookeeperRegistryFactory = new
                ZookeeperRegistryFactory();
        zookeeperRegistryFactory.setZookeeperTransporter(new
                CuratorZookeeperTransporter());
        Registry zookeeperRegistry = (ZookeeperRegistry)
                zookeeperRegistryFactory.createRegistry(registryUrl);
        URL routerURL =
                URL.valueOf("script://0.0.0.0/com.mxb.provider.service.ProviderService?category=routers&dynamic=false&enabled=true&force=false&priority=0");
        routerURL = routerURL.addParameter("rule",URL.encode("(function route(invokers) {\n" +
                "    var result = new java.util.ArrayList(invokers.size());\n" +
                "    for (i = 0; i < invokers.size(); i ++) {\n" +
                "        if (\"30.22.97.17:20882\".equals(invokers.get(i).getUrl().getAddress())) {\n" +
                "            result.add(invokers.get(i));\n" +
                "        }\n" +
                "    }\n" +
                "    return result;\n" +
                "} (invokers));  ") );
        zookeeperRegistry.register(routerURL);
    }
}

这里的脚本如下:

(function route(invokers) {
    var result = new java.util.ArrayList(invokers.size());
    for (i = 0; i < invokers.size(); i ++) {
        if ("30.22.97.17:20882".equals(invokers.get(i).getUrl().getAddress())) {
            result.add(invokers.get(i));
        }
    }
    return result;
} (invokers));

这里的invokers是全部的服务,这里脚本的意思是将address为30.22.96.238:20882的加入list并返回,表述只能访问30.22.96.238:20882的服务。
执行后,可以看到zookeeper的/dubbo/com.mxb.provider.service.ProviderService/routers下已经有了信息:
在这里插入图片描述
那么在使用消费者的时候,只能访问端口20882下灰度的服务:
在这里插入图片描述
那么,能否实现根据参数的内容进行路由呢,采用下面的脚本进行测试:参考Dubbo服务治理之灰度发布方案(版本发布控制影响范围)

(function route(invokers, invocation) {
    var serviceInvokers = new java.util.ArrayList(invokers.size());
    var grayInvokers = new java.util.ArrayList(invokers.size());
    for (i=0;i<invokers.size(); i++) {  
        if ("30.22.97.17:20881".equals(invokers.get(i).getUrl().getAddress())) {
            serviceInvokers.add(invokers.get(i));
        }else{
            grayInvokers.add(invokers.get(i));
        }
    }
    var invArguments = invocation.getArguments();
   // 进入了此分支
    if(invArguments == null || invArguments.length == 0) {
        return serviceInvokers;
    }
    var name = invArguments[0];
    if(name == "mxb") {
        return grayInvokers;
    }else{
        return serviceInvokers;
    }
}(invokers, invocation) );

这里的invocation参数的invocation.getArguments()是获取接口的参数,详见:源码分析。
但是在注释那块就进入了分支,也就是说并没有获得接口参数。尝试失败

标签路由

标签路由引入自dubbo2.7:官方文档-标签路由
运行下面demo之前记得清楚zookeeper上的router规则:

deleteall /dubbo/com.mxb.provider.service.ProviderService/routers

动态打标签

来自官方文档的例子:

force: false
  runtime: true
  enabled: true
  key: governance-tagrouter-provider
  tags:
    - name: tag1
      addresses: ["127.0.0.1:20880"]
    - name: tag2
      addresses: ["127.0.0.1:20881"]

这样就给127.0.0.1:20880上的governance-tagrouter-provider服务打上了tag1标签。

静态打标签

修改配置文件,在本项目中,在provider02的配置文件中加入:

    <dubbo:provider tag="grey"/>

给provider02打上了grey标签
在consumer调用的时候:

public class ConsumerApplication {
    public static void main(String[] args) throws IOException, InterruptedException {
        ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("consumer.xml");
        context.start();
        ProviderService providerService = (ProviderService) context.getBean("providerService");
        while (true)
        {
            RpcContext.getContext().setAttachment(Constants.TAG_KEY,"grey");
            String str = providerService.sayHello(" mxb");
            System.out.println(str);
            str = providerService.sayHello(" sgj");
            System.out.println(str);
            Thread.sleep(1500);
        }
    }
}

也就是在每次调用之前,加入Tag grey,因为这个标签仅一次有效,效果为应该是mxb的为灰度测试,sgj的为普通效果,如下:
在这里插入图片描述
可以看出tag发挥了作用

扩展SPI自定义路由

支持扩展SPI自定义路由规则:官方文档-路由扩展
demo没跑通,在调试。

参考

官方文档-路由扩展
官方文档-路由规则
Dubbo自定义路由
Dubbo3.0如何实现进行路由扩展
深度解析dubbo集群路由之脚本路由
Dubbo-Router条件路由、脚本路由使用
dubbo设置自定义路由
[Dubbo] 源码解析之服务路由策略
Dubbo服务治理之灰度发布方案(版本发布控制影响范围)
Curator基本操作
Dubbo 路由规则之标签路由

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值