Dubbo服务目录

408 阅读7分钟

Dubbo服务目录

什么是服务目录

在一个集群中,服务提供者的数量并不是一成不变的,有时候会有扩展或者缩容,也就是增加或者减少机器的数量,这些变化的信息需要同步到消费组,服务目录中包含了服务提供者的全部信息,以及根据服务提供者的数量或者配置的变更而动态变更,并封装成invoker列表

AbstractDirectory

该类实现了Directory接口并实现了list方法,也就是获取invoker列表的方法。而具体的实现逻辑是在子类StaticDirectory和RegistryDirectory中,很常见的模板方法。

public abstract class AbstractDirectory<T> implements Directory<T> {

    // logger
    private static final Logger logger = LoggerFactory.getLogger(AbstractDirectory.class);

    private final URL url;
	// 是否被销毁
    private volatile boolean destroyed = false;
	// 消费组url
    private volatile URL consumerUrl;
	// 服务路由
    private volatile List<Router> routers;
	
    public AbstractDirectory(URL url) {
        this(url, null);
    }

    public AbstractDirectory(URL url, List<Router> routers) {
        this(url, url, routers);
    }

    public AbstractDirectory(URL url, URL consumerUrl, List<Router> routers) {
        if (url == null)
            throw new IllegalArgumentException("url == null");
        this.url = url;
        this.consumerUrl = consumerUrl;
        setRouters(routers);
    }

    @Override
    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;
    }
    protected void setRouters(List<Router> routers) {
        // 服务路由列表
        routers = routers == null ? new ArrayList<Router>() : new ArrayList<Router>(routers);
        // 获取服务路由参数router (tag,script,condition,mock)
        String routerkey = url.getParameter(Constants.ROUTER_KEY);
        // 通过SPI机制获取到路由
        if (routerkey != null && routerkey.length() > 0) {
            RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getExtension(routerkey);
            routers.add(routerFactory.getRouter(url));
        }
        // 默认添加的类型 MockInvokersSelector和TagRouter
        routers.add(new MockInvokersSelector());
        routers.add(new TagRouter());
        Collections.sort(routers);
        this.routers = routers;
    }
    // 省略其他方法
	.......
    protected abstract List<Invoker<T>> doList(Invocation invocation) throws RpcException;
}

StaticDirectory

从名字可以看出是静态目录也就是不会变化的

public class StaticDirectory<T> extends AbstractDirectory<T> {
    private final List<Invoker<T>> invokers;
	......
    @Override
    protected List<Invoker<T>> doList(Invocation invocation) throws RpcException {
        return invokers;
    }
    ......
}

可以看到是直接返回invoker列表

RegistryDirectory

RegistryDirectory是动态目录,实现了NotifyListener接口,实现了notify方法处理动态刷新invoker列表的逻辑。先来看看doList逻辑

@Override
public List<Invoker<T>> doList(Invocation invocation) {
    if (forbidden) {
        // 1. 服务提供者关闭
        // 2. 服务提供者被禁用
        throw new RpcException(RpcException.FORBIDDEN_EXCEPTION,
            "No provider available from registry " + getUrl().getAddress() + " for service " + getConsumerUrl().getServiceKey() + " on consumer " +  NetUtils.getLocalHost()
                    + " use dubbo version " + Version.getVersion() + ", please check status of providers(disabled, not registered or in blacklist).");
    }
    List<Invoker<T>> invokers = null;
    // 从缓存中获取方法名对应的invokerk列表
    Map<String, List<Invoker<T>>> localMethodInvokerMap = this.methodInvokerMap; 
    if (localMethodInvokerMap != null && localMethodInvokerMap.size() > 0) {
        // 获取方法名
        String methodName = RpcUtils.getMethodName(invocation);
        // 获取方法参数列表
        Object[] args = RpcUtils.getArguments(invocation);
        if (args != null && args.length > 0 && args[0] != null
                && (args[0] instanceof String || args[0].getClass().isEnum())) {
            invokers = localMethodInvokerMap.get(methodName + "." + args[0]); 
        }
        if (invokers == null) {
            // 通过方法名获取 Invoker 列表
            invokers = localMethodInvokerMap.get(methodName);
        }
        if (invokers == null) {
            // 通过 * 获取 Invoker 列表 (泛化调用)
            invokers = localMethodInvokerMap.get(Constants.ANY_VALUE);
        }
    }
    // 返回invoker列表
    return invokers == null ? new ArrayList<Invoker<T>>(0) : invokers;
}

