Dubbo原理剖析

165 阅读28分钟

前言

大家好,我是张涛,今天来分享Dubbo的实现原理,如Dubbo整体架构的设计,服务注册与消费的源码剖析,SPI扩展机制源码剖析,集群容错源码剖析,如何手撸一个简易版的Dubbo,还不懂Dubbo是什么,或许还不知道如何使用Dubbo,这里推荐看Dubbo官方文档,可以说是非常详细了Dubbo官方文档

Dubbo调用关系说明

image.png 简单介绍上图四个主要组成部分

Provider: 暴露服务的服务提供方,主要分为三层

  • Protocol 负责提供者与消费者之间协议交互数据
  • Service 真实的业务服务信息,可以理解成接口和实现
  • Container Dubbo的运行环境

Consumer: 调用远程服务的服务消费方,主要分为三层

  • Protocol 负责提供者与消费者之间协议交互数据
  • Cluster 感知提供者端的列表信息
  • Proxy 可以理解成提供者的服务调用代理类,由它接管Consumer中的接口调用逻辑

Registry: 注册中心,用于作为服务发现和路由配置等工作,提供者和消费者都会在这里进行注册
Monitor: 用于提供者和消费者中的数据统计,比如调用频次,成功失败次数等信息。

启动与执行流程

  • 提供者端启动,容器负责把Service信息加载,并通过Protocol注册到Zookeeper注册中心
  • 消费者端启动,通过Zookeeper监听机制监听提供者列表来感知提供者的信息,并在提供者发生改变时,通过注册中心及时通知消费端
  • 消费者发起请求,通过Proxy模块从ProxyFactory中获取代理类
  • 利用Cluster模块根据具体的路由规则与负载均衡策略选择具体的Invoker
  • 将信息交给消费者端的Protocol模块,负责将信息发送给提供者
  • 提供者通过Protocol模块来处理消费者的信息
  • 最后交给真正的服务提供者Service来进行处理

Dubbo整体架构设计

image.png 通过这张架构图,我们可以看见Dubbo架构设计分层思想的精妙,来介绍以下Dubbo每一层的具体职责

  • Service: 业务层,直接面向开发者,如定义的接口与实现类
  • Config: 配置层,对外提供配置以ServiceConfig与ReferenceConfig为核心,可以直接初始化配置类,也可以解析配置文件生成
  • Proxy: 服务代理层,无论是生产者还是消费者,框架都会产生一个代理类,整个过程对上层透明,就是业务层对远程调用无感
  • Registry: 注册中心层,封装服务地址的注册与发现,以服务的URL为中心
  • Cluster: 集群容错层,提供了多个提供者的路由和负载均衡,并且它桥接注册中心,以Invoker为核心
  • Monitor: 监控层,RPC调用相关的信息,如调用次数,成功失败的情况,调用时间等都在这一层完成
  • Protocol: 远程调用层,封装RPC调用,无论是服务的暴露还是服务的引用都是在Protocol中作为主功能入口负责Invoker的整个生命周期,Dubbo中所有的模型都向Invoker靠拢
  • Exchange: 信息交换层,封装请求和响应的模式,如把请求由同步转换成异步
  • Transport: 网络传输层,统一网络传输的接口,比如Netty与Mina统一为一个网络传输接口
  • Serialize: 数据序列化层,负责管理整个框架中数据传输的序列化与反序列化

服务的注册与消费过程分析

服务注册过程

image.png 首先ServiceConfig类拿到对外提供的服务实际类ref,然后通过ProxyFactory接口实现类中的getInvoker方法使用ref生成一个AbstractProxyInvoker实例,到这一步就完成具体服务到Invoker的转换。
ServiceConfig类中将具体服务转换为Exporter的源码

// 从代理工厂中获取Invoker对象
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
// 将Invoker对象转换为Exporter
Exporter<?> exporter = protocol.export(wrapperInvoker);

跟踪代理工厂中的getInvoker方法,我们以Jdk动态代理工厂举例

public class JdkProxyFactory extends AbstractProxyFactory {

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, new InvokerInvocationHandler(invoker));
    }
    
    // 源码中是返回一个AbstractProxyInvoker,参数为代理类,类型,与注册的地址信息,重写doInvoker方法,看多了源码的同学应该都清楚以do开头的方法都是具体执行的方法,doInvoke方法便是真正执行调用的。
    @Override
    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        return new AbstractProxyInvoker<T>(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName,
                                      Class<?>[] parameterTypes,
                                      Object[] arguments) throws Throwable {
                Method method = proxy.getClass().getMethod(methodName, parameterTypes);
                return method.invoke(proxy, arguments);
            }
        };
    }

}

接下来我们看看Invoker是如何转换成Exporter的,首先我们看一下Protocol接口有哪些方法

@SPI("dubbo")
public interface Protocol {

    // 获取默认的端口
    int getDefaultPort();
    
    // 将Invoker对象转换成Exporter
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

    // 根据类型和地址获取Invoker信息
    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;

    // 销毁
    void destroy();

    // 获取所有的服务信息
    default List<ProtocolServer> getServers() {
        return Collections.emptyList();
    }

}

我们找到Protocol的实现类RegistryProtocol,这个类中的export方法完成了服务的注册,并将Invoker转换成Exporter对象返回,追踪服务注册源码之前,我们看看Exporter是什么?

public interface Exporter<T> {

    // 获取Invoker
    Invoker<T> getInvoker();

    // 可以理解为对Invoker的销毁
    void unexport();

}

找到具体的实现类 ExporterChangeableWrapper,观察它的属性exporter我们便知道它采用了装饰者模式

private class ExporterChangeableWrapper<T> implements Exporter<T> {

    private final ExecutorService executor = newSingleThreadExecutor(new NamedThreadFactory("Exporter-Unexport", true));
    // 原始的Invoker对象
    private final Invoker<T> originInvoker;
    // 待进行包装的导出的对象
    private Exporter<T> exporter;
    // 订阅地址
    private URL subscribeUrl;
    // 注册地址
    private URL registerUrl;

    public ExporterChangeableWrapper(Exporter<T> exporter, Invoker<T> originInvoker) {
        this.exporter = exporter;
        this.originInvoker = originInvoker;
    }

    public Invoker<T> getOriginInvoker() {
        return originInvoker;
    }

    @Override
    public Invoker<T> getInvoker() {
        return exporter.getInvoker();
    }

    public void setExporter(Exporter<T> exporter) {
        this.exporter = exporter;
    }

    // 取消导出
    @Override
    public void unexport() {
        String key = getCacheKey(this.originInvoker);
        bounds.remove(key);
        // 通过originInvoker对象获取真实的注册中心
        Registry registry = RegistryProtocol.this.getRegistry(originInvoker);
        try {
            // 取消注册
            registry.unregister(registerUrl);
        } catch (Throwable t) {
            logger.warn(t.getMessage(), t);
        }
        try {
            // 取消对订阅的url地址监听
            NotifyListener listener = RegistryProtocol.this.overrideListeners.remove(subscribeUrl);
            registry.unsubscribe(subscribeUrl, listener);
            ExtensionLoader.getExtensionLoader(GovernanceRuleRepository.class).getDefaultExtension()
                    .removeListener(subscribeUrl.getServiceKey() + CONFIGURATORS_SUFFIX,
                            serviceConfigurationListeners.get(subscribeUrl.getServiceKey()));
        } catch (Throwable t) {
            logger.warn(t.getMessage(), t);
        }
        
        // 执行exporter.unexport()方法
        executor.submit(() -> {
            try {
                int timeout = ConfigurationUtils.getServerShutdownTimeout();
                if (timeout > 0) {
                    logger.info("Waiting " + timeout + "ms for registry to notify all consumers before unexport. " +
                            "Usually, this is called when you use dubbo API");
                    Thread.sleep(timeout);
                }
                exporter.unexport();
            } catch (Throwable t) {
                logger.warn(t.getMessage(), t);
            }
        });
    }

