美文网首页
Dubbo源码分析(十六) Router实现

Dubbo源码分析(十六) Router实现

作者: skyguard | 来源:发表于2018-11-13 13:52 被阅读0次

下面我们来分析一下Dubbo的Router机制。Dubbo的路由规则就是根据路由规则从多个Invoker中选出一个子集AbstractDirectory是所有目录服务实现的上层抽象, 它在list列举出所有invokers后,会在通过Router服务进行路由过滤。先来看一下AbstractDirectory的setRouters方法

 protected void setRouters(List<Router> routers) {
    // copy list // 复制 routers ,因为下面要修改
    routers = routers == null ? new ArrayList<Router>() : new ArrayList<Router>(routers);
    // append url router
    // 拼接 `url` 中,配置的路由规则
    String routerkey = url.getParameter(Constants.ROUTER_KEY);
    if (routerkey != null && routerkey.length() > 0) {
        RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getExtension(routerkey);
        routers.add(routerFactory.getRouter(url));
    }
    // append mock invoker selector
    routers.add(new MockInvokersSelector());
    // 排序
    Collections.sort(routers);
    // 赋值给属性
    this.routers = routers;
}

再看一下list方法

 public List<Invoker<T>> list(Invocation invocation) throws RpcException {
    if (destroyed) {
        throw new RpcException("Directory already destroyed .url: " + getUrl());
    }
    // 获得所有 Invoker 集合
    List<Invoker<T>> invokers = doList(invocation);
    // 根据路由规则,筛选 Invoker 集合
    List<Router> localRouters = this.routers; // local reference 本地引用,避免并发问题
    if (localRouters != null && !localRouters.isEmpty()) {
        for (Router router : localRouters) {
            try {
                if (router.getUrl() == null || router.getUrl().getParameter(Constants.RUNTIME_KEY, false)) {
                    invokers = router.route(invokers, getConsumerUrl(), invocation);
                }
            } catch (Throwable t) {
                logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
            }
        }
    }
    return invokers;
}

Router由RouterFactory工厂类生产,先来看一下ConditionRouterFactory

public Router getRouter(URL url) {
    return new ConditionRouter(url);
}

这个类生产ConditionRouter,这个类是条件路由实现类

public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
    // 为空,直接返回空 Invoker 集合
    if (invokers == null || invokers.isEmpty()) {
        return invokers;
    }
    try {
        // 不匹配 `whenCondition` ,直接返回 `invokers` 集合,因为不需要走 `whenThen` 的匹配
        if (!matchWhen(url, invocation)) {
            return invokers;
        }
        List<Invoker<T>> result = new ArrayList<Invoker<T>>();
        // `whenThen` 为空,则返回空 Invoker 集合
        if (thenCondition == null) {
            logger.warn("The current consumer in the service blacklist. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey());
            return result;
        }
        // 使用 `whenThen` ,匹配 `invokers` 集合。若符合,添加到 `result` 中
        for (Invoker<T> invoker : invokers) {
            if (matchThen(invoker.getUrl(), url)) {
                result.add(invoker);
            }
        }
        // 若 `result` 非空,返回它
        if (!result.isEmpty()) {
            return result;
        // 如果 `force=true` ,代表强制执行,返回空 Invoker 集合
        } else if (force) {
            logger.warn("The route result is empty and force execute. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey() + ", router: " + url.getParameterAndDecoded(Constants.RULE_KEY));
            return result;
        }
    } catch (Throwable t) {
        logger.error("Failed to execute condition router rule: " + getUrl() + ", invokers: " + invokers + ", cause: " + t.getMessage(), t);
    }
    // 如果 `force=false` ,代表不强制执行,返回 `invokers` 集合,即忽略路由规则
    return invokers;
}

再看一下ScriptRouterFactory这个类

public Router getRouter(URL url) {
    return new ScriptRouter(url);
}

这个类生产ScriptRouter,这个类是基于脚本的路由实现类

public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
    try {
        // 执行脚本
        List<Invoker<T>> invokersCopy = new ArrayList<Invoker<T>>(invokers);
        Compilable compilable = (Compilable) engine;
        Bindings bindings = engine.createBindings();
        bindings.put("invokers", invokersCopy);
        bindings.put("invocation", invocation);
        bindings.put("context", RpcContext.getContext());
        CompiledScript function = compilable.compile(rule); // 编译
        Object obj = function.eval(bindings); // 执行
        // 根据结果类型,转换成 (List<Invoker<T>> 类型返回
        if (obj instanceof Invoker[]) {
            invokersCopy = Arrays.asList((Invoker<T>[]) obj);
        } else if (obj instanceof Object[]) {
            invokersCopy = new ArrayList<Invoker<T>>();
            for (Object inv : (Object[]) obj) {
                invokersCopy.add((Invoker<T>) inv);
            }
        } else {
            invokersCopy = (List<Invoker<T>>) obj;
        }
        return invokersCopy;
    } catch (ScriptException e) {
        // 发生异常,忽略路由规则,返回全 `invokers` 集合
        // fail then ignore rule .invokers.
        logger.error("route error , rule has been ignored. rule: " + rule + ", method:" + invocation.getMethodName() + ", url: " + RpcContext.getContext().getUrl(), e);
        return invokers;
    }
}

在服务调用的时候,会根据Route集合过滤生产的Invoker,然后进行服务的调用。
Dubbo的Router实现就分析到这里了。

相关文章

网友评论

      本文标题:Dubbo源码分析(十六) Router实现

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