-
Notifications
You must be signed in to change notification settings - Fork 26.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Dubbo- support tag router feature] Add a new Router implement -- TagRouter #2228
Conversation
Codecov Report
@@ Coverage Diff @@
## master #2228 +/- ##
=========================================
Coverage ? 54.65%
Complexity ? 5172
=========================================
Files ? 571
Lines ? 25084
Branches ? 4464
=========================================
Hits ? 13709
Misses ? 9334
Partials ? 2041
Continue to review full report at Codecov.
|
@lexburner Thanks for your contributing. I think this PR is a big feature, so I suggest discussing it on the dev mail list. BTW, pls. solve the conflicts. |
very nice feature, will look into it. |
import java.util.List; | ||
|
||
/** | ||
* @author [email protected] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove author plz
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lack of ASF license in the head:
/*
* 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.
*/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
got it, i will add licenses for all new files.
// Tag request | ||
if (!StringUtils.isEmpty(tag)) { | ||
// Select tag invokers first | ||
for (Invoker<T> invoker : invokers) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
three for statement can be combined one and check conditions in it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These three for statements have their own logic, depend on different conditions, can you give me more details, thanks a lot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lexburner @kimmking i think three for statement can be combined too,my impl:
public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
// filter
List<Invoker<T>> result = new ArrayList<>();
// all invokers that don't have "tag" parameter in url is a normal invoker
List<Invoker<T>> normalResult = new ArrayList<>();
try {
// Dynamic param
String tag = RpcContext.getContext().getAttachment(REQUEST_TAG_KEY);
for (Invoker<T> invoker : invokers) {
if (StringUtils.isEmpty(invoker.getUrl().getParameter(TAG_KEY))) {
// all invokers that don't have "tag" parameter in url is a normal invoker
normalResult.add(invoker);
} else {
if (invoker.getUrl().getParameter(TAG_KEY).equals(tag)) {
result.add(invoker);
}
}
}
// If no invoker be selected, downgrade to normal invokers
if (result.isEmpty()) {
return normalResult;
}
return result;
} catch (Exception e) {
logger.error("Route by tag error,return all invokers.", e);
}
// Downgrade to all invokers
return invokers;
}
@@ -307,6 +307,12 @@ | |||
<xsd:documentation><![CDATA[ The serialization protocol of service. ]]></xsd:documentation> | |||
</xsd:annotation> | |||
</xsd:attribute> | |||
<xsd:attribute name="tag" type="xsd:string"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pls. also change dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd
private final URL url; | ||
|
||
public static final URL ROUTER_URL = | ||
new URL("tag" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pls. change this to one liner, or complete remove it since I notice TagRouter()
is redundant.
} | ||
// Normal request | ||
} else { | ||
for (Invoker<T> invoker : invokers) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for normal request, I guess we should include all invokers, no matter the service is published with or without tag, for example: what if all services are published with tag, then is it expected that there's no valid service to invoke?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is intentional, if a service published with tag, we assume it as a new feature, and won't normal request or other feature's request hit it. Only the tag request hit the tag service.
In another case, tag request can hit normal service if (and only if) there is no tag service, i call it downgrade.
I know that no valid service to invoke
was sound terrible , but if users want to use TagRouter , i think it is their duty to know what will happen to their projects.
By default, if we do not config like:
Map<String,String> parameters = new HashMap<>();
parameters.put(Constants.ROUTER_KEY, "tag");
applicationConfig.setParameters(parameters);
There will no TagRouter feature exist in our project. Users who don't care about the TagRouter feature are not affected by it.
import org.apache.dubbo.rpc.cluster.RouterFactory; | ||
|
||
/** | ||
* @author [email protected] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again, remove author and add ASF license header.
import java.util.List; | ||
|
||
/** | ||
* @author [email protected] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again, remove author info and add ASF license header.
I think it's good to go, I will merge it. @lexburner pls. add corresponding doc at https://github.com/apache/incubator-dubbo-website, and pls. consider to write a tech blog too. Thx. |
TagRouter feature is very good, but i think an LoadBalance Wrapper can get same result ,and has smaller changes with source code: import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.RpcContext;
import com.alibaba.dubbo.rpc.RpcException;
import com.alibaba.dubbo.rpc.cluster.LoadBalance;
import java.util.ArrayList;
import java.util.List;
public class LoadbalanceTagFilterWrapper implements LoadBalance {
private static final Logger logger = LoggerFactory.getLogger(LoadbalanceTagFilterWrapper.class);
private LoadBalance loadBalance;
public LoadbalanceTagFilterWrapper(LoadBalance loadBalance) {
this.loadBalance = loadBalance;
}
@Override
public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
List<Invoker<T>> filteredInvokers = filterInvokers(invokers);
return loadBalance.select(filteredInvokers, url, invocation);
}
private <T> List<Invoker<T>> filterInvokers(List<Invoker<T>> invokers) {
// filter
List<Invoker<T>> result = new ArrayList<>();
// all invokers that don't have "tag" parameter in url is a normal invoker
List<Invoker<T>> normalResult = new ArrayList<>();
try {
// Dynamic param
String tag = RpcContext.getContext().getAttachment(Constants.REQUEST_TAG_KEY);
for (Invoker<T> invoker : invokers) {
if (StringUtils.isEmpty(invoker.getUrl().getParameter(Constants.TAG_KEY))) {
// all invokers that don't have "tag" parameter in url is a normal invoker
normalResult.add(invoker);
} else {
String[] tags = invoker.getUrl().getParameter(Constants.TAG_KEY).split(",");
for (String s : tags) {
if (s.equals(tag)) {
result.add(invoker);
break;
}
}
}
}
// If no invoker be selected, downgrade to normal invokers
if (result.isEmpty()) {
return normalResult;
}
return result;
} catch (Exception e) {
logger.error("Route by tag error,return all invokers.", e);
}
// Downgrade to all invokers
return invokers;
}
} |
Very interesting , so I should say route and loadBalance maybe a little redundancy of functions; |
How about providing a filter to set tag attachment at same time ? |
What is the purpose of the change
Support tag router feature for dubbo.
some pain points
use tag
some cases
usage
i suggest you can use filter or SPI to set this attachment, notice that RpcContext is bound to Thread.
Brief changelog
org.apache.dubbo.registry.integration.RegistryDirectory#route
,exclude runtime routers, avoid to filter some runtime invokers.Verifying this change
Here is more detail about Unit Test:
org.apache.dubbo.rpc.cluster.router.tag.TagRouterTest
I have verified that the unit test has been correct and passed.
Here is more detail about a Integration Test:
clone the repository
https://github.com/lexburner/Dubbo-Sample-TagRouter.git
,can see the route tag feature.Follow this checklist to help us incorporate your contribution quickly and easily:
[Dubbo-XXX] Fix UnknownException when host config not exist #XXX
. Each commit in the pull request should have a meaningful subject line and body.mvn clean install -DskipTests
&mvn clean test-compile failsafe:integration-test
to make sure unit-test and integration-test pass.