    public void setSubscribeUrl(URL subscribeUrl) {
        this.subscribeUrl = subscribeUrl;
    }

    public void setRegisterUrl(URL registerUrl) {
        this.registerUrl = registerUrl;
    }
}

我们已经了解了Exporter是干什么用的,接下来看看具体的导出方法做了什么?

public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
    // 获取注册中心的地址
    URL registryUrl = getRegistryUrl(originInvoker);
    // 获取当前提供者需要注册的地址
    URL providerUrl = getProviderUrl(originInvoker);
    // 获取进行注册override协议的访问地址
    final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
    // 增加override的监听器
    final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
    overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
    // 根据现有的override协议,对注册地址进行改写操作
    providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
    // 对当前的服务进行本地导出,完成后即可在看到本地的20880端口号已经启动,并且暴露服务
    final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
    // 获取真实的注册中心, 比如我们常用的ZookeeperRegistry
    final Registry registry = getRegistry(originInvoker);
    // 获取当前服务需要注册到注册中心的providerURL,主要用于去除一些没有必要的参数(比如在本地导出时所使用的qos参数等值)
    final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);
    // 获取当前url是否需要进行注册参数
    boolean register = providerUrl.getParameter(REGISTER_KEY, true);
    if (register) {
        // 将当前的提供者注册到注册中心上去
        register(registryUrl, registeredProviderUrl);
    }
    registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
    // 设置当前导出中的相关信息
    exporter.setRegisterUrl(registeredProviderUrl);
    exporter.setSubscribeUrl(overrideSubscribeUrl);
    // 返回导出对象(对数据进行封装)
    return new DestroyableExporter<>(exporter);
}

继续进入register方法,这个方法主要是从registryFactory中获取真正的注册中心,然后对服务进行注册

public void register(URL registryUrl, URL registeredProviderUrl) {
    // 获取注册中心
    Registry registry = registryFactory.getRegistry(registryUrl);
    // 对当前的服务进行注册
    registry.register(registeredProviderUrl);
    // ProviderModel 表示服务提供者模型,此对象中存储了与服务提供者相关的信息,比如服务的配置信息,服务实例等。每个被导出的服务对应一个 ProviderModel。
    ProviderModel model = ApplicationModel.getProviderModel(registeredProviderUrl.getServiceKey());
    model.addStatedUrl(new ProviderModel.RegisterStatedURL(
            registeredProviderUrl,
            registryUrl,
            true
    ));
}

追踪真正的服务注册方法之前,我们先了解以下Registry中类的目录结构

image.png

  • AbstractRegistry 是对注册中心的封装,其主要会对本地注册地址的封装,主要功能在于远程注册中心不可用的时候,可以采用本地的注册中心来使用。
  • FailbackRegistry 从名字中可以看出来,失败自动恢复,后台记录失败请求,定时重发功能。 具体看看FailbackRegistry是如何注册的?
public void register(URL url) {
    if (!acceptable(url)) {
        logger.info("URL " + url + " will not be registered to Registry. Registry " + url + " does not accept service of this protocol type.");
        return;
    }
    // 上层调用,主要用于保存已经注册的地址列表
    super.register(url);
    // 将一些错误的信息移除(确保当前地址可以在出现一些错误的地址时可以被删除)
    removeFailedRegistered(url);
    removeFailedUnregistered(url);
    try {
        // 发送给第三方渠道进行注册操作
        doRegister(url);
    } catch (Exception e) {
        Throwable t = e;

        // If the startup detection is opened, the Exception is thrown directly.
        boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
                && url.getParameter(Constants.CHECK_KEY, true)
                && !CONSUMER_PROTOCOL.equals(url.getProtocol());
        boolean skipFailback = t instanceof SkipFailbackWrapperException;
        if (check || skipFailback) {
            if (skipFailback) {
                t = t.getCause();
            }
            throw new IllegalStateException("Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);
        } else {
            logger.error("Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(), t);
        }

        // 后台异步进行重试,也是Failback比较关键的代码,底层就是一个定时调度线程进行失败重试,感兴趣的同学可以研究研究
        addFailedRegistered(url);
    }
}
继续看doRegister方法,如果是采用zookeeper作为注册中心的化,此时就会跳转到zookeeperRegistry类的doRegister方法
public void doRegister(URL url) {
    try {
        zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true));
    } catch (Throwable e) {
        throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
    }
}

private String toUrlPath(URL url) {
    // 分类地址 + URL
    return toCategoryPath(url) + PATH_SEPARATOR + URL.encode(url.toFullString());
}

private String toCategoryPath(URL url) {
    // 服务名称 + category(在当前的例子中是providers)
    return toServicePath(url) + PATH_SEPARATOR + url.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY);
}

private String toServicePath(URL url) {
    // 接口地址
    String name = url.getServiceInterface();
    if (ANY_VALUE.equals(name)) {
        return toRootPath();
    }
    // 根节点 + 接口地址
    return toRootDir() + URL.encode(name);
}

参考上图的注册流程,便可以理解为什么路径是这样了 image.png

服务的本地缓存

dubbo调用者需要通过注册中心(例如:ZK)注册信息,获取提供者,但是如果频繁往从ZK获取信息,肯定会存在单点故障问题,所以dubbo提供了将提供者信息缓存在本地的方法。
Dubbo在订阅注册中心的回调处理逻辑当中会保存服务提供者信息到本地缓存文件当中(同步/异步两种方式),以URL纬度进行全量保存。
Dubbo在服务引用过程中会创建registry对象并加载本地缓存文件,会优先订阅注册中心,订阅注册中心失败后会访问本地缓存文件内容获取服务提供信息。

服务消费过程

image.png 首先 ReferenceConfig 类的 init 方法调用 createProxy(),期间使用 Protocol 调用 refer 方法生成 Invoker 实例(如上图中的红色部分),这是服务消费的关键。接下来使用 ProxyFactory 把 Invoker 转为客户端需要的接口。
我们主要关注 ReferenceConfig 中的 createProxy 方法

private T createProxy(Map<String, String> map) {
    // 省略...
    if (urls.size() == 1) {
        // 通过Protocol调用refer方法获取可执行的Invoker
        invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
    } else {
        List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
        URL registryURL = null;
        for (URL url : urls) {
            invokers.add(REF_PROTOCOL.refer(interfaceClass, url));
            if (UrlUtils.isRegistry(url)) {
                registryURL = url; // use last registry url
            }
        }
        if (registryURL != null) { // registry url is available
            // for multi-subscription scenario, use 'zone-aware' policy by default
            URL u = registryURL.addParameterIfAbsent(CLUSTER_KEY, ZoneAwareCluster.NAME);
            // The invoker wrap relation would be like: ZoneAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker(RegistryDirectory, routing happens here) -> Invoker
            invoker = CLUSTER.join(new StaticDirectory(u, invokers));
        } else { // not a registry url, must be direct invoke.
            invoker = CLUSTER.join(new StaticDirectory(invokers));
        }
    }
    // ...省略
    // 生成Invoker的代理类
    return (T) PROXY_FACTORY.getProxy(invoker);
}

