环境
System: macOS Mojave
0.2.0-SNAPSHOTDubbo Version: 2.7.7
Dubbo Admin: 0.2.0-SNAPSHOT
注意: 不建议再生产环境中使用,问题很多,而且此版本本来就是develop,所以仅限学习研究使用
分析黑白名单功能
在Dubbo Admin 服务治理菜单下有个黑白名单,其功能是在应用级别,服务级别针对IP设置访问限制,
注意: 创建黑白名单的时候,可以设置应用级别,也可以设置服务级别,但是不能同时设置,此限制是在web端校验,服务端没有加校验,比较危险,而且2.6不支持应用级别
创建黑白名单
image.png
前端代码
if (this.modal.service && this.modal.application) {
this.$notify.error('You can not set both service ID and application name')
return
}
后台代码
if (StringUtils.isBlank(accessDTO.getService()) && StringUtils.isBlank(accessDTO.getApplication())) {
throw new ParamValidationException("Either Service or application is required.");
}
String application = accessDTO.getApplication();
## 如果2.6几倍设置application级别,直接抛出异常
if (StringUtils.isNotEmpty(application) && "2.6".equals(providerService.findVersionInApplication(application))) {
throw new VersionValidationException("dubbo 2.6 does not support application scope blackwhite list config");
}
if (accessDTO.getBlacklist() == null && accessDTO.getWhitelist() == null) {
throw new ParamValidationException("One of Blacklist/Whitelist is required.");
}
routeService.createAccess(accessDTO);
@Component
public class RouteServiceImpl extends AbstractService implements RouteService {
...
...
@Override
public void createAccess(AccessDTO accessDTO) {
String id = ConvertUtil.getIdFromDTO(accessDTO);
String path = getPath(id, Constants.CONDITION_ROUTE);
String config = dynamicConfiguration.getConfig(path);
List<String> blackWhiteList = RouteUtils.convertToBlackWhiteList(accessDTO);
RoutingRule ruleDTO;
if (config == null) {
ruleDTO = new RoutingRule();
ruleDTO.setEnabled(true);
if (StringUtils.isNoneEmpty(accessDTO.getApplication())) {
ruleDTO.setScope(Constants.APPLICATION);
} else {
ruleDTO.setScope(Constants.SERVICE);
}
ruleDTO.setKey(id);
ruleDTO.setConditions(blackWhiteList);
} else {
ruleDTO = YamlParser.loadObject(config, RoutingRule.class);
if (ruleDTO.getConditions() == null) {
ruleDTO.setConditions(blackWhiteList);
} else {
ruleDTO.getConditions().addAll(blackWhiteList);
}
}
dynamicConfiguration.setConfig(path, YamlParser.dumpObject(ruleDTO));
//for 2.6
if (ruleDTO.getScope().equals("service")) {
Route route = RouteUtils.convertAccessDTOtoRoute(accessDTO);
registry.register(route.toUrl());
}
}
}
创建成功
image.png
image.png
黑白名单功能分析
注意: Dubbo Admin的黑白名单功能有问题,无论设置Service还是Application都没啥鸟用,Service级别有用本质是因为走的是条件路由
- 在服务端设置
应用级别黑白名单是针对Provider服务设置的,但是consumer调用provider的时候,拦截的却是comsumer的application -
service无效是因为在Dubbo Admin中添加的服务黑白名单是有group的,但是ServiceRouter却没有考虑group. - ServiceRouter和AppRouter本质上还是构建
ConditionRouter,所以走得逻辑还是条件路由逻辑
⚠️ AppRouter: key是/dubbo/config/dubbo/consumer-of-hello-world.condition-router
image.png
⚠️ ServiceRouter key是/dubbo/config/dubbo/com.leimo.demo.DemoService:1.0:test.condition-router ,没有把group考虑进去
⚠️上面已经说了,无论App 路由还是Service路由,本质上走得还是条件路由**,所以主要分析下条件路由源码逻辑
ConditionRouter对象创建
image.png
route://0.0.0.0/com.leimo.demo.DemoService?category=routers&dynamic=false&enabled=true&force=true&group=test&name=null&priority=0&router=condition&rule=host+%3D+4.3.2.1+%26+host+%21%3D+1.2.3.4+%3D%3E+false&runtime=false&version=1.0创建的条件路由都在
dubbo/对应的服务/routers下,根据url参数router=condition决定 SPI动态加载ConditionRouterFactory创建ConditionRouter
public class ConditionRouterFactory implements RouterFactory {
public static final String NAME = "condition";
@Override
public Router getRouter(URL url) {
return new ConditionRouter(url);
}
}
public class ConditionRouter extends AbstractRouter {
public ConditionRouter(URL url) {
...
init(url.getParameterAndDecoded(RULE_KEY));
}
public void init(String rule) {
try {
if (rule == null || rule.trim().length() == 0) {
throw new IllegalArgumentException("Illegal route rule!");
}
rule = rule.replace("consumer.", "").replace("provider.", "");
int i = rule.indexOf("=>");
String whenRule = i < 0 ? null : rule.substring(0, i).trim();
String thenRule = i < 0 ? rule.trim() : rule.substring(i + 2).trim();
Map<String, MatchPair> when = StringUtils.isBlank(whenRule) || "true".equals(whenRule) ? new HashMap<String, MatchPair>() : parseRule(whenRule);
Map<String, MatchPair> then = StringUtils.isBlank(thenRule) || "false".equals(thenRule) ? null : parseRule(thenRule);
// NOTE: It should be determined on the business level whether the `When condition` can be empty or not.
this.whenCondition = when;
this.thenCondition = then;
} catch (ParseException e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
}
init方法获取 key rule 的值host = 4.3.2.1 & host != 1.2.3.4 => false初始化匹配规则
=>后面是false字符串,所以只构建了whenCondition Rule
image.png
route请求处理
@Override
public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation)
throws RpcException {
# Condition开关控制,可以在Dubbo Admin通过参数`enabled`开启,关闭条件路由
if (!enabled) {
return invokers;
}
if (CollectionUtils.isEmpty(invokers)) {
return invokers;
}
try {
# 如果没有匹配上直接返回invokers交给后续的Router处理
if (!matchWhen(url, invocation)) {
return invokers;
}
List<Invoker<T>> result = new ArrayList<Invoker<T>>();
# 上面构建的thenCondition是null,说明上黑名单了,所以当前请求被阻断.
if (thenCondition == null) {
logger.warn("The current consumer in the service blacklist. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey());
return result;
}
for (Invoker<T> invoker : invokers) {
if (matchThen(invoker.getUrl(), url)) {
result.add(invoker);
}
}
if (!result.isEmpty()) {
return result;
} else if (force) {
logger.warn("The route result is empty and force execute. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey() + ", router: " + url.getParameterAndDecoded(RULE_KEY));
return result;
}
} catch (Throwable t) {
logger.error("Failed to execute condition router rule: " + getUrl() + ", invokers: " + invokers + ", cause: " + t.getMessage(), t);
}
return invokers;
}












网友评论