invoker列表是从本地缓存中获取的 那么本地缓存中的值是从哪里更新的呢?前面说了该类实现了notify方法可以动态刷新invoker列表

@Override
public synchronized void notify(List<URL> urls) {
    // 存放providers,routers,configurators列表下的内容
    List<URL> invokerUrls = new ArrayList<URL>();
    List<URL> routerUrls = new ArrayList<URL>();
    List<URL> configuratorUrls = new ArrayList<URL>();
    for (URL url : urls) {
        // 获取协议名
        String protocol = url.getProtocol();
        // 获取category参数值,默认为providers
        String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
        // 根据不同的category类型将url封装成routerUrls,configuratorUrls,invokerUrls
        if (Constants.ROUTERS_CATEGORY.equals(category)
                || Constants.ROUTE_PROTOCOL.equals(protocol)) {
            routerUrls.add(url);
        } else if (Constants.CONFIGURATORS_CATEGORY.equals(category)
                || Constants.OVERRIDE_PROTOCOL.equals(protocol)) {
            configuratorUrls.add(url);
        } else if (Constants.PROVIDERS_CATEGORY.equals(category)) {
            invokerUrls.add(url);
        } else {
            logger.warn("Unsupported category " + category + " in notified url: " + url + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost());
        }
    }
    // 将configuratorUrls封装成List<Configurator>configurators
    if (configuratorUrls != null && !configuratorUrls.isEmpty()) {
        this.configurators = toConfigurators(configuratorUrls);
    }
    // 将routerUrls封装成List<Router>routers
    if (routerUrls != null && !routerUrls.isEmpty()) {
        List<Router> routers = toRouters(routerUrls);
        if (routers != null) { // null - do nothing
            setRouters(routers);
        }
    }
    // 做缓存
    List<Configurator> localConfigurators = this.configurators; // local reference
    // merge override parameters
    this.overrideDirectoryUrl = directoryUrl;
    if (localConfigurators != null && !localConfigurators.isEmpty()) {
        for (Configurator configurator : localConfigurators) {
            // 配置 overrideDirectoryUrl
            this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl);
        }
    }
    // 刷新invoker列表
    refreshInvoker(invokerUrls);
}
private void refreshInvoker(List<URL> invokerUrls) {
    // 如果协议头为Empty,设置forbidden为true,清空缓存,销毁所有的invoker
    if (invokerUrls != null && invokerUrls.size() == 1 && invokerUrls.get(0) != null
            && Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
        this.forbidden = true; 
        this.methodInvokerMap = null; 
        destroyAllInvokers(); 
    } else {
        this.forbidden = false; 
        Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; 
        if (invokerUrls.isEmpty() && this.cachedInvokerUrls != null) {
            // 如果invokerUrls为空,但是缓存不为空,则直接添加缓存列表至invokerUrls
            invokerUrls.addAll(this.cachedInvokerUrls);
        } else {
            // 缓存为空,则将invokerUrls添加到缓存中
            this.cachedInvokerUrls = new HashSet<URL>();
            this.cachedInvokerUrls.addAll(invokerUrls);
        }
        if (invokerUrls.isEmpty()) {
            return;
        }
        // 将 url 转成 Invoker
        Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);
        // 将newUrlInvokerMap转换成<method,List<Invoker<T>>>的映射
        // doList的方法中就是从newMethodInvokerMap中根据方法名获取List列表
        Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap);
        if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0) {
            logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :" + invokerUrls.size() + ", invoker.size :0. urls :" + invokerUrls.toString()));
            return;
        }
        // 是否有多组提供者,有的话合并  添加至缓存中
        this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;
        this.urlInvokerMap = newUrlInvokerMap;
        try {
            // 销毁无用的invoker
            destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); 
        } catch (Exception e) {
            logger.warn("destroyUnusedInvokers error. ", e);
        }
    }
}

上面的逻辑比较简单,如果协议为Empty则清空缓存,销毁所有的invoker,将url装成成Invoker列表,再将该列表转成成<method,List<Invoker>>的映射,如果有多组服务提供者则需要合并,添加到缓存中,最后清空无用的invoker。来看看其中一些具体的操作