我们现在只要知道客户端消费的过程,具体里面的实现细节我们暂时不讨论,留到后面讲解。

Dubbo扩展SPI机制源码剖析

Dubbo中SPI的实现主要依赖于ExtensionLoader类,我们主要关注这个类的两个核心方法来讨论。分别是 getExtensionLoader 与 getExtension 方法

getExtensionLoader的加载过程

private static <T> boolean withExtensionAnnotation(Class<T> type) {
    // 判断类上面是否标有 SPI 注解
    return type.isAnnotationPresent(SPI.class);
}

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    // 类型不能为空
    if (type == null) {
        throw new IllegalArgumentException("Extension type == null");
    } 
    // 必须时接口类型
    else if (!type.isInterface()) {
        throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
    } 
    // 必须包含 SPI 注解
    else if (!withExtensionAnnotation(type)) {
        throw new IllegalArgumentException("Extension type (" + type + ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
    } else {
        // 尝试从已经加载过的数据中去读取(缓存功能)
        ExtensionLoader<T> loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
        if (loader == null) {
            // 如果没有的话,才会进行初始化,并且放入本地缓存中
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));
            loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
        }

        return loader;
    }
}

接着看 ExtensionLoader 的构造函数

private ExtensionLoader(Class<?> type) {
    this.type = type;
    // 如果类型时 ExtensionFactory,那么直接返回null,否则调用getExtensionLoader(ExtensionFactory.class)获取具体的 ExtensionFactory 实现类
    this.objectFactory = type == ExtensionFactory.class ? null : (ExtensionFactory)getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension();
}

接着我们看看 ExtensionFactory 是干什么的?有什么作用?

// 这里加了SPI注解,说明ExtensionFactory也是通过SPI机制加载的,与上边对应
@SPI
public interface ExtensionFactory {

    // 根据类型与名称获取具体的扩展
    <T> T getExtension(Class<T> type, String name);

}

ExtensionFactory 默认提供三个实现类,分别如下图所示

image.png 先讲解以下 AdaptiveExtensionFactory,打开类会发现,它是带有 @Adaptive 注解的,通过类名基本上也可以看出来,他其实最主要的作用是进行代理其它的ExtensionFactory,其中比较重要的方法在于 getSupportedExtensions 方法,获取所有支持的扩展信息实现。

@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {

    private final List<ExtensionFactory> factories;

    public AdaptiveExtensionFactory() {
        // 获取针对 ExtensionFactory 扩展加载器
        ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
        List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
        // 获取支持的扩展
        for (String name : loader.getSupportedExtensions()) {
            // 将所有的ExtensionFactory进行缓存
            list.add(loader.getExtension(name));
        }
        factories = Collections.unmodifiableList(list);
    }

    @Override
    public <T> T getExtension(Class<T> type, String name) {
        for (ExtensionFactory factory : factories) {
            // 交给每个真实的ExtensionFactory来处理
            T extension = factory.getExtension(type, name);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }

}

获取所有支持的扩展信息实现: ExtensionLoader.getSupportedExtensions ,这里可以看到,其实比较关键的方法在于 getExtensionClasses 方法

public Set<String> getSupportedExtensions() {
    // 获取所有的扩展类信息
    Map<String, Class<?>> clazzes = getExtensionClasses();
    // 返回所有的扩展点名称
    return Collections.unmodifiableSet(new TreeSet<>(clazzes.keySet()));
}

private Map<String, Class<?>> getExtensionClasses() {
    // 从缓存中获取已加载的扩展类
    Map<String, Class<?>> classes = cachedClasses.get();
    if (classes == null) {
        // 双重校验
        synchronized (cachedClasses) {
            classes = cachedClasses.get();
            if (classes == null) {
                // 加载扩展类
                classes = loadExtensionClasses();
                cachedClasses.set(classes);
            }
        }
    }
    return classes;
}

观察 loadExtensionClasses 方法实现。这里主要做了两件事情。1: 加载当前SPI的默认实现。2: 加载这个类的所有扩展点实现,并且按照name和Class对象的形式存储,下面会专门针对于cacheDefaultExtensionName 和 loadDirectory 方法做说明

private Map<String, Class<?>> loadExtensionClasses() {
    // 加载默认扩展的实现名称
    cacheDefaultExtensionName();
    // 获取其中每一种实现的名称和对应的classes,具体的目录请参照下面的所有目录
    Map<String, Class<?>> extensionClasses = new HashMap<>();
    // internal extension load from ExtensionLoader's ClassLoader first
    loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName(), true);
    loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"), true);

    loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
    loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
    loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
    loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
    return extensionClasses;
}

继续观察 cacheDefaultExtensionName 方法实现

private void cacheDefaultExtensionName() {
    // 获取当前类是否包含SPI注解,一般走到这里都是拥有这个注解的
    final SPI defaultAnnotation = type.getAnnotation(SPI.class);
    if (defaultAnnotation == null) {
        return;
    }
    // 来获取其的value值,这个值主要的作用是设置这个SPI中的默认扩展名
    String value = defaultAnnotation.value();
    if ((value = value.trim()).length() > 0) {
        String[] names = NAME_SEPARATOR.split(value);
        if (names.length > 1) {
            throw new IllegalStateException("More than 1 default extension name on extension " + type.getName()
                    + ": " + Arrays.toString(names));
        }
        if (names.length == 1) {
            cachedDefaultName = names[0];
        }
    }
}

观察 loadDirectory 方法实现。这里的主要功能是从这个文件夹中寻找真正的文件列表,并且对其中的文件内容解析并且放入到 extensionClasses Map中,具体解析文件的内容实现,还要参考 loadResource 实现。

private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
    loadDirectory(extensionClasses, dir, type, false);
}

private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type, boolean extensionLoaderClassLoaderFirst) {
    // 文件名称规则: 路径/包名.接口名
    String fileName = dir + type;
    try {
        // 寻找classloader和url列表
        Enumeration<java.net.URL> urls = null;
        ClassLoader classLoader = findClassLoader();
        
        // try to load from ExtensionLoader's ClassLoader first
        // 如果需要的话,先使用扩展类加载器加载
        if (extensionLoaderClassLoaderFirst) {
            ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
            if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
                urls = extensionLoaderClassLoader.getResources(fileName);
            }
        }
        // 如果url列表为空,使用引用类加载器加载
        if(urls == null || !urls.hasMoreElements()) {
            if (classLoader != null) {
                urls = classLoader.getResources(fileName);
            } else {
                urls = ClassLoader.getSystemResources(fileName);
            }
        }
        // 如果rul列表不为空
        if (urls != null) {
            while (urls.hasMoreElements()) {
                java.net.URL resourceURL = urls.nextElement();
                // 遍历每一个资源文件,并且加载资源到 extensionClass,主要功能是读取文件内容
                loadResource(extensionClasses, classLoader, resourceURL);
            }
        }
    } catch (Throwable t) {
        logger.error("Exception occurred when loading extension class (interface: " +
                type + ", description file: " + fileName + ").", t);
    }
}

