dubbo 路由过滤
官网:https://dubbo.apache.org/zh/docs/advanced/routing-rule/
路由过滤
路由过滤:消费端调用服务时,根据自定义规则过滤服务调用列表
消费端服务调用流程
消费端应用启动时,创建clusterInvoker对象(默认为failoverClusterInvoker)
# 消费端发起服务调用时,依次执行如下过程:
获取所有可用invoker列表(directory#list)
* dynamicDirectory:动态invoker列表,从注册中心获取
* staticDirectory:静态invoker列表,创建staticDirectory对象时传入的invoker列表
对所有可用列表进行路由过滤(routerChain#route)
使用负载均衡算法(loadbalance#select)在路由过滤后的invoker列表选择一个invoker,发起远程服务调用;
# 服务调用结果处理
如果调用正常,消费端收到服务端返回的结果(如果有结果返回);
如果调用异常,执行容错策略:重试(failover,默认)、忽略异常(failsafe)等;
如果设置了mock,服务调用异常时,可进行服务降级;
路由分类
条件路由:使用dubbo定义的语法规则在dubbo-admin中写路由规则;
文件路由:将路由规则写在文件中,读取文件提取路由规则;
脚本路由:使用jdk支持的引擎解析路由脚本,默认使用javascript引擎
标签路由:给服务(provider)打标签,后续如果consumer调用时携带标签,
标签如果匹配则优先调用,如果不匹配则调用不携带标签的provider服务
StateRouteFactory
@SPI
public interface StateRouterFactory {
/**
* Create state router.
*
* @param url url
* @return router instance
* @since 3.0
*/
@Adaptive("protocol")
<T> StateRouter<T> getRouter(Class<T> interfaceClass, URL url);
}
***********
条件路由
应用粒度条件路由
# app1的消费者只能消费所有端口为20880的服务实例
# app2的消费者只能消费所有端口为20881的服务实例
---
scope: application
force: true
runtime: true
enabled: true
key: governance-conditionrouter-consumer
conditions:
- application=app1 => address=*:20880
- application=app2 => address=*:20881
接口粒度条件路由
# 服务粒度示例
# DemoService的sayHello方法只能消费所有端口为20880的服务实例
# DemoService的sayHi方法只能消费所有端口为20881的服务实例
---
scope: service
force: true
runtime: true
enabled: true
key: org.apache.dubbo.samples.governance.api.DemoService
conditions:
- method=sayHello => address=*:20880
- method=sayHi => address=*:20881
参数说明
scope:application(应用粒度)、service(服务粒度)
force:路由结果为空时,是否强制执行,默认false
true:强制执行,路由结果为空,则返回空结果
false:路由结果为空时,返回所有服务列表
runtime:是否在每次调用时执行路由规则,默认false
true:每次调用时执行路由规则,如果使用参数路由,必须设置为true
false:只有在服务者列表更新后执行路由规则,如果没有更新,使用缓存的列表
enabled:是否启用路由规则,默认true
priority:路由规则优先级,值越大越靠前执行,默认为0
key:路由规则作用的应用名(scope为application)、服务名(scope为service)
scope为application时,key:应用名
scope为service时,key:[{group}]{service}[{version}],group、version可缺省
conditions:路由条件规则设置(whenRule => thenRule)
conditions 路由规则:whenRule ==> thenRule
whenRule:消费者匹配条件,消费者满足匹配条件时,执行后面的过滤规则
thenRule:服务提供者匹配条件,返回满足匹配条件的服务者列表
whenRule为空,表示对所有消费者都应用后面的匹配规则(thenRule),如:=> host != 10.20.153.11
thenRule为空,表示禁止访问,如:host = 10.20.153.10 =>
whenRule、thenRule格式:key = value、key != value
key为服务调用信息(method、argument等)暂不支持参数路由、
url本身字段(protocol, host, port 等)、
url上的所有参数,如:application, organization
value可包含特殊字符:*(通配符),如host != 10.20.*
,(分隔符),如host != 10.20.153.10,10.20.153.11
$(引用消费者参数),如host = $host
# 路由规则示例
=> host != 172.22.3.91:所有消费者均不调用172.22.3.91上的服务
register.ip != 10.20.153.10,10.20.153.11 =>:注册地址不在指定地址的服务不得调用任何服务
application != kylin => host != 172.22.3.95,172.22.3.96:应用名不为指定名称的不得调用不为指定host的服务
***********
标签路由
动态标签:dubbo admin中设置
# 动态标签,动态标签优先级更高,同时包含动态标签、静态标签,以动态标签为准
# governance-tagrouter-provider应用增加了两个标签分组tag1和tag2
# tag1包含一个实例 127.0.0.1:20880
# tag2包含一个实例 127.0.0.1:20881
---
force: false
runtime: true
enabled: true
key: governance-tagrouter-provider
tags: #可以给应用(该应用下用多个服务实例)打多个标签
- name: tag1 #name标签名称,addresses表示数组中的实例都打上该标签
addresses: ["127.0.0.1:20880"] #地址为127.0.0.1:20880的实例打标签tag1
- name: tag2
addresses: ["127.0.0.1:20881"] #地址为127.0.0.1:20881的实例打标签tag2
静态标签:服务端注册服务时设置
<dubbo:provider tag="tag1"/>
<dubbo:service tag="tag1"/>
java -jar xxx-provider.jar -Ddubbo.provider.tag={tag1}
消费端标签过滤:调用时在上下文中设置标签
# 消费端调用标签tag1的服务
RpcContext.getContext().setAttachment(Constants.REQUEST_TAG_KEY,"tag1");
<dubbo:reference interface="com.xxx.MenuService" tag="tag1" />
# 消费端标签路由规则
如果消费端携带标签tag,则优先调用含有同一标签的服务提供者;
如果没有同一标签的服务提供者,默认访问tag为null的服务提供者;
设置参数request.tag.force=true,当不存在同一tag的服务提供者,则抛出异常;
消费端如果不携带参数,则只会调用tag=null的服务提供者
***********
文件路由
FileStateRouterFactory
public class FileStateRouterFactory implements StateRouterFactory {
public static final String NAME = "file";
private StateRouterFactory routerFactory;
public void setRouterFactory(StateRouterFactory routerFactory) {
this.routerFactory = routerFactory;
}
@Override
public <T> StateRouter<T> getRouter(Class<T> interfaceClass, URL url) {
try {
// Transform File URL into Script Route URL, and Load
// file:///d:/path/to/route.js?router=script ==> script:///d:/path/to/route.js?type=js&rule=<file-content>
//将文件url转换为scripr路由url,并且加载文件内容附加在url中,如
//file:///d:/path/to/route.js?router=script
//转换为script:///d:/path/to/route.js?type=js&rule=<file-content>
String protocol = url.getParameter(ROUTER_KEY, ScriptStateRouterFactory.NAME); // Replace original protocol (maybe 'file') with 'script'
String type = null; // Use file suffix to config script type, e.g., js, groovy ...
String path = url.getPath();
if (path != null) {
int i = path.lastIndexOf('.');
if (i > 0) {
type = path.substring(i + 1);
}
}
String rule = IOUtils.read(new FileReader(new File(url.getAbsolutePath())));
// FIXME: this code looks useless
boolean runtime = url.getParameter(RUNTIME_KEY, false);
URL script = URLBuilder.from(url)
.setProtocol(protocol)
.addParameter(TYPE_KEY, type)
.addParameter(RUNTIME_KEY, runtime)
.addParameterAndEncoded(RULE_KEY, rule)
.build();
return routerFactory.getRouter(interfaceClass, script);
} catch (IOException e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
}
***********
脚本路由
ScriptStateRouterFactory
/**
* ScriptRouterFactory
* <p>
* Example URLS used by Script Router Factory:
* <ol>
* <li> script://registryAddress?type=js&rule=xxxx //注册中心脚本路由
* <li> script:///path/to/routerfile.js?type=js&rule=xxxx //linux、mac脚本路由
* <li> script://D:\path\to\routerfile.js?type=js&rule=xxxx //windows脚本路由
* <li> script://C:/path/to/routerfile.js?type=js&rule=xxxx
* </ol>
* The host value in URL points out the address of the source content of the Script Router,Registry、File etc
*
*/
public class ScriptStateRouterFactory implements StateRouterFactory {
public static final String NAME = "script";
@Override
public <T> StateRouter<T> getRouter(Class<T> interfaceClass, URL url) {
return new ScriptStateRouter<>(url);
}
}
消费端标签过滤
@DubboReference:消费端服务调用注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
public @interface DubboReference {
/**
* Interface class, default value is void.class
*/
Class<?> interfaceClass() default void.class;
/**
* Interface class name, default value is empty string
*/
String interfaceName() default "";
String group() default ""; //服务分组
String version() default ""; //服务版本
/**
* Service target URL for direct invocation, if this is specified, then registry center takes no effect.
*/
String url() default ""; //直连服务提供端(如果配置了,则不使用注册中心)
/**
* Client transport type, default value is "netty"
*/
String client() default ""; //客户端传输方式,默认为netty
/**
* Whether to enable generic invocation, default value is false
* @deprecated Do not need specify generic value, judge by injection type and interface class
*/
@Deprecated
boolean generic() default false; //已禁用
/**
* When enable, prefer to call local service in the same JVM if it's present, default value is true
* @deprecated using scope="local" or scope="remote" instead
*/
@Deprecated
boolean injvm() default true; //已禁用
/**
* Check if service provider is available during boot up, default value is true
*/
boolean check() default true;
/**
* Whether eager initialize the reference bean when all properties are set, default value is true ( null as true)
* @see ReferenceConfigBase#shouldInit()
*/
boolean init() default true;
/**
* Whether to make connection when the client is created, the default value is false
*/
boolean lazy() default false;
/**
* Export an stub service for event dispatch, default value is false.
* <p>
* see org.apache.dubbo.rpc.Constants#STUB_EVENT_METHODS_KEY
*/
boolean stubevent() default false;
/**
* Whether to reconnect if connection is lost, if not specify, reconnect is enabled by default, and the interval
* for retry connecting is 2000 ms
* <p>
* see org.apache.dubbo.remoting.Constants#DEFAULT_RECONNECT_PERIOD
*/
String reconnect() default "";
/**
* Whether to stick to the same node in the cluster, the default value is false
* <p>
* see Constants#DEFAULT_CLUSTER_STICKY
*/
boolean sticky() default false;
/**
* How the proxy is generated, legal values include: jdk, javassist
*/
String proxy() default "";
/**
* Service stub name, use interface name + Local if not set
*/
String stub() default "";
/**
* Cluster strategy, legal values include: failover, failfast, failsafe, failback, forking
* you can use {@link org.apache.dubbo.common.constants.ClusterRules#FAIL_FAST} ……
*/
String cluster() default ClusterRules.EMPTY;
/**
* Maximum connections service provider can accept, default value is 0 - connection is shared
*/
int connections() default -1;
/**
* The callback instance limit peer connection
* <p>
* see org.apache.dubbo.rpc.Constants#DEFAULT_CALLBACK_INSTANCES
*/
int callbacks() default -1;
/**
* Callback method name when connected, default value is empty string
*/
String onconnect() default "";
/**
* Callback method name when disconnected, default value is empty string
*/
String ondisconnect() default "";
/**
* Service owner, default value is empty string
*/
String owner() default "";
/**
* Service layer, default value is empty string
*/
String layer() default "";
/**
* Service invocation retry times
* <p>
* see Constants#DEFAULT_RETRIES
*/
int retries() default -1;
/**
* Load balance strategy, legal values include: random, roundrobin, leastactive
* you can use {@link org.apache.dubbo.common.constants.LoadbalanceRules#RANDOM} ……
*/
String loadbalance() default LoadbalanceRules.EMPTY;
/**
* Whether to enable async invocation, default value is false
*/
boolean async() default false;
/**
* Maximum active requests allowed, default value is 0
*/
int actives() default -1;
/**
* Whether the async request has already been sent, the default value is false
*/
boolean sent() default false;
/**
* Service mock name, use interface name + Mock if not set
*/
String mock() default "";
/**
* Whether to use JSR303 validation, legal values are: true, false
*/
String validation() default "";
/**
* Timeout value for service invocation, default value is 0
*/
int timeout() default -1;
/**
* Specify cache implementation for service invocation, legal values include: lru, threadlocal, jcache
*/
String cache() default "";
/**
* Filters for service invocation
* <p>
* see Filter
*/
String[] filter() default {};
/**
* Listeners for service exporting and unexporting
* <p>
* see ExporterListener
*/
String[] listener() default {};
/**
* Customized parameter key-value pair, for example: {key1, value1, key2, value2} or {"key1=value1", "key2=value2"}
*/
String[] parameters() default {};
/**
* Application name
* @deprecated This attribute was deprecated, use bind application/module of spring ApplicationContext
*/
@Deprecated
String application() default "";
/**
* Module associated name
*/
String module() default "";
/**
* Consumer associated name
*/
String consumer() default "";
/**
* Monitor associated name
*/
String monitor() default "";
/**
* Registry associated name
*/
String[] registry() default {};
/**
* The communication protocol of Dubbo Service
*
* @return the default value is ""
* @since 2.6.6
*/
String protocol() default "";
/**
* Service tag name
*/
String tag() default ""; //标签设置
/**
* Service merger
*/
String merger() default "";
/**
* methods support
*/
Method[] methods() default {};
/**
* The id
* NOTE: The id attribute is ignored when using @DubboReference on @Bean method
* @return default value is empty
* @since 2.7.3
*/
String id() default "";
/**
* @return The service names that the Dubbo interface subscribed
* @see RegistryConstants#SUBSCRIBED_SERVICE_NAMES_KEY
* @since 2.7.8
* @deprecated using {@link DubboReference#providedBy()}
*/
@Deprecated
String[] services() default {};
/**
* declares which app or service this interface belongs to
* @see RegistryConstants#PROVIDED_BY
*/
String[] providedBy() default {};
/**
* the scope for referring/exporting a service, if it's local, it means searching in current JVM only.
* @see org.apache.dubbo.rpc.Constants#SCOPE_LOCAL
* @see org.apache.dubbo.rpc.Constants#SCOPE_REMOTE
*/
String scope() default "";
/**
* Weather the reference is refer asynchronously
*/
boolean referAsync() default false;
}
HelloController
@RestController
public class HelloController {
@DubboReference(tag = "tag1") //调用含有标签tag1的服务
private HelloService helloService;
@RequestMapping("/hello")
public String hello(){
helloService.hello().forEach(System.out::print);
return "hello consumer 2";
}
}