private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
    Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<String, Invoker<T>>();
    if (urls == null || urls.isEmpty()) {
        return newUrlInvokerMap;
    }
    Set<String> keys = new HashSet<String>();
    // 获取服务消费端配置的协议
    String queryProtocols = this.queryMap.get(Constants.PROTOCOL_KEY);
    for (URL providerUrl : urls) {
        // 检测协议是否被消费端支持
        if (queryProtocols != null && queryProtocols.length() > 0) {
            boolean accept = false;
            String[] acceptProtocols = queryProtocols.split(",");
            for (String acceptProtocol : acceptProtocols) {
                if (providerUrl.getProtocol().equals(acceptProtocol)) {
                    accept = true;
                    break;
                }
            }
            // 若服务提供者协议头不被消费者所支持,则忽略当前 providerUrl
            if (!accept) {
                continue;
            }
        }
        // 忽略 empty 协议
        if (Constants.EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
            continue;
        }
        // 通过 SPI 检测服务端协议是否被消费端支持,不支持则抛出异常
        if (!ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) {
            logger.error(new IllegalStateException("Unsupported protocol " + providerUrl.getProtocol() + " in notified url: " + providerUrl + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost()
                    + ", supported protocol: " + ExtensionLoader.getExtensionLoader(Protocol.class).getSupportedExtensions()));
            continue;
        }
        // 合并url
        URL url = mergeUrl(providerUrl);

        String key = url.toFullString();
        // 过滤重复的url
        if (keys.contains(key)) {
            continue;
        }
        keys.add(key);
        Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
        Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
        // 如果缓存没有命中则创建新的invoker
        if (invoker == null) { 
            try {
                boolean enabled = true;
                if (url.hasParameter(Constants.DISABLED_KEY)) {
                    enabled = !url.getParameter(Constants.DISABLED_KEY, false);
                } else {
                    enabled = url.getParameter(Constants.ENABLED_KEY, true);
                }
                if (enabled) {
                    // 创建新的invoker
                    invoker = new InvokerDelegate<T>(protocol.refer(serviceType, url), url, providerUrl);
                }
            } catch (Throwable t) {
                logger.error("Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t);
            }
            if (invoker != null) { 
                // 将新创建的invoker加入到缓存中
                newUrlInvokerMap.put(key, invoker);
            }
        } else {
            newUrlInvokerMap.put(key, invoker);
        }
    }
    keys.clear();
    return newUrlInvokerMap;
}

上面的逻辑也很简单,就是检测协议是否被消费端支持,支持的话合并URL从缓存中获取invoker,如果缓存未命中的话创建新的invoker

private Map<String, List<Invoker<T>>> toMethodInvokers(Map<String, Invoker<T>> invokersMap) {
    Map<String, List<Invoker<T>>> newMethodInvokerMap = new HashMap<String, List<Invoker<T>>>();
    List<Invoker<T>> invokersList = new ArrayList<Invoker<T>>();
    if (invokersMap != null && invokersMap.size() > 0) {
        for (Invoker<T> invoker : invokersMap.values()) {
            // 从URL中获取methods参数值
            String parameter = invoker.getUrl().getParameter(Constants.METHODS_KEY);
            if (parameter != null && parameter.length() > 0) {
                // 获取方法列表
                String[] methods = Constants.COMMA_SPLIT_PATTERN.split(parameter);
                if (methods != null && methods.length > 0) {
                    for (String method : methods) {
                        if (method != null && method.length() > 0
                                && !Constants.ANY_VALUE.equals(method)) {
                            // 从缓存中获取,如果未命中缓存则创建新的invoker列表加入到缓存中
                            List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method);
                            if (methodInvokers == null) {
                                methodInvokers = new ArrayList<Invoker<T>>();
                                newMethodInvokerMap.put(method, methodInvokers);
                            }
                            methodInvokers.add(invoker);
                        }
                    }
                }
            }
            invokersList.add(invoker);
        }
    }
    // 进行服务级别路由
    List<Invoker<T>> newInvokersList = route(invokersList, null);
    // 存储 <*, newInvokersList> 映射关系
    newMethodInvokerMap.put(Constants.ANY_VALUE, newInvokersList);
    if (serviceMethods != null && serviceMethods.length > 0) {
        for (String method : serviceMethods) {
            List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method);
            if (methodInvokers == null || methodInvokers.isEmpty()) {
                methodInvokers = newInvokersList;
            }
            // 进行方法级别路由
            newMethodInvokerMap.put(method, route(methodInvokers, method));
        }
    }
    // 排序,转成不可变列表
    for (String method : new HashSet<String>(newMethodInvokerMap.keySet())) {
        List<Invoker<T>> methodInvokers = newMethodInvokerMap.get(method);
        Collections.sort(methodInvokers, InvokerComparator.getComparator());
        newMethodInvokerMap.put(method, Collections.unmodifiableList(methodInvokers));
    }
    return Collections.unmodifiableMap(newMethodInvokerMap);
}