private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
    try {
        // 读取文件
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
            String line;
            while ((line = reader.readLine()) != null) {
                final int ci = line.indexOf('#');
                if (ci >= 0) {
                    // 获取 # 作为分隔符前的内容
                    line = line.substring(0, ci);
                }
                line = line.trim();
                if (line.length() > 0) {
                    try {
                        String name = null;
                        // 以 = 分隔成 key value 键值对
                        int i = line.indexOf('=');
                        if (i > 0) {
                            name = line.substring(0, i).trim();
                            line = line.substring(i + 1).trim();
                        }
                        if (line.length() > 0) {
                            // 对类信息进行加载操作
                            loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
                        }
                    } catch (Throwable t) {
                        IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
                        exceptions.put(line, e);
                    }
                }
            }
        }
    } catch (Throwable t) {
        logger.error("Exception occurred when loading extension class (interface: " +
                type + ", class file: " + resourceURL + ") in " + resourceURL, t);
    }
}

private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
    // 当前扩展点的实现,必须是当前扩展接口的实现才可以
    if (!type.isAssignableFrom(clazz)) {
        throw new IllegalStateException("Error occurred when loading extension class (interface: " +
                type + ", class line: " + clazz.getName() + "), class "
                + clazz.getName() + " is not subtype of interface.");
    }
    // 如果是包含了Adaptive注解,则认为是需要对扩展点包装的方法,这里只做了存储操作,存储至cachedAdaptiveClass中
    if (clazz.isAnnotationPresent(Adaptive.class)) {
        cacheAdaptiveClass(clazz);
    } else if (isWrapperClass(clazz)) {
        cacheWrapperClass(clazz);
    } else {
        clazz.getConstructor();
        if (StringUtils.isEmpty(name)) {
            name = findAnnotationName(clazz);
            if (name.length() == 0) {
                throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
            }
        }

        String[] names = NAME_SEPARATOR.split(name);
        if (ArrayUtils.isNotEmpty(names)) {
            cacheActivateClass(clazz, names[0]);
            // 进行名称映射保存
            for (String n : names) {
                cacheName(clazz, n);
                saveInExtensionClass(extensionClasses, clazz, n);
            }
        }
    }
}

根据name获取扩展点的方法 getExtension

public T getExtension(String name) {
    if (StringUtils.isEmpty(name)) {
        throw new IllegalArgumentException("Extension name == null");
    }
    // 如果name = true,采用默认的扩展实现类
    if ("true".equals(name)) {
        return getDefaultExtension();
    }
    // 获取当前类的holder
    final Holder<Object> holder = getOrCreateHolder(name);
    Object instance = holder.get();
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
                // 真正进行创建实例
                instance = createExtension(name);
                holder.set(instance);
            }
        }
    }
    return (T) instance;
}

追踪 getOrCreateHolder 方法,方法很简单,先从缓存中获取,缓存中不存在就创建一个Holder对象放入缓存里边

private Holder<Object> getOrCreateHolder(String name) {
    Holder<Object> holder = cachedInstances.get(name);
    if (holder == null) {
        cachedInstances.putIfAbsent(name, new Holder<>());
        holder = cachedInstances.get(name);
    }
    return holder;
}

主要看看 createInstances 方法是如何实现的?

private T createExtension(String name) {
    // 这个方法上面已经讲过了,先从配置文件中加载所有的扩展类信息并通过名称与类做映射,这里是通过名称获取具体的类型类
    Class<?> clazz = getExtensionClasses().get(name);
    if (clazz == null) {
        throw findException(name);
    }
    try {
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance == null) {
            // 如果实例不存在,就通过反射创建实例对象
            EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        // 注入其他扩展点的实体,用于扩展点和其他的扩展点相互打通
        injectExtension(instance);
        Set<Class<?>> wrapperClasses = cachedWrapperClasses;
        if (CollectionUtils.isNotEmpty(wrapperClasses)) {
            for (Class<?> wrapperClass : wrapperClasses) {
                instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
            }
        }
        // 对扩展点进行初始化操作
        initExtension(instance);
        return instance;
    } catch (Throwable t) {
        throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                type + ") couldn't be instantiated: " + t.getMessage(), t);
    }
}

继续跟踪 injectExtension 方法

private T injectExtension(T instance) {

    if (objectFactory == null) {
        return instance;
    }

    try {
        // 遍历其中的所有方法
        for (Method method : instance.getClass().getMethods()) {
            if (!isSetter(method)) {
                continue;
            }
            /**
             * Check {@link DisableInject} to see if we need auto injection for this property
             */
            // 如果设置了不需要自动注入这不进行处理
            if (method.getAnnotation(DisableInject.class) != null) {
                continue;
            }
            // 获取方法的参数类型,如果是私有的不处理
            Class<?> pt = method.getParameterTypes()[0];
            if (ReflectUtils.isPrimitives(pt)) {
                continue;
            }

            try {
                // 获取方法名称,去除get或set,将首字母小写,如setRandom,得到的名称为random
                String property = getSetterProperty(method);
                // 通过从ExtensionLoader中加载指定的扩展点,比如有一个方法setRandom(LoadBalance loadBalance),那么则认为这需要加载负载均衡中名为random的扩展点
                Object object = objectFactory.getExtension(pt, property);
                if (object != null) {
                    method.invoke(instance, object);
                }
            } catch (Exception e) {
                logger.error("Failed to inject via method " + method.getName()
                        + " of interface " + type.getName() + ": " + e.getMessage(), e);
            }

        }
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    }
    return instance;
}

Adaptive 功能的实现原理

Adaptive的主要功能是对所有的扩展点进行封装为一个类,通过URL传入参数的时动态选择需要使用的扩展点。其底层的实现原理就是动态代理,我们通过阅读源码,看看他是如何通过动态代理进行加载的。
源码入口主要看看 getAdaptiveExtension 方法是如何实现的?

public T getAdaptiveExtension() {
    // 又是双重校验
    Object instance = cachedAdaptiveInstance.get();
    if (instance == null) {
        if (createAdaptiveInstanceError != null) {
            throw new IllegalStateException("Failed to create adaptive instance: " +
                    createAdaptiveInstanceError.toString(),
                    createAdaptiveInstanceError);
        }

        synchronized (cachedAdaptiveInstance) {
            instance = cachedAdaptiveInstance.get();
            if (instance == null) {
                try {
                    // 创建扩展实例
                    instance = createAdaptiveExtension();
                    cachedAdaptiveInstance.set(instance);
                } catch (Throwable t) {
                    createAdaptiveInstanceError = t;
                    throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
                }
            }
        }
    }

    return (T) instance;
}

追踪 createAdaptiveExtension 方法看看是如何实现的?

private T createAdaptiveExtension() {
    try {
        return injectExtension((T) getAdaptiveExtensionClass().newInstance());
    } catch (Exception e) {
        throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
    }
}

