dubbo-admin同时对多个ip进行黑名单访问控制无效
在dubbo-admin中对一个服务可以按ip进行访问控制,测试中发现只禁用一个ip才有效果,但是同时禁用多个ip则会整个禁用控制失效,经跟踪代码发现com.alibaba.dubbo.rpc.cluster.router.condition.ConditionRouter.java里的
private static final class MatchPair { final Set<String> matches = new HashSet<String>(); final Set<String> mismatches = new HashSet<String>(); public boolean isMatch(String value, URL param) { for (String match : matches) { if (! UrlUtils.isMatchGlobPattern(match, value, param)) { return false; } } for (String mismatch : mismatches) { if (UrlUtils.isMatchGlobPattern(mismatch, value, param)) { return false; } } return true; } }
ismacth方法,当有多个不同的ip禁用时,如上代码所示,只要有一个match值与value不一致,就会返回false,从而使需要禁用的ip也能匹配失败。
路由的时候遇到相同的问题,后面换用IP段来路由。
确实,com.alibaba.dubbo.rpc.cluster.router.condition.ConditionRouter.MatchPair#isMatch 这个方法判断是否匹配有问题
通过自定RouterFactory拓展点可以解决此问题,如下是本人的解决方案:
- 定义如下类MyConditionRouterFactory
public class MyConditionRouterFactory implements RouterFactory {
public static final String NAME = "myCondition";
public Router getRouter(URL url) {
return new MyConditionRouter(url);
}
}
- 增加类MyConditionRouter,基于源码com.alibaba.dubbo.rpc.cluster.router.condition.ConditionRouter,并修改如下关键方法
...
private static final class MatchPair {
final Set<String> matches = new HashSet<String>();
final Set<String> mismatches = new HashSet<String>();
public boolean isMatch(String value, URL param) {
for (String mismatch : mismatches) {
if (UrlUtils.isMatchGlobPattern(mismatch, value, param)) {
return false;
}
}
if (matches.isEmpty()) return true;
//这里只要任意个匹配,直接返回,形如 application=myConsumer => host = 127.0.0.1,127.0.0.2
for (String match : matches) {
if (UrlUtils.isMatchGlobPattern(match, value, param)) {
return true;
}
}
return false;
}
}
...
-
在 META-INF/dubbo增加拓展点配置文件com.alibaba.dubbo.rpc.cluster.RouterFactory,内容:
myCondition=xxx.MyConditionRouterFactory -
使用上述自定义路由规则,举例:
RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://xxx:2181"));
//注意这里改为myCondition(协议?^_^)
final URL url = URL.valueOf("myCondition://0.0.0.0/xxx.DemoService?category=routers&force=true&enabled=true&dynamic=false&rule=" +
URL.encode("application = myConsumer => host = 127.0.0.1,127.0.0.2"));
registry.register(url);
以上
方案二:基于script的路由,思路与myCondition路由大同小异,这是在不改任何源代码的方式来实现(dubbo的script路由很灵活),定义如下:
RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://xxx:2181"));
String script = "(function route(invokers,invocation){var routeApp='myConsumer';var routeHosts='127.0.0.1,127.0.0.2'.split(',');var result=new java.util.ArrayList();for(var i=0;i<invokers.size();i++){var url=invokers.get(i).getUrl();if(url.getParameter('application')!==routeApp){result.add(invokers.get(i))}else if(routeHosts.indexOf(url.getHost())>=0){result.add(invokers.get(i))}}return result}(invokers,invocation));";
URL url = URL.valueOf("script://0.0.0.0/xxx.DemoService?category=routers&force=true&enabled=true&dynamic=false&type=javascript&rule=" + URL.encode(script));
registry.register(url);
ps 完整的script如下:
(function route(invokers, invocation) {
var routeApp = 'myConsumer';
var routeHosts = '127.0.0.1,127.0.0.2'.split(',');
var result = new java.util.ArrayList();
for (var i = 0; i < invokers.size(); i++) {
var url = invokers.get(i).getUrl();
if (url.getParameter('application') !== routeApp) {
result.add(invokers.get(i));
} else if (routeHosts.indexOf(url.getHost()) >= 0) {
result.add(invokers.get(i));
}
}
return result;
}(invokers, invocation));
方案二:基于script的路由,思路与myCondition路由大同小异,这是在不改任何源代码的方式来实现(dubbo的script路由很灵活),定义如下:
RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension(); Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://xxx:2181")); String script = "(function route(invokers,invocation){var routeApp='myConsumer';var routeHosts='127.0.0.1,127.0.0.2'.split(',');var result=new java.util.ArrayList();for(var i=0;i<invokers.size();i++){var url=invokers.get(i).getUrl();if(url.getParameter('application')!==routeApp){result.add(invokers.get(i))}else if(routeHosts.indexOf(url.getHost())>=0){result.add(invokers.get(i))}}return result}(invokers,invocation));"; URL url = URL.valueOf("script://0.0.0.0/xxx.DemoService?category=routers&force=true&enabled=true&dynamic=false&type=javascript&rule=" + URL.encode(script)); registry.register(url);ps 完整的script如下:
(function route(invokers, invocation) { var routeApp = 'myConsumer'; var routeHosts = '127.0.0.1,127.0.0.2'.split(','); var result = new java.util.ArrayList(); for (var i = 0; i < invokers.size(); i++) { var url = invokers.get(i).getUrl(); if (url.getParameter('application') !== routeApp) { result.add(invokers.get(i)); } else if (routeHosts.indexOf(url.getHost()) >= 0) { result.add(invokers.get(i)); } } return result; }(invokers, invocation));
相当于是推了个js脚本到zookeeper中下发,从ip规则转为脚本规则。 感觉还是升级大法好,哈哈