dubbo源码分析(一)

阅读源码的做用

  • 提取设计思路,加强设计能力
  • 理解运行机制,便于快速解决问题以及功能扩展

常见有关dubbo的问题

  • dubbo的负载均衡是在哪一个组件中处理的?
  • dubbo默认的负载均衡算法是什么?
  • 若是注册中心挂掉了客户端是否可以继续调用dubbo?
  • 一个请求从调用端到服务端的处理流程是什么?

若是你有仔细的研读dubbo的开发文档,大部分问题都是有答案的,为了进一步了解细节就有必要对源码进行了解。html

源码分析计划

使用dubbo时间也有快两年时间了,虽然官方的dubbo已经中止维护,但也有其它组织在继续维护:java

  • dubbox,是当当维护的
  • 还有一个是基于jdk8版本的

一直想看看dubbo的源码,但没有集中时间来学习。最近利用了一部分时间因此将个人学习经历记录下来,主要从这几步来展开,只想搞清楚大致流程,对于其它一些细节我目前并不太关注,好比序列化,线程池,集群,上下文,异步回调等。git

  • RPC框架的简易结构
  • dubbo客户端的初始化
  • dubbo服务端的初始化
  • dubbo客户端处理请求流程
  • dubbo服务端处理请求流程

RPC简易结构

为了更加清楚的看清楚RPC的结构,主要看如下几个核心组件便可,其他的组件都是围绕它们来完成。github

  • 序列化组件,由于须要远程调用,因此须要将参数以二进制流的形式进行编码操做
  • 发送组件,将二进制流发送到服务端
  • 接收组件,接收调用端发送的二进制流以及接收服务端返回的二进制流

dubbo客户端的初始化

从这开始来实际看下dubbo的工做流程。RPC的亮点在于将远程调用的细节隐藏起来,使得调用远程服务像调用本地服务同样简单,而实现上面的功能就是代理。web

代理的做用

RPC框架隐藏了具体的实现细节,客户端经过调用特定的代理类来访问远程服务,下面是一个dubbo远程接口的本地引用。算法

<dubbo:reference check="false"
	                  timeout="200000"
	                  interface="com.product.core.service.ProductFacadeService"
	                  id="productFacadeService">

ReferenceConfig这个类的createProxy是用来生成远程服务的本地代理,最终交给RegistryProtocol来处理。两处核心代码:canvas

  • refprotocol.refer,与注册中心相关
  • proxyFactory.getProxy,获取代理
private T createProxy(Map<String, String> map) {
		//...
		
		if (isJvmRefer) {
			//...
		} else {
            //...

            if (urls.size() == 1) {
                invoker = refprotocol.refer(interfaceClass, urls.get(0));
            } else {
               //...
            }
        }

        //...
        // 建立服务代理
        return (T) proxyFactory.getProxy(invoker);
    }

本地直连不走上面的逻辑缓存

注册中心

因为服务注册到ZK,因此调用端要想调用服务端须要取得服务的注册信息而后创建网络链接。ruby

 private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
        RegistryDirectory directory = new RegistryDirectory(type, url);
        directory.setRegistry(registry);
        directory.setProtocol(this.protocol);
        URL subscribeUrl = new URL("consumer", NetUtils.getLocalHost(), 0, type.getName(), directory.getUrl().getParameters());
        if(!"*".equals(url.getServiceInterface()) && url.getParameter("register", true)) {
            registry.register(subscribeUrl.addParameters(new String[]{"category", "consumers", "check", String.valueOf(false)}));
        }

        directory.subscribe(subscribeUrl.addParameter("category", "providers,configurators,routers"));
        return cluster.join(directory);
    }

上面代码两个做用:markdown

  • 将调用端以消费者的身份进行信息注册
  • 监听注册中心的信息变化以刷新本地对服务注册信息的缓存

因为有服务端信息的本地缓存,因此当注册中心挂掉后调用端依然可以工做,也就是说调用端不是强依赖服务端。

链接服务端

上面的subscribe方法会监听注册中心的变化,当获取到服务注册信息后会触发ProtocolListenerWrapper的refer方法:

 public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        return (Invoker)("registry".equals(url.getProtocol())?this.protocol.refer(type, url):

new ListenerInvokerWrapper(this.protocol.refer(type, url),

Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(InvokerListener.class).getActivateExtension(url, "invoker.listener")))); }

上面的this.protocol就是DubboProtocol这个类,此类的其它方法暂时先不关注,只看refer。

public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
    this.optimizeSerialization(url);
    DubboInvoker invoker = new DubboInvoker(serviceType, url, this.getClients(url), this.invokers);
    this.invokers.add(invoker);
    return invoker;
}

getClients负责建立连接以供调用端调用服务端方法时使用。

 private ExchangeClient[] getClients(URL url) {
        boolean service_share_connect = false;
        int connections = url.getParameter("connections", 0);
        if(connections == 0) {
            service_share_connect = true;
            connections = 1;
        }

        ExchangeClient[] clients = new ExchangeClient[connections];

        for(int i = 0; i < clients.length; ++i) {
            if(service_share_connect) {
                clients[i] = this.getSharedClient(url);
            } else {
                clients[i] = this.initClient(url);
            }
        }

        return clients;
    }

建立代理

回到ReferenceConfig的createProxy方法,最后会调用proxyFactory.getProxy,此方法最终会调用JavassistProxyFactory的getProxy,一看javassist就知道是利用字节码来实现代理功能。

 public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

Proxy,这个类的实现比较复杂就不详细分析了,获得了proxy将它放在容器中,当调用端调用服务端代码时就有了实例跟本地的实例没什么区别。

 public static Proxy getProxy(Class... ics) {
        return getProxy(ClassHelper.getCallerClassLoader(Proxy.class), ics);
    }

初始化时序图

下图中的callback是指监听注册中心后回调产生的调用关系。

总结

本文提到了分析框架源码的做用,RPC简单结构,并制定了一个源码分析的计划。最后以调用端在启动时执行的dubbo流程为起点开始了dubbo流程的源码分析。

相关文章
相关标签/搜索