private Class<?> getAdaptiveExtensionClass() {
    // 确保已经加载了所有的扩展类信息
    getExtensionClasses();
    // 如果已经加载过了,则直接返回,cacheAdaptiveClass在之前的loadClass方法中出现过
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    // 否则进行构建操作
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

看看 createAdaptiveExtensionClass() 方法

private Class<?> createAdaptiveExtensionClass() {
    // 实例化一个新的Adaptive的代码生成器,并且进行代码生成
    String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
    ClassLoader classLoader = findClassLoader();
    // 通过扩展点,寻找编译器
    org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
    // 编译并且生成class
    return compiler.compile(code, classLoader);
}

代码生成方法

public String generate() {
    // no need to generate adaptive class since there's no adaptive method found.
    // 如果没有任何方法标记为Adaptive,则不做处理
    if (!hasAdaptiveMethod()) {
        throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");
    }

    StringBuilder code = new StringBuilder();
    code.append(generatePackageInfo());
    code.append(generateImports());
    code.append(generateClassDeclaration());

    Method[] methods = type.getMethods();
    for (Method method : methods) {
        code.append(generateMethod(method));
    }
    code.append("}");

    if (logger.isDebugEnabled()) {
        logger.debug(code.toString());
    }
    return code.toString();
}

private String generateMethod(Method method) {
    String methodReturnType = method.getReturnType().getCanonicalName();
    String methodName = method.getName();
    String methodContent = generateMethodContent(method);
    String methodArgs = generateMethodArguments(method);
    String methodThrows = generateMethodThrows(method);
    return String.format(CODE_METHOD_DECLARATION, methodReturnType, methodName, methodArgs, methodThrows, methodContent);
}

private String generateMethodContent(Method method) {
    // 获取Adaptive注解,只支持含有Adaptive注解方法处理
    Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
    StringBuilder code = new StringBuilder(512);
    if (adaptiveAnnotation == null) {
        // 没有该注解,直接抛出异常
        return generateUnsupported(method);
    } else {
        // 获取URL参数的所在位置
        int urlTypeIndex = getUrlTypeIndex(method);

        // found parameter in URL type
        if (urlTypeIndex != -1) {
            // Null Point check
            // 增加判断url不为空的代码
            code.append(generateUrlNullCheck(urlTypeIndex));
        } else {
            // did not find parameter in URL type
            // 获取这个方法中的所有参数列表
            // 寻找每个参数中是否有"get"开头的方法,并且返回值是URL的
            // 如果有则同样认定为找到,否则抛出异常
            code.append(generateUrlAssignmentIndirectly(method));
        }

        // 获取扩展点的适配名称
        String[] value = getMethodAdaptiveValue(adaptiveAnnotation);

        // 判断是否有参数是Invocation类
        // 这里判断的主要目的在于,拥有Invocation时,则获取扩展名称的方式发生改变
        // 存在Invocation时,通过getMethodParameter,否则通过getParameter来执行
        // getMethodParameter是dubboURL中特有的,用于将"test.a"转换为"testA"的形式
        boolean hasInvocation = hasInvocationArgument(method);
        // 增加有Invocation类时的不为空判断
        code.append(generateInvocationArgumentNullCheck(method));
        // 生成获取扩展点名称的方法
        code.append(generateExtNameAssignment(value, hasInvocation));
        // check extName == null?
        // 检查扩展点不能为空
        code.append(generateExtNameNullCheck(value));
        // 获取扩展点实现
        code.append(generateExtensionAssignment());

        // return statement
        // 返回扩展点中的真实调用
        code.append(generateReturnAndInvocation(method));
    }

    return code.toString();
}

集群容错源码剖析

在集群调用失败时,Dubbo提供了多种容错方案,缺省为 failover 重试

image.png 集群容错模式有以下几种

  • Failover Cluster: 失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。
  • Failfast Cluster: 快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
  • Failsafe Cluster: 失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
  • Failback Cluster: 失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
  • Forking Cluster: 并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。
  • Broadcast Cluster: 广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。
  • Available Cluster: 调用目前可用的实例(只调用一个),如果当前没有可用的实例,则抛出异常。通常用于不需要负载均衡的场景。
  • Mergeable Cluster: 将集群中的调用结果聚合起来返回结果,通常和group一起配合使用。通过分组对结果进行聚合并返回聚合后的结果,比如菜单服务,用group区分同一接口的多种实现,现在消费方需从每种group中调用一次并返回结果,对结果进行合并之后返回,这样就可以实现聚合菜单项。
  • ZoneAware Cluster: 多注册中心订阅的场景,注册中心集群间的负载均衡。

信息缓存接口 Directory

Directory是 dubbo 中的一个接口,主要用于缓存当前可以被调用的提供者列表信息。

public interface Directory<T> extends Node {

    // 获取服务的类型
    Class<T> getInterface();

    // 根据本次调用的信息来获取所有可以被执行的提供者信息
    List<Invoker<T>> list(Invocation invocation) throws RpcException;

    // 获取所有的提供者信息
    List<Invoker<T>> getAllInvokers();

}

找到 Directory 的实现类 AbstractDirectory

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

    @Override
    public List<Invoker<T>> list(Invocation invocation) throws RpcException {
        if (destroyed) {
            throw new RpcException("Directory already destroyed .url: " + getUrl());
        }
        // 交给子类进行处理
        return doList(invocation);
    }

    protected abstract List<Invoker<T>> doList(Invocation invocation) throws RpcException;

}

找到具体实现类 RegistryDirectory 中的doList方法

public List<Invoker<T>> doList(Invocation invocation) {

    if (multiGroup) {
        return this.invokers == null ? Collections.emptyList() : this.invokers;
    }

    List<Invoker<T>> invokers = null;
    try {
        // 交给routerChain去处理并且获取所有的invokers
        invokers = routerChain.route(getConsumerUrl(), invocation);
    } catch (Throwable t) {
        logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
    }

    return invokers == null ? Collections.emptyList() : invokers;
}

继续看 RouterChain 中的 route 方法

public List<Invoker<T>> route(URL url, Invocation invocation) {
    // 可以看到 invokers 此时已经有值了,后边只不过是通过路由规则过滤
    List<Invoker<T>> finalInvokers = invokers;
    for (Router router : routers) {
        finalInvokers = router.route(finalInvokers, url, invocation);
    }
    return finalInvokers;
}

invoker列表是在什么时候获取的呢?主要看服务消费流程中未讲解的 RegistryProtocol.refer 方法

public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
    // 获取注册中心的地址URL(主要用于转换协议),比如我们是使用的zookeeper,那么他就会转换为zookeeper://
    url = getRegistryUrl(url);
    // 获取注册中心的配置信息
    Registry registry = registryFactory.getRegistry(url);
    if (RegistryService.class.equals(type)) {
        return proxyFactory.getInvoker((T) registry, type, url);
    }

    // 适用于多个分组时使用
    Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY));
    String group = qs.get(GROUP_KEY);
    if (group != null && group.length() > 0) {
        if ((COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) {
            return doRefer(getMergeableCluster(), registry, type, url);
        }
    }
    // 真正进行构建 invoker 和我们上面的 Directory
    return doRefer(cluster, registry, type, url);
}

继续跟踪 doRefer 方法,这里面就是最主要产生Directory并且是注册和监听的主要代码逻辑。我们所需要的 routerChain 也是在这里产生的。