上面的逻辑相对比较简单,遍历map,获取到methods数组,根据方法名映射成方法名到invoker列表的映射关系存储到map中,并基于服务和方法级别进行路由,对invoker列表进行排序转换成不可变列表

private Map<String, List<Invoker<T>>> toMergeMethodInvokerMap(Map<String, List<Invoker<T>>> methodMap) {
    Map<String, List<Invoker<T>>> result = new HashMap<String, List<Invoker<T>>>();
    for (Map.Entry<String, List<Invoker<T>>> entry : methodMap.entrySet()) {
        String method = entry.getKey();
        List<Invoker<T>> invokers = entry.getValue();
        Map<String, List<Invoker<T>>> groupMap = new HashMap<String, List<Invoker<T>>>();
        for (Invoker<T> invoker : invokers) {
            // 获取group参数值
            String group = invoker.getUrl().getParameter(Constants.GROUP_KEY, "");
            List<Invoker<T>> groupInvokers = groupMap.get(group);
            if (groupInvokers == null) {
                groupInvokers = new ArrayList<Invoker<T>>();
                groupMap.put(group, groupInvokers);
            }
            groupInvokers.add(invoker);
        }
        // 如果groupMap 只有一组键值对直接返回
        if (groupMap.size() == 1) {
            result.put(method, groupMap.values().iterator().next());
        } else if (groupMap.size() > 1) {
            // 合并多个invoker
            List<Invoker<T>> groupInvokers = new ArrayList<Invoker<T>>();
            for (List<Invoker<T>> groupList : groupMap.values()) {
                groupInvokers.add(cluster.join(new StaticDirectory<T>(groupList)));
            }
            result.put(method, groupInvokers);
        } else {
            result.put(method, invokers);
        }
    }
    return result;
}

遍历map获取method到groupInvokers的映射关系,如果groupMap只有一对则返回该键值对,如果有多个,则通过集群类合并每组 Invoker加入到result中

最后就是销毁的逻辑了

private void destroyUnusedInvokers(Map<String, Invoker<T>> oldUrlInvokerMap, Map<String, Invoker<T>> newUrlInvokerMap) {
    if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0) {
        destroyAllInvokers();
        return;
    }
    // check deleted invoker
    List<String> deleted = null;
    if (oldUrlInvokerMap != null) {
        Collection<Invoker<T>> newInvokers = newUrlInvokerMap.values();
        // 查找oldUrlInvokerMap和newUrlInvokerMap的交集存储到List<String> deleted中
        for (Map.Entry<String, Invoker<T>> entry : oldUrlInvokerMap.entrySet()) {
            if (!newInvokers.contains(entry.getValue())) {
                if (deleted == null) {
                    deleted = new ArrayList<String>();
                }
                deleted.add(entry.getKey());
            }
        }
    }
	// 从oldUrlInvokerMap中移除deleted集合中的invoker
    if (deleted != null) {
        for (String url : deleted) {
            if (url != null) {
                // 从oldUrlInvokerMap移除invoker
                Invoker<T> invoker = oldUrlInvokerMap.remove(url);
                if (invoker != null) {
                    try {
                        // 销毁invoker
                        invoker.destroy();
                        if (logger.isDebugEnabled()) {
                            logger.debug("destroy invoker[" + invoker.getUrl() + "] success. ");
                        }
                    } catch (Exception e) {
                        logger.warn("destroy invoker[" + invoker.getUrl() + "] faild. " + e.getMessage(), e);
                    }
                }
            }
        }
    }
}

上面的逻辑就是根据newUrlInvokerMap找出oldUrlInvokerMap中需要删除的url,再从oldUrlInvokerMap中移除并销毁invoekr

总结

整个服务目录的逻辑已经看完了 ,主要对动态服务目录的逻辑进行分析,并总结:

  1. 将url根据不同的category类型将url封装成routerUrls,configuratorUrls
  2. 将url转换成invoker列表,并转换成method到invoker列表的映射,如果有多组提供者则合并methodInvokerMap
  3. 最后销毁无用的invoker