prev:远程过程调用RPC 4:RPC服务框架Dubbo
Dubbo路由概述
dubbo2.7官方文档-旧路由规则 Dubbo在服务治理方面提供了路由功能,主要是决定一次Dubbo服务调用时调用的目标服务器。 dubbo主要支持三种路由方式:
条件路由
条件路由是基于条件表达式的路由规则。大致语法是以=>符号进行分隔:
- => 之前的为消费者匹配条件,所有参数和消费者的 URL 进行对比,当消费者满足匹配条件时,对该消费者执行后面的过滤规则
- => 之后为提供者地址列表的过滤条件,所有参数和提供者的 URL 进行对比,消费者最终只拿到过滤后的地址列表
- 符号前后可以为空,表示对提供方/消费方进行不加限制/禁止访问
举个简单的例子:
host = 10.20.153.10 => host = 10.20.153.11
host = 10.20.153.10 => host != 10.20.153.10,10.20.153.11
=> 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:registry address="zookeeper://127.0.0.1:2181" />
<dubbo:protocol name="dubbo" port="20881"/>
<dubbo:service
interface="com.mxb.provider.service.ProviderService"
ref="providerService"
/>
<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 路由规则之标签路由
|