private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
    // 实例化Directory
    RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
    // 设置注册中心和所使用的协议
    directory.setRegistry(registry);
    directory.setProtocol(protocol);
    // all attributes of REFER_KEY
    // 生成监听路径URL
    Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
    URL subscribeUrl = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters);
    if (!ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(REGISTER_KEY, true)) {
        // 在Directory中设置监听的consumerurl地址
        directory.setRegisteredConsumerUrl(getRegisteredConsumerUrl(subscribeUrl, url));
        // 在注册中心中注册消费者URL,也就是我们之前的Zookeeper的node中看到的consumer://
        registry.register(directory.getRegisteredConsumerUrl());
    }
    // 构建路由链
    directory.buildRouterChain(subscribeUrl);
    // 进行监听所有的 provider
    directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY,
            PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY));
    // 加入到集群中
    Invoker invoker = cluster.join(directory);
    return invoker;
}

继续看看 dubbo 的服务发现机制是如何做的,从下图可以看到 RegistryDirectory 实现了 NotifyListener 接口

image.png

public interface NotifyListener {

    // 当监听的路径发生变化时,便会调用 notify 方法
    void notify(List<URL> urls);

}

查看 RegistryDirectory 中的 notify 方法,会调用 refreshOverrideAndInvoker 方法,然后调用 refreshInvoker 方法,也就是这个方法中会调用 routerChain.setInvokers(newInvokers);

路由规则实现原理

我们先找到路口,便是 RouterChain 中的 route 方法

public List<Invoker<T>> route(URL url, Invocation invocation) {
    List<Invoker<T>> finalInvokers = invokers;
    for (Router router : routers) {
        // 依次交给所有的路由规则进行选取路由列表
        finalInvokers = router.route(finalInvokers, url, invocation);
    }
    return finalInvokers;
}

我们找到具体的Router实现类ConditionRouter,在这里查看 route 方法的执行逻辑
讲解这个方法之前,先看看这个类中比较重要的两个属性

  • Map<String, MatchPair> whenCondition : 是否满足判断条件
  • Map<String, MatchPair> thenCondition : 当满足判断条件时如何选择invokers 每一个MatchPair对应两个属性
  • matches : 表示满足的条件,也就是后边路由规则中的 = 分隔符条件
  • mismatches : 表示不满足的条件,也就是后边路由规则中的 != 分隔符条件 进入route方法
public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation)
        throws RpcException {
        
    // 不启用的时,则直接返回提供者的列表
    if (!enabled) {
        return invokers;
    }
    // 如果不存在任何invoker则直接返回
    if (CollectionUtils.isEmpty(invokers)) {
        return invokers;
    }
    try {
        // 判断是否满足判断条件,不满足直接返回列表
        if (!matchWhen(url, invocation)) {
            return invokers;
        }
        List<Invoker<T>> result = new ArrayList<Invoker<T>>();
        if (thenCondition == null) {
            logger.warn("The current consumer in the service blacklist. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey());
            return result;
        }
        // 依次判断每一个invoker的url是否满足条件
        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;
}

其中主要判断逻辑 matchWhen 与 matchThen

boolean matchWhen(URL url, Invocation invocation) {
    // 主要是对消费者匹配条件,允许为空
    return CollectionUtils.isEmptyMap(whenCondition) || matchCondition(whenCondition, url, null, invocation);
}

private boolean matchThen(URL url, URL param) {
    // 主要是对提供者匹配条件,不允许为空
    return CollectionUtils.isNotEmptyMap(thenCondition) && matchCondition(thenCondition, url, param, null);
}

private boolean matchCondition(Map<String, MatchPair> condition, URL url, URL param, Invocation invocation) {
    Map<String, String> sample = url.toMap();
    boolean result = false;
    for (Map.Entry<String, MatchPair> matchPair : condition.entrySet()) {
        String key = matchPair.getKey();
        String sampleValue;
        //get real invoked method name from invocation
        if (invocation != null && (METHOD_KEY.equals(key) || METHODS_KEY.equals(key))) {
            sampleValue = invocation.getMethodName();
        } else if (ADDRESS_KEY.equals(key)) {
            sampleValue = url.getAddress();
        } else if (HOST_KEY.equals(key)) {
            sampleValue = url.getHost();
        } else {
            sampleValue = sample.get(key);
            if (sampleValue == null) {
                sampleValue = sample.get(key);
            }
        }
        if (sampleValue != null) {
            if (!matchPair.getValue().isMatch(sampleValue, param)) {
                return false;
            } else {
                result = true;
            }
        } else {
            //not pass the condition
            if (!matchPair.getValue().matches.isEmpty()) {
                return false;
            } else {
                result = true;
            }
        }
    }
    return result;
}

最后看看路由规则是如何生成的,主要看 ConditionRouter 的 init 方法,关键方法 parseRule 方法,建议结合官放文档路由规则来看

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.", "");
        // 根据"=>"来判断when或者then条件
        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);
    }
}

parseRule方法

