美文网首页
Dubbo黑白名单源码分析

Dubbo黑白名单源码分析

作者: 王兴岭 | 来源:发表于2020-06-08 13:47 被阅读0次

环境

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级别有用本质是因为走的是条件路由

  1. 在服务端设置应用级别黑白名单是针对Provider服务设置的,但是consumer调用provider的时候,拦截的却是comsumerapplication
  2. service无效是因为在Dubbo Admin中添加的服务黑白名单是有group的,但是ServiceRouter却没有考虑group.
  3. 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;
    }

参考文章

条件路由

相关文章

网友评论

      本文标题:Dubbo黑白名单源码分析

      本文链接:https://www.haomeiwen.com/subject/xtxktktx.html