Routers zookeeper path (#3173)

* give router rule a more layered extensiable ZK path:
* ../routers/condition
* ../routers/tag
* ../routers/otherRouter

* Fix UT

* Adjust ZK to handle layered routers path

* Remove BlackWhiteListRule

* Adjust code to avoid possible ConcurrentModificationException

* revert router from layered path to flat path:
/router/condition -> router-codition

* adjust router path in UT

* change suffix literal: router-condition to condition-router

* change suffix literal: router-condition to condition-router
This commit is contained in:
ken.lj 2019-01-09 15:15:58 +08:00 committed by Ian Luo
parent 21e0227a44
commit c679d0b9d4
11 changed files with 37 additions and 94 deletions

View File

@ -21,7 +21,7 @@ import org.apache.dubbo.common.URL;
import org.apache.dubbo.configcenter.DynamicConfiguration;
/**
* Application level router, "application.routers"
* Application level router, "application.condition-router"
*/
public class AppRouter extends ListenableRouter {
public static final String NAME = "APP_ROUTER";

View File

@ -16,7 +16,6 @@
*/
package org.apache.dubbo.rpc.cluster.router.condition.config;
import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
@ -32,22 +31,23 @@ import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.cluster.Router;
import org.apache.dubbo.rpc.cluster.router.AbstractRouter;
import org.apache.dubbo.rpc.cluster.router.condition.ConditionRouter;
import org.apache.dubbo.rpc.cluster.router.condition.config.model.BlackWhiteListRule;
import org.apache.dubbo.rpc.cluster.router.condition.config.model.ConditionRouterRule;
import org.apache.dubbo.rpc.cluster.router.condition.config.model.ConditionRuleParser;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* Abstract router which listens to dynamic configuration
*/
public abstract class ListenableRouter extends AbstractRouter implements ConfigurationListener {
public static final String NAME = "LISTENABLE_ROUTER";
private static final String RULE_SUFFIX = ".condition-router";
public static final int DEFAULT_PRIORITY = 200;
private static final Logger logger = LoggerFactory.getLogger(ListenableRouter.class);
private ConditionRouterRule routerRule;
private List<ConditionRouter> conditionRouters = new ArrayList<>();
private List<ConditionRouter> conditionRouters = Collections.emptyList();
public ListenableRouter(DynamicConfiguration configuration, URL url, String ruleKey) {
super(configuration, url);
@ -64,11 +64,11 @@ public abstract class ListenableRouter extends AbstractRouter implements Configu
if (event.getChangeType().equals(ConfigChangeType.DELETED)) {
routerRule = null;
conditionRouters.clear();
conditionRouters = Collections.emptyList();
} else {
try {
routerRule = ConditionRuleParser.parse(event.getValue());
generateConditions(routerRule, conditionRouters);
generateConditions(routerRule);
} catch (Exception e) {
logger.error("Failed to parse the raw condition rule and it will not take effect, please check " +
"if the condition rule matches with the template, the raw rule is:\n " + event.getValue(), e);
@ -104,23 +104,12 @@ public abstract class ListenableRouter extends AbstractRouter implements Configu
return routerRule != null && routerRule.isValid() && routerRule.isRuntime();
}
private void generateConditions(ConditionRouterRule rule, List<ConditionRouter> routers) {
private void generateConditions(ConditionRouterRule rule) {
if (rule != null && rule.isValid()) {
routers.clear();
rule.getConditions().forEach(condition -> {
// All sub rules have the same force, runtime value.
ConditionRouter subRouter = new ConditionRouter(condition, rule.isForce(), rule.isEnabled());
routers.add(subRouter);
});
BlackWhiteListRule blackWhiteList = rule.getBlackWhiteList();
if (blackWhiteList != null && blackWhiteList.isValid()) {
blackWhiteList.getConditions().forEach(condition -> {
// All sub rules have the same force, runtime value.
ConditionRouter subRouter = new ConditionRouter(condition, true, blackWhiteList.isEnabled());
routers.add(subRouter);
});
}
this.conditionRouters = rule.getConditions()
.stream()
.map(condition -> new ConditionRouter(condition, rule.isForce(), rule.isEnabled()))
.collect(Collectors.toList());
}
}
@ -128,7 +117,7 @@ public abstract class ListenableRouter extends AbstractRouter implements Configu
if (StringUtils.isEmpty(ruleKey)) {
return;
}
String routerKey = ruleKey + Constants.ROUTERS_SUFFIX;
String routerKey = ruleKey + RULE_SUFFIX;
configuration.addListener(routerKey, this);
String rule = configuration.getConfig(routerKey);
if (rule != null) {

View File

@ -20,7 +20,7 @@ import org.apache.dubbo.common.URL;
import org.apache.dubbo.configcenter.DynamicConfiguration;
/**
* Service level router, "server-uniq-name.routers"
* Service level router, "server-unique-name.condition-router"
*/
public class ServiceRouter extends ListenableRouter {
public static final String NAME = "SERVICE_ROUTER";

View File

@ -1,36 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.rpc.cluster.router.condition.config.model;
import org.apache.dubbo.rpc.cluster.router.AbstractRouterRule;
import java.util.List;
/**
*
*/
public class BlackWhiteListRule extends AbstractRouterRule {
private List<String> conditions;
public List<String> getConditions() {
return conditions;
}
public void setConditions(List<String> conditions) {
this.conditions = conditions;
}
}

View File

@ -27,7 +27,6 @@ public class ConditionRouterRule extends AbstractRouterRule {
public ConditionRouterRule() {
}
private BlackWhiteListRule blackWhiteList;
private List<String> conditions;
public List<String> getConditions() {
@ -37,12 +36,4 @@ public class ConditionRouterRule extends AbstractRouterRule {
public void setConditions(List<String> conditions) {
this.conditions = conditions;
}
public BlackWhiteListRule getBlackWhiteList() {
return blackWhiteList;
}
public void setBlackWhiteList(BlackWhiteListRule blackWhiteList) {
this.blackWhiteList = blackWhiteList;
}
}

View File

@ -47,10 +47,6 @@ public class ConditionRuleParser {
rule.setValid(false);
}
BlackWhiteListRule blackWhiteList = rule.getBlackWhiteList();
if (blackWhiteList != null && CollectionUtils.isEmpty(blackWhiteList.getConditions())) {
blackWhiteList.setValid(false);
}
return rule;
}

View File

@ -42,13 +42,13 @@ import static org.apache.dubbo.common.Constants.FORCE_USE_TAG;
import static org.apache.dubbo.common.Constants.TAG_KEY;
/**
* TagRouter
* TagRouter, "application.tag-router"
*/
public class TagRouter extends AbstractRouter implements Comparable<Router>, ConfigurationListener {
public static final String NAME = "TAG_ROUTER";
private static final int DEFAULT_PRIORITY = 100;
private static final Logger logger = LoggerFactory.getLogger(TagRouter.class);
private static final String RULE_PREFIX = ".tagrouters";
private static final String RULE_SUFFIX = ".tag-router";
private TagRouterRule tagRouterRule;
private String application;
@ -198,9 +198,9 @@ public class TagRouter extends AbstractRouter implements Comparable<Router>, Con
synchronized (this) {
if (!providerApplication.equals(application)) {
if (!StringUtils.isEmpty(application)) {
configuration.removeListener(application + RULE_PREFIX, this);
configuration.removeListener(application + RULE_SUFFIX, this);
}
String key = providerApplication + RULE_PREFIX;
String key = providerApplication + RULE_SUFFIX;
configuration.addListener(key, this);
application = providerApplication;
String rawRule = configuration.getConfig(key);

View File

@ -20,11 +20,10 @@ import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
/**
* FIXME This is not a formal UT
*/
@Ignore("FIXME This is not a formal UT")
public class ConfigConditionRouterTest {
private static CuratorFramework client;
@ -48,7 +47,7 @@ public class ConfigConditionRouterTest {
" - method=notExitMethod => \n" +
"...";
try {
String servicePath = "/dubbo/config/demo-consumer/routers";
String servicePath = "/dubbo/config/demo-consumer/condition-router";
if (client.checkExists().forPath(servicePath) == null) {
client.create().creatingParentsIfNeeded().forPath(servicePath);
}
@ -72,7 +71,7 @@ public class ConfigConditionRouterTest {
" - method=routeMethod1 => host=30.5.120.37\n" +
"...";
try {
String servicePath = "/dubbo/config/demo-consumer/routers";
String servicePath = "/dubbo/config/demo-consumer/condition-router";
if (client.checkExists().forPath(servicePath) == null) {
client.create().creatingParentsIfNeeded().forPath(servicePath);
}
@ -97,7 +96,7 @@ public class ConfigConditionRouterTest {
"...";
// String serviceStr = "";
try {
String servicePath = "/dubbo/config/org.apache.dubbo.demo.DemoService/routers";
String servicePath = "/dubbo/config/org.apache.dubbo.demo.DemoService/condition-router";
if (client.checkExists().forPath(servicePath) == null) {
client.create().creatingParentsIfNeeded().forPath(servicePath);
}
@ -118,7 +117,7 @@ public class ConfigConditionRouterTest {
"key: org.apache.dubbo.demo.DemoService\n" +
"...";
try {
String servicePath = "/dubbo/config/org.apache.dubbo.demo.DemoService/routers";
String servicePath = "/dubbo/config/org.apache.dubbo.demo.DemoService/condition-router";
if (client.checkExists().forPath(servicePath) == null) {
client.create().creatingParentsIfNeeded().forPath(servicePath);
}

View File

@ -20,11 +20,10 @@ import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
/**
* FIXME This is not a formal UT
*/
@Ignore("FIXME This is not a formal UT")
public class TagRouterTest {
private static CuratorFramework client;
@ -51,7 +50,7 @@ public class TagRouterTest {
"...";
// String serviceStr = "";
try {
String servicePath = "/dubbo/config/demo-provider/tagrouters";
String servicePath = "/dubbo/config/demo-provider/tag-router";
if (client.checkExists().forPath(servicePath) == null) {
client.create().creatingParentsIfNeeded().forPath(servicePath);
}

View File

@ -65,7 +65,7 @@ public class CacheListener implements TreeCacheListener {
// TODO We limit the notification of config changes to a specific path level, for example
// /dubbo/config/service/configurators, other config changes not in this level will not get notified,
// say /dubbo/config/dubbo.properties
if (data.getPath().split("/").length == 5) {
if (data.getPath().split("/").length >= 5) {
byte[] value = data.getData();
String key = pathToKey(data.getPath());
ConfigChangeType changeType;

View File

@ -122,13 +122,18 @@ public class ZookeeperDynamicConfiguration implements DynamicConfiguration {
@Override
public String getConfig(String key, String group, long timeout) throws IllegalStateException {
// when group is not null, we are getting startup configs from Config Center
// for example, group=dubbo, key=dubbo.properties
/**
* when group is not null, we are getting startup configs from Config Center, for example:
* group=dubbo, key=dubbo.properties
*/
if (StringUtils.isNotEmpty(group)) {
key = group + "/" + key;
}
// when group is null, we are fetching governance rules.
// for example, key=org.apache.dubbo.DemoService.configurators
/**
* when group is null, we are fetching governance rules, for example:
* 1. key=org.apache.dubbo.DemoService.configurators
* 2. key = org.apache.dubbo.DemoService.condition-router
*/
else {
int i = key.lastIndexOf(".");
key = key.substring(0, i) + "/" + key.substring(i + 1);