private static Map<String, MatchPair> parseRule(String rule)
        throws ParseException {
    Map<String, MatchPair> condition = new HashMap<String, MatchPair>();
    if (StringUtils.isBlank(rule)) {
        return condition;
    }
    // Key-Value pair, stores both match and mismatch conditions
    // 当前所操作的数据,用于后面循环中使用,标识上一次循环中所操作的信息
    MatchPair pair = null;
    // Multiple values
    Set<String> values = null;
    // 转化每一个条件
    // 这里分别会对每一次的分割做匹配
    // host = 1.1.1.* & host != 1.1.1.2 & method=sayHello
    // 1. "" host
    // 2. "=" 1.1.1.x
    // 3. "&" host
    // 4. "!=" 1.1.1.2
    // ....
    final Matcher matcher = ROUTE_PATTERN.matcher(rule);
    while (matcher.find()) { // Try to match one by one
        // 分隔符
        String separator = matcher.group(1);
        // 内容
        String content = matcher.group(2);
        // Start part of the condition expression.
        // 如果不存在分隔符,则认为是首个判断
        if (StringUtils.isEmpty(separator)) {
            pair = new MatchPair();
            // 则直接放入当前condition
            condition.put(content, pair);
        }
        // The KV part of the condition expression
        // 如果是"&"则代表并且
        else if ("&".equals(separator)) {
            // 如果当前的when或者then中不包含该判定条件则添加则放入,否则当前的condition就需要拿出来
            if (condition.get(content) == null) {
                pair = new MatchPair();
                condition.put(content, pair);
            } else {
                pair = condition.get(content);
            }
        }
        // The Value in the KV part.
        else if ("=".equals(separator)) {
            if (pair == null) {
                throw new ParseException("Illegal route rule ""
                        + rule + "", The error char '" + separator
                        + "' at index " + matcher.start() + " before ""
                        + content + "".", matcher.start());
            }
            // 如果是等于的比较,则需要将值放入matches中
            values = pair.matches;
            values.add(content);
        }
        // The Value in the KV part.
        else if ("!=".equals(separator)) {
            if (pair == null) {
                throw new ParseException("Illegal route rule ""
                        + rule + "", The error char '" + separator
                        + "' at index " + matcher.start() + " before ""
                        + content + "".", matcher.start());
            }
            // 如果为不等于,则需要放入到不等于中
            values = pair.mismatches;
            values.add(content);
        }
        // 如果values是多个的话
        // The Value in the KV part, if Value have more than one items.
        else if (",".equals(separator)) { // Should be separated by ','
            if (values == null || values.isEmpty()) {
                throw new ParseException("Illegal route rule ""
                        + rule + "", The error char '" + separator
                        + "' at index " + matcher.start() + " before ""
                        + content + "".", matcher.start());
            }
            // 则分别加入到values列表中
            values.add(content);
        } else {
            throw new ParseException("Illegal route rule "" + rule
                    + "", The error char '" + separator + "' at index "
                    + matcher.start() + " before "" + content + "".", matcher.start());
        }
    }
    return condition;
}

Cluster 组件

Dubbo中非常关键的一个组件,它主要用于代理真正的Invoker执行时做处理,提供了多种容错方案。

@SPI(FailoverCluster.NAME)
public interface Cluster {

    // 生成一个Invoker
    @Adaptive
    <T> Invoker<T> join(Directory<T> directory) throws RpcException;

}

进入 AbstractCluster 类中的 join 方法

private <T> Invoker<T> buildClusterInterceptors(AbstractClusterInvoker<T> clusterInvoker, String key) {
    AbstractClusterInvoker<T> last = clusterInvoker;
    // 获取所有的拦截器
    List<ClusterInterceptor> interceptors = ExtensionLoader.getExtensionLoader(ClusterInterceptor.class).getActivateExtension(clusterInvoker.getUrl(), key);

    if (!interceptors.isEmpty()) {
        for (int i = interceptors.size() - 1; i >= 0; i--) {
            // 对拦截器进行一层封装,这里运用了装饰者模式
            final ClusterInterceptor interceptor = interceptors.get(i);
            final AbstractClusterInvoker<T> next = last;
            last = new InterceptorInvokerNode<>(clusterInvoker, interceptor, next);
        }
    }
    return last;
}

@Override
public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
    // 使用子类doJoin来真正生成Invoker,并且使用拦截器的方式进行一层封装
    return buildClusterInterceptors(doJoin(directory), directory.getUrl().getParameter(REFERENCE_INTERCEPTOR_KEY));
}

继续进入默认的集群容错模式 FailoverCluster 的 doJoin 方法

public class FailoverCluster extends AbstractCluster {

    public final static String NAME = "failover";
    // 进入源码后发现,它是直接新创建一个 FailoverClusterInvoker
    @Override
    public <T> AbstractClusterInvoker<T> doJoin(Directory<T> directory) throws RpcException {
        return new FailoverClusterInvoker<>(directory);
    }

}

核心方法在 FailoverClusterInvoker 中的 doInvoke 方法,先看它的父类 AbstractClusterInvoker的invoke方法,这里运用了模板方法模式,这里的 invoke 方法就是模板方法。

public Result invoke(final Invocation invocation) throws RpcException {
    // 检查是否已经关闭了
    checkWhetherDestroyed();

    // binding attachments into invocation.
    // 拷贝当前RPCContext中的附加信息到当前的invocation中
    Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
    if (contextAttachments != null && contextAttachments.size() != 0) {
        ((RpcInvocation) invocation).addAttachments(contextAttachments);
    }
    // 找寻出所有支持的invoker,已经路由过的
    List<Invoker<T>> invokers = list(invocation);
    // 初始化负载均衡器
    LoadBalance loadbalance = initLoadBalance(invokers, invocation);
    // 用于适配异步请求使用
    RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
    // 交给子类进行真正处理请求
    return doInvoke(invocation, invokers, loadbalance);
}

进入 FailoverClusterInvoker 中的 doInvoke 方法

public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
    // 如果没有任何的invoker则抛出异常
    List<Invoker<T>> copyInvokers = invokers;
    checkInvokers(copyInvokers, invocation);
    // 获取这个方法最大的重试次数
    String methodName = RpcUtils.getMethodName(invocation);
    int len = getUrl().getMethodParameter(methodName, RETRIES_KEY, DEFAULT_RETRIES) + 1;
    if (len <= 0) {
        len = 1;
    }
    // retry loop.
    RpcException le = null; // last exception.
    List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyInvokers.size()); // invoked invokers.
    Set<String> providers = new HashSet<String>(len);
    // 通过for循环的形式表示可以重试的次数
    for (int i = 0; i < len; i++) {
        //Reselect before retry to avoid a change of candidate `invokers`.
        //NOTE: if `invokers` changed, then `invoked` also lose accuracy.
        if (i > 0) {
            // 每次都执行一次是否关闭当前consumer的判断
            checkWhetherDestroyed();
            // 重新获取一遍invoker列表
            copyInvokers = list(invocation);
            // check again
            // 再次进行一次存在invoker的检查
            checkInvokers(copyInvokers, invocation);
        }
        // 选择具体的invoker(交给负载均衡)
        Invoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked);
        // 增加到已经执行过得invoker列表中
        invoked.add(invoker);
        RpcContext.getContext().setInvokers((List) invoked);
        try {
            // 让其真正的去进行执行操作
            Result result = invoker.invoke(invocation);
            if (le != null && logger.isWarnEnabled()) {
                logger.warn("Although retry the method " + methodName
                        + " in the service " + getInterface().getName()
                        + " was successful by the provider " + invoker.getUrl().getAddress()
                        + ", but there have been failed providers " + providers
                        + " (" + providers.size() + "/" + copyInvokers.size()
                        + ") from the registry " + directory.getUrl().getAddress()
                        + " on the consumer " + NetUtils.getLocalHost()
                        + " using the dubbo version " + Version.getVersion() + ". Last error is: "
                        + le.getMessage(), le);
            }
            return result;
        } catch (RpcException e) {
            // 如果是业务异常则直接抛出
            if (e.isBiz()) { // biz exception.
                throw e;
            }
            le = e;
        } catch (Throwable e) {
            le = new RpcException(e.getMessage(), e);
        } finally {
            providers.add(invoker.getUrl().getAddress());
        }
    }
    // 如果重试了指定次数后依旧失败,则直接认定为失败
    throw new RpcException(le.getCode(), "Failed to invoke the method "
            + methodName + " in the service " + getInterface().getName()
            + ". Tried " + len + " times of the providers " + providers
            + " (" + providers.size() + "/" + copyInvokers.size()
            + ") from the registry " + directory.getUrl().getAddress()
            + " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version "
            + Version.getVersion() + ". Last error is: "
            + le.getMessage(), le.getCause() != null ? le.getCause() : le);
}

负载均衡实现原理

首先看看 LoadBalance 接口的定义

// 默认采用随机负载均衡算法
@SPI(RandomLoadBalance.NAME)
public interface LoadBalance {

    // 通过不同的负载均衡策略选择真正的待执行的Invoker
    @Adaptive("loadbalance")
    <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;

}

依旧先看AbstractLoadBalance中的select方法,发现dubbo中大量运用模板方法模式

public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {
    // 如果不存在任何的invoker则直接返回
    if (CollectionUtils.isEmpty(invokers)) {
        return null;
    }
    // 如果还有一个invoker则直接返回,不需要执行负载均衡
    if (invokers.size() == 1) {
        return invokers.get(0);
    }
    // 交给子类进行实现
    return doSelect(invokers, url, invocation);
}

继续看 RandomLoadBalance 中的 doSelect 方法

protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
    // 总计的invoker列表数量
    int length = invokers.size();
    // 默认每个invoker的权重都是相同的
    boolean sameWeight = true;
    // 所有的权重列表
    int[] weights = new int[length];
    // 首个invoker的权重信息
    int firstWeight = getWeight(invokers.get(0), invocation);
    weights[0] = firstWeight;
    // 计算总共的权重,并且吧每一个invoker的权重进行设置到列表中
    int totalWeight = firstWeight;
    for (int i = 1; i < length; i++) {
        int weight = getWeight(invokers.get(i), invocation);
        // save for later use
        weights[i] = weight;
        // Sum
        totalWeight += weight;
        if (sameWeight && weight != firstWeight) {
            sameWeight = false;
        }
    }
    // 如果权重不相同,采用轮盘算法
    if (totalWeight > 0 && !sameWeight) {
        // 通过总共的权重来随机分配
        int offset = ThreadLocalRandom.current().nextInt(totalWeight);
       // 看看最终落到哪一个机器上去
        for (int i = 0; i < length; i++) {
            offset -= weights[i];
            if (offset < 0) {
                return invokers.get(i);
            }
        }
    }
    // 如果权重都是相同的话,则随机选取一个即可
    return invokers.get(ThreadLocalRandom.current().nextInt(length));
}

Invoker 执行逻辑

依旧看看 Invoker 接口的定义

public interface Invoker<T> extends Node {

    // 当前执行器的服务接口是哪一个
    Class<T> getInterface();

    // 执行请求操作
    Result invoke(Invocation invocation) throws RpcException;

}

还是与上面一样的逻辑,先进入AbstractInvoker 的 invoke 方法

public Result invoke(Invocation inv) throws RpcException {
    // 判断系统是否已经关闭
    if (destroyed.get()) {
        logger.warn("Invoker for service " + this + " on consumer " + NetUtils.getLocalHost() + " is destroyed, "
                + ", dubbo version is " + Version.getVersion() + ", this invoker should not be used any longer");
    }
    RpcInvocation invocation = (RpcInvocation) inv;
    invocation.setInvoker(this);
    if (CollectionUtils.isNotEmptyMap(attachment)) {
        invocation.addAttachmentsIfAbsent(attachment);
    }
    Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
    // 设置所有的RPCContext中的附加信息
    if (CollectionUtils.isNotEmptyMap(contextAttachments)) {
        
        invocation.addAttachments(contextAttachments);
    }

    // 获取执行的模式
    invocation.setInvokeMode(RpcUtils.getInvokeMode(url, invocation));
    // 设置执行id,主要用于适配异步模式使用
    RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);

    // 交给子类进行真正的执行
    AsyncRpcResult asyncResult;
    try {
        asyncResult = (AsyncRpcResult) doInvoke(invocation);
    } catch (InvocationTargetException e) { // biz exception
        Throwable te = e.getTargetException();
        if (te == null) {
            asyncResult = AsyncRpcResult.newDefaultAsyncResult(null, e, invocation);
        } else {
            if (te instanceof RpcException) {
                ((RpcException) te).setCode(RpcException.BIZ_EXCEPTION);
            }
            asyncResult = AsyncRpcResult.newDefaultAsyncResult(null, te, invocation);
        }
    } catch (RpcException e) {
        if (e.isBiz()) {
            asyncResult = AsyncRpcResult.newDefaultAsyncResult(null, e, invocation);
        } else {
            throw e;
        }
    } catch (Throwable e) {
        asyncResult = AsyncRpcResult.newDefaultAsyncResult(null, e, invocation);
    }
    // 设置执行的结果信息
    RpcContext.getContext().setFuture(new FutureAdapter(asyncResult.getResponseFuture()));
    // 返回结果
    return asyncResult;
}

进入 DubboInvoker 中的 doInvoke 方法

protected Result doInvoke(final Invocation invocation) throws Throwable {
    RpcInvocation inv = (RpcInvocation) invocation;
    final String methodName = RpcUtils.getMethodName(invocation);
    inv.setAttachment(PATH_KEY, getUrl().getPath());
    inv.setAttachment(VERSION_KEY, version);
    // 传输的客户端
    ExchangeClient currentClient;
    if (clients.length == 1) {
        currentClient = clients[0];
    } else {
        currentClient = clients[index.getAndIncrement() % clients.length];
    }
    try {
        // 是否返回值,也就是相当于发送了一个指令,不在乎服务端的返回,通常适用于异步请求
        boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
        // 获取超时的配置
        int timeout = getUrl().getMethodPositiveParameter(methodName, TIMEOUT_KEY, DEFAULT_TIMEOUT);
        if (isOneway) {
            // 如果不需要返回值信息(异步)
            boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
            // 发送命令
            currentClient.send(inv, isSent);
            // 告知为异步的结果
            return AsyncRpcResult.newDefaultAsyncResult(invocation);
        } else {
            // 获取真正执行的线程池(ThreadPool中的SPI)
            ExecutorService executor = getCallbackExecutor(getUrl(), inv);
            CompletableFuture<AppResponse> appResponseFuture =
                    currentClient.request(inv, timeout, executor).thenApply(obj -> (AppResponse) obj);
            // save for 2.6.x compatibility, for example, TraceFilter in Zipkin uses com.alibaba.xxx.FutureAdapter
            // 设置完成的额结果信息
            FutureContext.getContext().setCompatibleFuture(appResponseFuture);
            // 创建新的结果信息并且返回
            AsyncRpcResult result = new AsyncRpcResult(appResponseFuture, inv);
            result.setExecutor(executor);
            return result;
        }
    } catch (TimeoutException e) {
        throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Invoke remote method timeout. method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
    } catch (RemotingException e) {
        throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
    }
}

查看 ExchangeClient 接口发现是一个实现了ExchangeChannel接口的空接口

public interface ExchangeClient extends Client, ExchangeChannel {

}

查找具体的实现类 HeaderExchangeClient

private final ExchangeChannel channel;

@Override
public CompletableFuture<Object> request(Object request) throws RemotingException {
    // 交给 ExchangeChannel 调用 request 方法执行
    return channel.request(request);
}

最后会进入 HeaderExchangeChannel 中的 request 方法

public CompletableFuture<Object> request(Object request, int timeout, ExecutorService executor) throws RemotingException {
    if (closed) {
        throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!");
    }
    // create request.
    // 创建一个新的request对象
    Request req = new Request();
    req.setVersion(Version.getProtocolVersion());
    req.setTwoWay(true);
    req.setData(request);
    // 创建一个执行结果的回调信息处理
    DefaultFuture future = DefaultFuture.newFuture(channel, req, timeout, executor);
    try {
        // 交给真正的业务渠道进行处理,这里的渠道是交给Transporter这个SPI进行创建的,其中我们的NettyChannel就是在这里产生的
        channel.send(req);
    } catch (RemotingException e) {
        // 请求出现异常则取消当前的请求封装
        future.cancel();
        throw e;
    }
    return future;
}

Dubbo 原理剖析文章到此就结束了,由于这篇文章太长了,如何手撸一个RPC框架会放在下一篇文章来讲解!