志宇-dubbo源码分析

文档

官方文档
gitbook 文档

dubbo加载配置文件

dubbo-2.6.4.jar 中spring.handlers 文件中指明解析xml的类
文件内容如下

http\://dubbo.apache.org/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler

DubboNamespaceHandler 的init 方法会
创建多个DubboBeanDefinitionParser 在其parse方法中会解析xml
parse方法会将xml中配置的每个对象放在一个BeanDefinition中
在这里插入图片描述
上面代码将xml配置信息解析成BeanDefinition 放到了spring容器中

dubboSPI

目的,用来在启动时候不初始化实现类,再调用时候选择初始化实现类
spi中可以通过spring进行属性自动注入

dubbo服务提供

dubbo提供服务主要有如下步骤
1.将dubbo的配置信息在spring容器中获得,同时校验配置信息
2.配置信息参数校验,根据配置信息创建url对象
-------(服务端和客户端相互调用的主要信息都存储在URL中)
3.创建Invoker对象
-------(Invoker对象使用Javassist对指定接口进行包装,对其进行参数校验 同时调用时才加载实现类)
4.本地注册 (将服务根据名字和Invoker放到一个map中)
5.远程注册
5.1 开启netty服务端
5.2 连接注册中心zk,写入信息
5.3 监听注册中心

1.校验配置信息

dubbo提供服务的入口在ServiceBean类中,先观察下ServiceBean类吧

public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, BeanNameAware {
    
    }

先分析下 InitializingBean 接口的方法 afterPropertiesSet()
这个方法会在项目启动时spring调用初始化方法时候执行

方法中 将spring容器中 ModuleConfig.class、RegistryConfig.class、
MonitorConfig.class、 ProtocolConfig.class 类型的 BeanDefinition 对象全部获得,同时校验对象信息

接下来分析 ApplicationContextAware 接口中的方法 setApplicationContext()
这个方法会在spring初始化回调注入ApplicationContext对象时调用
方法中 通过反射将ServiceBean(自己)添加到了spring的监听器中,
serviceBean是ApplicationListener 接口实现类,也就是一个监听器
部分代码如下

//这样在spring启动完成会触发ApplicationListener 类中的 onApplicationEvent() 
Method method = applicationContext.getClass()
		.getMethod("addApplicationListener", new Class<?>[]{
    
    ApplicationListener.class}); 
method.invoke(applicationContext, new Object[]{
    
    this});

接下来分析ApplicationListener 类中的方法 onApplicationEvent(ContextRefreshedEvent event)
spring初始化完成后会调用下面方法

public void onApplicationEvent(ContextRefreshedEvent event) {
    
    
    //如果是非延迟导出 && !导出过 &&  !不用导出
    if (isDelay() && !isExported() && !isUnexported()) {
    
    
        if (logger.isInfoEnabled()) {
    
    
            logger.info("The service ready on spring started. service: " + getInterface());
        }
        //导出服务  就这一句话实现服务提供
        //接下来分析export()方法吧
        export();
    }
}

2.创建URL

我们通常会做如下配置,这个配置用对应存储到ServiceConfig中

<dubbo:service interface="com.alibaba.dubbo.config.spring.api.DemoService"   ref="demoService" />

接下来继续研究 ServiceBean 类中的 export() 方法

//首先进行一系列校验
//interfaceName 变量是xml 中dubbo:service 中配置的interface属性
//加载下类,判断这个类是否存在
    interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
                        .getContextClassLoader());
    //导出服务
    doExportUrls();

接下来继续研究 doExportUrls()方法

private void doExportUrls() {
    
    
    //获得所有的注册中心地址,dubbo可以配置多个注册中心
    List<URL> registryURLs = loadRegistries(true);
    //每个注册中心可以配置多个协议
    for (ProtocolConfig protocolConfig : protocols) {
    
    
        //根据协议和注册中心 提供服务
        doExportUrlsFor1Protocol(protocolConfig, registryURLs);
    }
}

接下来继续研究 doExportUrlsFor1Protocol(protocolConfig,registryURLs) 方法

//将配置的信息取出来放到一个map中,然后用这个map去创建URL对象
URL url = new URL(name, host, port,provider.getContextpath() + path, map);
//url中的方法 getParameter(key,defaultValue)用来获得map中的信息

这时URL对象就创建好了

3.本地注册

doExportUrlsFor1Protocol() 方法继续往下走

	if (!"remote".equalsIgnoreCase(scope))
 	{
    
    
 	    //本地服务注册
		exportLocal(url);
	}

接下来研究 exportLocal(url)方法

 private void exportLocal(URL url) {
    
    
        if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
    
    
            URL local = URL.valueOf(url.toFullString())
                    .setProtocol(Constants.LOCAL_PROTOCOL) 
                     //Constants.LOCAL_PROTOCOL这个值是injvm,所以会调用InjvmProtocol
                    .setHost(LOCALHOST)
                    .setPort(0);
            ServiceClassHolder.getInstance().pushServiceClass(getServiceClass(ref));
            //在这里会调用Protocol.class 的实现类 InjvmProtocol中的export方法
            Exporter<?> exporter = protocol.export(
                    //这个ref是<dubbo:service> 中的ref 属性(引用一个bean的id名字)
                    //这里会创建一个Invoker
                    proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
            exporters.add(exporter);
            logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");
        }
    }

接下来研究proxyFactory 的 getInvoker(ref,interfaceClass,local) 方法,这个方法会创建一个Invoker对象
因ProxyFactory接口上注解为@SPI(“javassist”),所以会调用JavassistProxyFactory类
getInvoker方法如下

public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
    
    
        // 创建一个包装类,返回的Invoker对象执行所有的方法都调用包装类中的invokeMethod方法
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
        return new AbstractProxyInvoker<T>(proxy, type, url) {
    
    
            @Override
            protected Object doInvoke(T proxy, String methodName,
                                      Class<?>[] parameterTypes,
                                      Object[] arguments) throws Throwable {
    
    
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }

Wrapper 对象是怎么创建的呢,咱们接着分析 Wrapper.getWrapper(ref)

public static Wrapper getWrapper(Class<?> c) {
    
    
    while (ClassGenerator.isDynamicClass(c)) 
        c = c.getSuperclass();

    //如果是object类型直接返回OBJECT_WRAPPER一个简单的对象
    if (c == Object.class)
        return OBJECT_WRAPPER;
    //看缓存中有没有
    Wrapper ret = WRAPPER_MAP.get(c);
    if (ret == null) {
    
    
        //缓存中没有则直接创建这个ref类的代理对象
        ret = makeWrapper(c);
        //将对象放到缓存
        WRAPPER_MAP.put(c, ret);
    }
    return ret;
}

接下来我们分析 makeWrapper(ref) 方法,它到底是如何创建Wrapper 对象的呢

//这个类中的代码很多,它主要创建一个对象的代理,这个代理对象会根据被调用时根据参数加载要调用的实现类
//首先将所有的字段都创建
	   for (Field f : c.getFields()) {
    
    
            String fn = f.getName();
            Class<?> ft = f.getType();
            if (Modifier.isStatic(f.getModifiers()) || Modifier.isTransient(f.getModifiers()))
                continue;
            c1.append(" if( $2.equals(\"").append(fn).append("\") ){ w.").append(fn).append("=").append(arg(ft, "$3")).append("; return; }");
            c2.append(" if( $2.equals(\"").append(fn).append("\") ){ return ($w)w.").append(fn).append("; }");
            pts.put(fn, ft);
        }
//然后将所有的方法都一次创建
 			for (Method m : methods) {
    
    
            if (m.getDeclaringClass() == Object.class) //ignore Object's method.
                continue;

            String mn = m.getName();
            c3.append(" if( \"").append(mn).append("\".equals( $2 ) ");
            int len = m.getParameterTypes().length;
            c3.append(" && ").append(" $3.length == ").append(len);

            boolean override = false;
            for (Method m2 : methods) {
    
    
                if (m != m2 && m.getName().equals(m2.getName())) {
    
    
                    override = true;
                    break;
                }
            }
            if (override) {
    
    
                if (len > 0) {
    
    
                    for (int l = 0; l < len; l++) {
    
    
                        c3.append(" && ").append(" $3[").append(l).append("].getName().equals(\"")
                                .append(m.getParameterTypes()[l].getName()).append("\")");
                    }
                }
            }

            c3.append(" ) { ");

            if (m.getReturnType() == Void.TYPE)
                c3.append(" w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");").append(" return null;");
            else
                c3.append(" return ($w)w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");");

            c3.append(" }");

            mns.add(mn);
            if (m.getDeclaringClass() == c)
                dmns.add(mn);
            ms.put(ReflectUtils.getDesc(m), m);
        }
//get set  is  开头方法的通过正则匹配再进行处理
			for (Map.Entry<String, Method> entry : ms.entrySet()) {
    
    
            String md = entry.getKey();
            Method method = (Method) entry.getValue();
            if ((matcher = ReflectUtils.GETTER_METHOD_DESC_PATTERN.matcher(md)).matches()) {
    
    
                String pn = propertyName(matcher.group(1));
                c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");
                pts.put(pn, method.getReturnType());
            } else if ((matcher = ReflectUtils.IS_HAS_CAN_METHOD_DESC_PATTERN.matcher(md)).matches()) {
    
    
                String pn = propertyName(matcher.group(1));
                c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");
                pts.put(pn, method.getReturnType());
            } else if ((matcher = ReflectUtils.SETTER_METHOD_DESC_PATTERN.matcher(md)).matches()) {
    
    
                Class<?> pt = method.getParameterTypes()[0];
                String pn = propertyName(matcher.group(1));
                c1.append(" if( $2.equals(\"").append(pn).append("\") ){ w.").append(method.getName()).append("(").append(arg(pt, "$3")).append("); return; }");
                pts.put(pn, pt);
            }
        }
 //根据信息生成class文件
  			 Class<?> wc = cc.toClass();
            // setup static field.
            wc.getField("pts").set(null, pts);
            wc.getField("pns").set(null, pts.keySet().toArray(new String[0]));
            wc.getField("mns").set(null, mns.toArray(new String[0]));
            wc.getField("dmns").set(null, dmns.toArray(new String[0]));
            int ix = 0;
            for (Method m : ms.values())
                wc.getField("mts" + ix++).set(null, m.getParameterTypes());
            return (Wrapper) wc.newInstance();

这时Invoke 对象创建好了,这个对象会对传入对象进行包装,解决调用时才加载实现类
因为是本地注册服务,所以该将创建的对象存起来了
接下来研究 exportLocal(url)方法中 InjvmProtocol 类执的 expor 方法

 public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    
    
        return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap);
    }
	//InjvmExporter的构造方法如下,可以看到就是将Invoker放到了exporterMap中    
	InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {
    
    
	        super(invoker);
	        this.key = key;
	        this.exporterMap = exporterMap;
	        //存储invoker 
	        exporterMap.put(key, this);
	 }

4.远程注册

接着研究doExportUrlsFor1Protocol(protocolConfig,registryURLs)的后续代码
代码如下

  if (registryURLs != null && !registryURLs.isEmpty()) {
    
    
        //循环遍历所有注册中心的地址
        for (URL registryURL : registryURLs) {
    
    
      //将URL中的protocol 替换成registry 为了后面 protocol.export()调用RegistryProtocol实现类
                URL monitorUrl = loadMonitor(registryURL);
               //创建远程提供服务的invoker
               //这里逻辑和本地导出创建invoker的逻辑相同,都是调用JavassistProxyFactory中的getInvoker()
               Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
               //对invoker和serviceConfig 对象进行包装
               DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
			  //通过invoker对象进行提供服务
              Exporter<?> exporter = protocol.export(wrapperInvoker);
              //添加到缓存
              exporters.add(exporter);
             }
   }

4.1 开启netty服务端

接下来研究 protocol.export(wrapperInvoker) 方法
首先protocol是怎么创建的呢

private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

接下来我们研究下 getAdaptiveExtension() 方法
这个方法会动态创建 Protocol的代理类,代理类代码如下

  public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
    
    
        public void destroy() {
    
    
            throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
        }

        public int getDefaultPort() {
    
    
            throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
        }

        public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
    
    
            if (arg1 == null) throw new IllegalArgumentException("url == null");
            com.alibaba.dubbo.common.URL url = arg1;
            String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
            if (extName == null)
                throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
            com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
            return extension.refer(arg0, arg1);
        }

        public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
    
    
            if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
            if (arg0.getUrl() == null)
                throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
            com.alibaba.dubbo.common.URL url = arg0.getUrl();
            String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
            if (extName == null)
                throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
            com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
            return extension.export(arg0);
        }
    }

根据上面代码得出是根据传入参数 url 中的 protocol 参数调用指定的实现类
这时 protocol 的参数是 registry 所以调用 RegistryProtocol 类中的 export() 方法
接下来分析export()中开启Netty服务的代码

 public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
    
    
        //开启netty服务
        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
    }

接下来分析 doLocalExport(originInvoker) 方法,代码如下

String key = getCacheKey(originInvoker);
        ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
        if (exporter == null) {
    
    
            synchronized (bounds) {
    
    
                exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
                if (exporter == null) {
    
    
                    final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));
                    exporter = new ExporterChangeableWrapper<T>((Exporter<T>) protocol.export(invokerDelegete), originInvoker);
                    bounds.put(key, exporter);
                }
            }
        }
        return exporter;

上面代码最主要就 protocol.export(invokerDelegete) 方法
接下来分析这个方法
首先这个 protocol.export() 会调用哪个实现类,这里protocol对象是这个类的成员变量
这个类的实现类一般是 DubboProtocol 那么接下来看 DubboProtocol 的方法 exprot()

 public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    
    
        URL url = invoker.getUrl();

        String key = serviceKey(url);
        DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
        exporterMap.put(key, exporter);

        Boolean isStubSupportEvent = url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT);
        Boolean isCallbackservice = url.getParameter(Constants.IS_CALLBACK_SERVICE, false);
        if (isStubSupportEvent && !isCallbackservice) {
    
    
            String stubServiceMethods = url.getParameter(Constants.STUB_EVENT_METHODS_KEY);
            if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
    
    
                if (logger.isWarnEnabled()) {
    
    
                    logger.warn(new IllegalStateException("consumer [" + url.getParameter(Constants.INTERFACE_KEY) +
                            "], has set stubproxy support event ,but no stub methods founded."));
                }
            } else {
    
    
                stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
            }
        }
		//这个方法最主要,用来开启Netty服务
        openServer(url);
        optimizeSerialization(url);
        return exporter;
    }

接下来分析 openServer(url) 方法
代码如下

private void openServer(URL url) {
    
    
        // find server.
        String key = url.getAddress();
        //client can export a service which's only for server to invoke
        boolean isServer = url.getParameter(Constants.IS_SERVER_KEY, true);
        if (isServer) {
    
    
            //判断缓存中有没有  没有则开服务
            ExchangeServer server = serverMap.get(key);
            if (server == null) {
    
    
                serverMap.put(key, createServer(url));
            } else {
    
    
                // server supports reset, use together with override
                server.reset(url);
            }
        }
    }

接下分析 createServer(url) 方法
主要代码如下

   try {
    
    
      //这里传入了个requestHandler对象 这个对象是是在DubboProtocol声明的 ExchangeHandlerAdapter(){}
      //这个对象在最后会用来处理请求服务  先记下这个类
      server = Exchangers.bind(url, requestHandler);
  } catch (RemotingException e) {
    
    
       throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
 }

接下来就分析 Exchangers.bind(url, requestHandler); 方法吧
调用过程就不细说了,最后会调用 HeaderExchanger 类的 bind方法
代码如下

public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
    
    
        //在这里对刚才传入的handler进行了两次包装
        //那么现在的handler包装成什么样子了呢
        //new DecodeHandler(new HeaderExchangeHandler(new ExchangeHandlerAdapter()))
        //要记下这个handler,因这个handler最后会处理请求服务
        return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}

接下来分析 Transporters.bind(url,handler) 方法,经过spi调用会调用到实现类NettyTransporter
代码如下,我们分析Netty3.x版本的吧

public Server bind(URL url, ChannelHandler listener) throws RemotingException {
    
    
    //经过复杂的调用终于看到了开启Netty的代码
    return new NettyServer(url, listener);
}

接下来我们看下 NettyServer(url, listener); 都做了什么

public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
    
    
    //这里对handler进行了包装
    //包装成了如下类型 
    //new MultiMessageHandler(new HeartbeatHandler(
    //new AllChannelHandler(new DecodeHandler(
    //new HeaderExchangeHandler(new ExchangeHandlerAdapter())))))
    super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
}

接下来分析 new NettyServer(url, listener);的父类构造方法做了什么
跳过简单调用,直接看主要代码

protected void doOpen() throws Throwable {
    
    
    NettyHelper.setNettyLoggerFactory();
    //创建boss线程池
    ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true));
    //创建worker线程池
    ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true));
    //创建工厂
    ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS));
    //创建核心配置类
    bootstrap = new ServerBootstrap(channelFactory);
    
    //创建一个hadlder,对NettyServer进行包装,NettyServer的父类成员变量中存储着传入的handler
    final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
    channels = nettyHandler.getChannels();
  
    bootstrap.setOption("child.tcpNoDelay", true);
    bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
    
    
        @Override
        public ChannelPipeline getPipeline() {
    
    
            NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
            ChannelPipeline pipeline = Channels.pipeline();
            //当超时执行
            /*int idleTimeout = getIdleTimeout();
            if (idleTimeout > 10000) {
                pipeline.addLast("timer", new IdleStateHandler(timer, idleTimeout / 1000, 0, 0));
            }*/
            pipeline.addLast("decoder", adapter.getDecoder());
            pipeline.addLast("encoder", adapter.getEncoder());
            pipeline.addLast("handler", nettyHandler);
            return pipeline;
        }
    });
    // bind
    channel = bootstrap.bind(getBindAddress());
}

这样就开启NettyServer 服务端,那么服务端是如何处理请求的呢
我们先将处理请求的类记下,用于后续分析
adapter.getDecoder() //编码器 使用 NettyCodecAdapter 类
adapter.getEncoder() //解码器 使用 NettyCodecAdapter 类
nettyHandler //处理请求的handler 使用如下类进行了包装
new NettyHandler(new MultiMessageHandler(new HeartbeatHandler(new AllChannelHandler(new DecodeHandler(new HeaderExchangeHandler(new ExchangeHandlerAdapter()))))))

4.2 连接注册中心zk,写入信息

接下来接着分析RegistryProtocol 类的 export(Invoker) 方法
export部分代码如下

//获得注册中心地址
URL registryUrl = getRegistryUrl(originInvoker);
//在这里根据注册中心地址链接注册中心
final Registry registry = getRegistry(originInvoker);
//移除一些参数
final URL registeredProviderUrl = getRegisteredProviderUrl(originInvoker);
//to judge to delay publish whether or not
boolean register = registeredProviderUrl.getParameter("register", true);
//包装下 然后存起来将 invoker
ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registeredProviderUrl);

if (register) {
    
    
    //执行注册
    register(registryUrl, registeredProviderUrl);
    ProviderConsumerRegTable.getProviderWrapper(originInvoker).setReg(true);
}

我们先分析下getRegistry(originInvoker);方法
然后再分析register(registryUrl, registeredProviderUrl);方法
在 getRegistry(originInvoker); 方法中 如果注册中心以zk为例子,跳过调用代码最后会调用到CuratorZookeeperClient 类的CuratorZookeeperClient(URL url)方法,
下面代码为连接zk,代码如下

 public CuratorZookeeperClient(URL url) {
    
    
        super(url);
        try {
    
    
            //连接zk服务端
            CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder()
                    .connectString(url.getBackupAddress())
                    .retryPolicy(new RetryNTimes(1, 1000))
                    .connectionTimeoutMs(5000);
            String authority = url.getAuthority();
            if (authority != null && authority.length() > 0) {
    
    
                builder = builder.authorization("digest", authority.getBytes());
            }
            client = builder.build();
            client.getConnectionStateListenable().addListener(new ConnectionStateListener() {
    
    
                @Override
                public void stateChanged(CuratorFramework client, ConnectionState state) {
    
    
                    if (state == ConnectionState.LOST) {
    
    
                        CuratorZookeeperClient.this.stateChanged(StateListener.DISCONNECTED);
                    } else if (state == ConnectionState.CONNECTED) {
    
    
                        CuratorZookeeperClient.this.stateChanged(StateListener.CONNECTED);
                    } else if (state == ConnectionState.RECONNECTED) {
    
    
                        CuratorZookeeperClient.this.stateChanged(StateListener.RECONNECTED);
                    }
                }
            });
            //连接
            client.start();
        } catch (Exception e) {
    
    
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

接下来分析 register(registryUrl, registeredProviderUrl);方法
去掉调用代码,代码如下

public void register(URL url) {
    
    
        super.register(url);
        failedRegistered.remove(url);
        failedUnregistered.remove(url);
        try {
    
    
            // Sending a registration request to the server side
            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)
                    && !Constants.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);
            }

            // Record a failed registration request to a failed list, retry regularly
            failedRegistered.add(url);
        }
    }

接下来我们分析 doRegister(url) 方法
代码如下,下面代码会在zk中创建一个持久化节点

@Override
    protected void doRegister(URL url) {
    
    
        try {
    
    
            zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true));
        } catch (Throwable e) {
    
    
            throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

4.3监听注册中心

接下来接着分析RegistryProtocol 类的 export(Invoker) 方法,export部分代码如下

  //创建url
  final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registeredProviderUrl);
  //创建一个监听器
  final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
  //放到缓存中
  overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
  //订阅消息
  registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);

dubbo引用服务

1.引用服务入口

服务引用要在xml中配置如下信息

<!-- 增加引用远程服务配置 -->
<dubbo:reference id=“xxxService” interface=“com.xxx.XxxService” />
<!-- 和本地服务一样使用远程服务 -->
<bean id=“xxxAction” class=“com.xxx.XxxAction”> 
    <property name=“xxxService” ref=“xxxService” />
</bean>

上面的配置信息会被解析到 ReferenceConfig 类中
服务引用的入口类是 ReferenceBean,接下来我们分析它,先看下它的实现

public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean {
    
    }

先看 InitializingBean 接口的 afterPropertiesSet() 方法
方法中获得配置信息存储到 ReferenceConfig 的成员变量中
如果不是懒加载则直接调用getObject()方法创建对象

      Map<String, ConsumerConfig> consumerConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ConsumerConfig.class, false, false);
         for (ConsumerConfig config : consumerConfigMap.values()) {
    
    
              setConsumer(consumerConfig);
         }

      Map<String, ApplicationConfig> applicationConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class, false, false);
         for (ApplicationConfig config : applicationConfigMap.values()) {
    
    
              setApplication(applicationConfig);
         }
      Map<String, ModuleConfig> moduleConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ModuleConfig.class, false, false);
         for (ModuleConfig config : moduleConfigMap.values()) {
    
    
              setModule(moduleConfig);
         }
      Map<String, RegistryConfig> registryConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class, false, false);
         for (RegistryConfig config : registryConfigMap.values()) {
    
    
              if (config.isDefault() == null || config.isDefault().booleanValue()) {
    
    
                  registryConfigs.add(config);
              }
         }
         if (registryConfigs != null && !registryConfigs.isEmpty()) {
    
    
              super.setRegistries(registryConfigs);
         }
      Map<String, MonitorConfig> monitorConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class, false, false);
         for (MonitorConfig config : monitorConfigMap.values()) {
    
    
              setMonitor(monitorConfig);
         }
         Boolean b = isInit();
        if (b == null && getConsumer() != null) {
    
    
              b = getConsumer().isInit();
        }
        if (b != null && b.booleanValue()) {
    
    
              getObject();
        }

接下来看 FactoryBean 的接口实现,既然是FactoryBean类型那么就看下它能创建什么对象吧
代码如下

    @Override
    public Object getObject() throws Exception {
    
    
        return get();
    }

    //返回创建对象的类型
    @Override
    public Class<?> getObjectType() {
    
    
        //这个对象的类型是ReferenceConfig 类中的 interfaceClass
        //<dubbo:reference id=“xxxService” interface=“com.xxx.XxxService” />
        //也就是xml中 dubbo:reference  的interface属性的值
        return getInterfaceClass();
    }

    @Override
    @Parameter(excluded = true)
    //创建的对象是否是单例的
    public boolean isSingleton() {
    
    
        return true;
    }

接下来分析 get() 吧
代码如下

public synchronized T get() {
    
    
    if (destroyed) {
    
    
        throw new IllegalStateException("Already destroyed!");
    }
    //这个ref 就是要返回的对象
    if (ref == null) {
    
    
        //这个对象没有就去创建它
        init();
    }
    return ref;
}

接下来分析 init() 方法吧
主要代码如下

    // 从系统变量中获取与接口名对应的属性值
    String resolve = System.getProperty(interfaceName);
    String resolveFile = null;
    if (resolve == null || resolve.length() == 0) {
    
    
        // 从系统属性中获取解析文件路径
        resolveFile = System.getProperty("dubbo.resolve.file");
        if (resolveFile == null || resolveFile.length() == 0) {
    
    
            // 从指定位置加载配置文件
            File userResolveFile = new File(new File(System.getProperty("user.home")), "dubbo-resolve.properties");
            if (userResolveFile.exists()) {
    
    
                // 获取文件绝对路径
                resolveFile = userResolveFile.getAbsolutePath();
            }
        }
        if (resolveFile != null && resolveFile.length() > 0) {
    
    
            Properties properties = new Properties();
            FileInputStream fis = null;
            try {
    
    
                fis = new FileInputStream(new File(resolveFile));
                // 从文件中加载配置
                properties.load(fis);
            } catch (IOException e) {
    
    
                throw new IllegalStateException("Unload ..., cause:...");
            } finally {
    
    
                try {
    
    
                    if (null != fis) fis.close();
                } catch (IOException e) {
    
    
                    logger.warn(e.getMessage(), e);
                }
            }
            // 获取与接口名对应的配置
            resolve = properties.getProperty(interfaceName);
        }
    }
    
	//创建interfaceClass接口对应的 invoker 对象缓存起来
	String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
	//构建map 一系列参数存储其中
	appendParameters(map, application);
	appendParameters(map, module);
	appendParameters(map, consumer, Constants.DEFAULT_KEY);
	appendParameters(map, this);
	//通过map创建 最终想要的对象
	ref = createProxy(map);

上面代码加载文件是为了
通过系统变量或 dubbo.properties 配置文件填充 ConsumerConfig 的字段

2.服务引用的invoker创建

接下来分析createProxy(map); 中的方法
代码如下(省略非主要代码)

private T createProxy(Map<String, String> map) {
    
    
     // 如果用户显式配置了 scope=local,此时 isInjvmRefer 返回 true
    boolean isJvmRefer=InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl);
    //本地服务引用
    if (isJvmRefer) {
    
    
        //Constants.LOCAL_PROTOCOL的值是injvm
        //所以后面refprotocol.refer(interfaceClass, url) 调用的是InjvmProtocol类
        URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
        //创建调用本地服务的invoker
        //调用 InjvmProtocol 的refer方法
        invoker = refprotocol.refer(interfaceClass, url);
    } else {
    
    
       // url 不为空,表明用户可能想进行点对点调用
	   if (url != null && url.length() > 0) {
    
     // user specified URL, could be peer-to-peer address, or register center's address.
             String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url);
             if (us != null && us.length > 0) {
    
    
                    for (String u : us) {
    
    
                        URL url = URL.valueOf(u);
                        if (url.getPath() == null || url.getPath().length() == 0) {
    
    
                            url = url.setPath(interfaceName);
                        }
                        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
    
    
                            urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
                        } else {
    
    
                            urls.add(ClusterUtils.mergeUrl(url, map));
                        }
                    }
                }
            } else {
    
     
             //加载注册中心
             //同时设置Registry为protocol 用于后面调用refprotocol.refer()的实现类是RegistryProtocol 
                List<URL> us = loadRegistries(false);
                if (us != null && !us.isEmpty()) {
    
    
                    for (URL u : us) {
    
    
                        URL monitorUrl = loadMonitor(u);
                        if (monitorUrl != null) {
    
    
                            map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
                        }
                        urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
                    }
                }
       }
		
        // 生产者的服务提供者只有一个
        if (urls.size() == 1) {
    
    
            //创建调用远程服务的invoker,invoker类型为dubboinvoker
            // 调用 RegistryProtocol 的 refer 构建 Invoker 实例
            invoker = refprotocol.refer(interfaceClass, urls.get(0));
        } else {
    
    
            // 多个服务提供者,存到list中
            List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
            URL registryURL = null;
            for (URL url : urls) {
    
    
                invokers.add(refprotocol.refer(interfaceClass, url));
                if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
    
    
                    registryURL = url; // use last registry url
                }
            }
            if (registryURL != null) {
    
     
                URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
                //根据集群管理选择一个一个dubboinvoker
                invoker = cluster.join(new StaticDirectory(u, invokers));
            } else {
    
     // not a registry url
                invoker = cluster.join(new StaticDirectory(invokers));
            }
        }
    }
    // create service proxy
    //这里通过invoker创建代理对象
    return (T) proxyFactory.getProxy(invoker);
}

2.1本地服务引用invoker创建

我们先看一下 InjvmProtocol 中的 refprotocol.refer(interfaceClass, url)方法
代码如下

public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
    
    
    return new InjvmInvoker<T>(serviceType, url, url.getServiceKey(), exporterMap);
}

直接创建 InjvmInvoker 对象,挺简单的

2.2远程服务引用invoker创建

DubboProtocol 中的 refprotocol.refer(interfaceClass, urls) 方法

接下来我们来看DubboProtocol 中的 refprotocol.refer(interfaceClass, urls) 方法
代码如下

public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
    
    
    optimizeSerialization(url);
    //创建一个DubboInvoker类型的 invoker,这个对象的invoke 方法会向netty服务端发送调用信息
    DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
    invokers.add(invoker);
    return invoker;
}

下面最主要的方法是getClients(url),代码如下

private ExchangeClient[] getClients(URL url) {
    
    
        // whether to share connection
        boolean service_share_connect = false;
        int connections = url.getParameter(Constants.CONNECTIONS_KEY, 0);
        // if not configured, connection is shared, otherwise, one connection for one service
        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] = getSharedClient(url);
            } else {
    
    
                clients[i] = initClient(url);
            }
        }
        return clients;
    }

接下来分析getSharedClient(url); 方法
代码如下

private ExchangeClient getSharedClient(URL url) {
    
    
        String key = url.getAddress();
        ReferenceCountExchangeClient client = referenceClientMap.get(key);
        if (client != null) {
    
    
            if (!client.isClosed()) {
    
    
                client.incrementAndGetCount();
                return client;
            } else {
    
    
                referenceClientMap.remove(key);
            }
        }

        locks.putIfAbsent(key, new Object());
        synchronized (locks.get(key)) {
    
    
            if (referenceClientMap.containsKey(key)) {
    
    
                return referenceClientMap.get(key);
            }
			//初始化客户端
            ExchangeClient exchangeClient = initClient(url);
            client = new ReferenceCountExchangeClient(exchangeClient, ghostClientMap);
            referenceClientMap.put(key, client);
            ghostClientMap.remove(key);
            locks.remove(key);
            return client;
        }
    }

接下来分析 initClient(url) 方法
代码如下

   if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)) {
    
    
        client = new LazyConnectExchangeClient(url, requestHandler);
   } else {
    
    
       //这里的 requestHandler是 DubboProtocol类中的 ExchangeHandlerAdapter()
       //这个handler用来调用服务
        client = Exchangers.connect(url, requestHandler);
   }

接下来研究 Exchangers.connect(url, requestHandler) 方法,
跳过调用代码,直接进入HeaderExchanger 类的connect(url,hanlder)方法

  public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
    
    
        //这时handler被包装成
        //new DecodeHandler(new HeaderExchangeHandler(new ExchangeHandlerAdapter()))
        return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true);
 }

接下来分析Transporters.connect(url,hanlder)方法
跳过调用代码进入到 NettyTransporter 类的connect(url, listener)方法
代码如下

public Client connect(URL url, ChannelHandler listener) throws RemotingException {
    
    
    return new NettyClient(url, listener);
}

接下来new NettyClient(url, listener)都做了什么
代码如下

 public NettyClient(final URL url, final ChannelHandler handler) throws RemotingException {
    
    
       //对handler进行了包装
        super(url, wrapChannelHandler(url, handler));
    }

我们先看下wrapChannelHandler(url,handler) 对handler做了哪些包装,一会再看 NettyClient 父类的构造方法,wrapChannelHandler(url,handler) 代码如下

protected static ChannelHandler wrapChannelHandler(URL url, ChannelHandler handler) {
    
    
    url = ExecutorUtil.setThreadName(url, CLIENT_THREAD_POOL_NAME);
    url = url.addParameterIfAbsent(Constants.THREADPOOL_KEY, Constants.DEFAULT_CLIENT_THREADPOOL);
    return ChannelHandlers.wrap(handler, url);
}

接下来分析ChannelHandlers.wrap(handler, url)方法
代码如下

public class ChannelHandlers {
    
    

    private static ChannelHandlers INSTANCE = new ChannelHandlers();

    public static ChannelHandler wrap(ChannelHandler handler, URL url) {
    
    
        return ChannelHandlers.getInstance().wrapInternal(handler, url);
    }

    protected static ChannelHandlers getInstance() {
    
    
        return INSTANCE;
    }
    protected ChannelHandler wrapInternal(ChannelHandler handler, URL url) {
    
    
        //ExtensionLoader.getExtensionLoader(Dispatcher.class)
        //.getAdaptiveExtension().dispatch(handler, url)的实现类暂且认为是AllDispatcher
        //AllDispatcher 中使用 new AllChannelHandler(handler)对handler进行了包装
        //这时handler已经被包装成了这样
        //new MultiMessageHandler(new HeartbeatHandler(new AllChannelHandler(
        //new DecodeHandler(new HeaderExchangeHandler(new ExchangeHandlerAdapter())))))
        return new MultiMessageHandler(new HeartbeatHandler(ExtensionLoader.getExtensionLoader(Dispatcher.class)
                .getAdaptiveExtension().dispatch(handler, url)));
    }

现在的handler类型就是这样的
new MultiMessageHandler(new HeartbeatHandler(new AllChannelHandler(new DecodeHandler(new HeaderExchangeHandler(new ExchangeHandlerAdapter())))))
看上去比较恶心,但是他就是要用这样的handler,我们要记住他,以后分析

看完handler的包装再看NettyClient 父类的构造方法吧
主要代码如下

        try {
    
    
            //创建bootstrap配置信息
            doOpen();
        } catch (Throwable t) {
    
    
            close();
        }
        try {
    
    
            // 根据创建的bootstrap配置信息来进行连接
            connect();
        } catch (RemotingException t) {
    
    
            if (url.getParameter(Constants.CHECK_KEY, true)) {
    
    
                close();
                throw t;
            }
 executor = (ExecutorService) ExtensionLoader.getExtensionLoader(DataStore.class)
                .getDefaultExtension().get(Constants.CONSUMER_SIDE, Integer.toString(url.getPort()));
 ExtensionLoader.getExtensionLoader(DataStore.class)
                .getDefaultExtension().remove(Constants.CONSUMER_SIDE, Integer.toString(url.getPort()));

下面来看下doOpen()方法

 protected void doOpen() throws Throwable {
    
    
        NettyHelper.setNettyLoggerFactory();
        bootstrap = new ClientBootstrap(channelFactory);
        // config
        // @see org.jboss.netty.channel.socket.SocketChannelConfig
        bootstrap.setOption("keepAlive", true);
        bootstrap.setOption("tcpNoDelay", true);
        bootstrap.setOption("connectTimeoutMillis", getTimeout());
        //再一次对handler进行包装现在handler包装的信息如下
        
        //new NettyHandler(new MultiMessageHandler(new HeartbeatHandler(new  
        //AllChannelHandler(new DecodeHandler(new 
        //HeaderExchangeHandler(new ExchangeHandlerAdapter())))))
        final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
    
    
            @Override
            public ChannelPipeline getPipeline() {
    
    
                NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);
                ChannelPipeline pipeline = Channels.pipeline();
                pipeline.addLast("decoder", adapter.getDecoder());
                pipeline.addLast("encoder", adapter.getEncoder());
                pipeline.addLast("handler", nettyHandler);
                return pipeline;
            }
        });
    }

上面代码只创建对象不进行连接
下面来看下connect()方法,代码如下

 protected void doConnect() throws Throwable {
    
    
        long start = System.currentTimeMillis();
        //连接服务端
        ChannelFuture future = bootstrap.connect(getConnectAddress());
        try {
    
    
            boolean ret = future.awaitUninterruptibly(getConnectTimeout(), TimeUnit.MILLISECONDS);
            if (ret && future.isSuccess()) {
    
    
                Channel newChannel = future.getChannel();
                newChannel.setInterestOps(Channel.OP_READ_WRITE);
                try {
    
    
                    // Close old channel
                    Channel oldChannel = NettyClient.this.channel; // copy reference
                    if (oldChannel != null) {
    
    
                        try {
    
    
                            oldChannel.close();
                        } finally {
    
    
                            NettyChannel.removeChannelIfDisconnected(oldChannel);
                        }
                    }
                } finally {
    
    
                    if (NettyClient.this.isClosed()) {
    
    
                        try {
    
    
                            }
                            newChannel.close();
                        } finally {
    
    
                            NettyClient.this.channel = null;
                            NettyChannel.removeChannelIfDisconnected(newChannel);
                        }
                    } else {
    
    
                        NettyClient.this.channel = newChannel;
                    }
                }
            } 
        } finally {
    
    
            if (!isConnected()) {
    
    
                future.cancel();
            }
        }
    }

这样客户端的服务器起来了
我们要记住客户端的handler包装信息(和服务端的一样)有
new NettyHandler(new MultiMessageHandler(new HeartbeatHandler(new AllChannelHandler(new DecodeHandler(new HeaderExchangeHandler(new ExchangeHandlerAdapter())))))

RegistryProtocol 中的 refprotocol.refer(interfaceClass, urls) 方法

接下来分析 RegistryProtocol 类的 refer 方法
代码如下

  public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
    
    
  
        url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
        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(Constants.REFER_KEY));
        String group = qs.get(Constants.GROUP_KEY);
        if (group != null && group.length() > 0) {
    
    
            if ((Constants.COMMA_SPLIT_PATTERN.split(group)).length > 1
                    || "*".equals(group)) {
    
    
                return doRefer(getMergeableCluster(), registry, type, url);
            }
        }
        return doRefer(cluster, registry, type, url);
    }

接下来看doRefer方法

private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
    
    
    // 创建 RegistryDirectory 实例
    RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
    // 设置注册中心和协议
    directory.setRegistry(registry);
    directory.setProtocol(protocol);
    Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
    // 生成url
    URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);

    // 注册服务消费者,在 consumers 目录下新节点
    if (!Constants.ANY_VALUE.equals(url.getServiceInterface())
            && url.getParameter(Constants.REGISTER_KEY, true)) {
    
    
        registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
                Constants.CHECK_KEY, String.valueOf(false)));
    }

    // 订阅 providers、configurators、routers 等节点数据
    directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
            Constants.PROVIDERS_CATEGORY
                    + "," + Constants.CONFIGURATORS_CATEGORY
                    + "," + Constants.ROUTERS_CATEGORY));

    // 一个注册中心可能有多个服务提供者,因此这里需要将多个服务提供者合并为一个
    Invoker invoker = cluster.join(directory);
    ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
    return invoker;
}

如上,doRefer 方法创建一个 RegistryDirectory 实例(RegistryDirectory 会同步生产者的服务信息,根据可以调用的服务创建对应的DubboInvoker对象存储到RegistryDirectory 中),然后生成消费者url,并向注册中心进行注册。注册完毕后,紧接着订阅 providers、configurators、routers 等节点下的数据。
由于一个服务可能部署在多台服务器上,这样生产者就会在注册中心上注册多个服务,这个时候就需要 Cluster 在多个服务中选择一个invoker(负载均衡)返回

3.通过invoker 创建代理对象

接下来研究 ReferenceConfig 的 proxyFactory.getProxy(invoker) 方法,就是对invoker进行下代理
接下来看JavassistProxyFactory的 getProxy 方法

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

最后生成的代理类是这样的

package org.apache.dubbo.common.bytecode;

public class proxy0 implements org.apache.dubbo.demo.DemoService {
    
    

    public static java.lang.reflect.Method[] methods;

    private java.lang.reflect.InvocationHandler handler;

    public proxy0() {
    
    
    }

    public proxy0(java.lang.reflect.InvocationHandler arg0) {
    
    
        handler = $1;
    }

    public java.lang.String sayHello(java.lang.String arg0) {
    
    
        Object[] args = new Object[1];
        args[0] = ($w) $1;
        Object ret = handler.invoke(this, methods[0], args);
        return (java.lang.String) ret;
    }
}

集群容错

官方文档上说集群容错分为
服务目录 Directory、服务路由 Router、集群 Cluster、负载均衡 LoadBalance

服务目录是什么: 生产者启动时会将可以调用的服务存储到zk上。消费者启动后拉取zk上可以调用的服务,根据每个服务信息创建一个Invoker对象,将所有的invoker存储到服务目录 Directory中,Directory中存储的invoker信息会随着生产者提供服务信息变化。

服务路由 Router的作用: 服务路由规定了服务消费者可调用哪些服务提供者。
服务路由包含一条路由规则,路由规则决定了服务消费者的调用目标,条件路由规则信息如下

host = 10.20.153.10 => host = 10.20.153.11
[服务消费者ip] => [服务提供者ip]

说明服务消费者ip 可以调用此服务提供者的ip

集群 Cluste: 在dubbo中 Cluste 用途是将多个服务提供者合并为一个 Cluster Invoker,并将这个 Invoker 暴露给服务消费者。这样一来,服务消费者只需通过这个 Invoker 进行远程调用即可,至于具体调用哪个服务提供者,以及调用失败后如何处理打印日志还是抛异常),现在都交给集群模块去处理

负载均衡 LoadBalance:调用时在多个invoker中通过权重和策略的参数选择一个invoker

服务目录 Directory

通常使用RegistryDirectory,类图如下

在这里插入图片描述
Directory 继承自 Node 接口,这个接口包含了一个获取配置信息的方法 getUrl,实现该接口的类可以向外提供配置信息。
AbstractDirectory 实现了 Directory 接口,这个接口包含了一个重要的方法定义,list(Invocation),用于获取所有 Invoke对象,这里的invoke对象可以调用生产者的服务。
RegistryDirectory 实现了 NotifyListener 接口,当注册中心节点信息发生变化后,可以通过此接口方法得到变更invoke信息

RegistryDirectory是在哪里初始化的呢,肯定是消费者使用到它了
在RegistryProtocol 的 doRefer 方法进行的初始化

 private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
    
    
        //在这里进行的初始化
        RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
        directory.setRegistry(registry);
        directory.setProtocol(protocol);
        Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
        URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);
        if (!Constants.ANY_VALUE.equals(url.getServiceInterface())
                && url.getParameter(Constants.REGISTER_KEY, true)) {
    
    
            registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
                    Constants.CHECK_KEY, String.valueOf(false)));
        }
        //订阅服务端提供的服务
        directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
                Constants.PROVIDERS_CATEGORY
                        + "," + Constants.CONFIGURATORS_CATEGORY
                        + "," + Constants.ROUTERS_CATEGORY));

        Invoker invoker = cluster.join(directory);
        ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
        return invoker;
    }

接下来分析 RegistryDirectory 是如何将zk中的生产者服务信息同步到每个invoker对象中的
先看父类的 list 方法,它可以返回所有的invoker对象,代码如下

//可以获得所有的invocation
public List<Invoker<T>> list(Invocation invocation) throws RpcException {
    
    
    if (destroyed) {
    
    
        throw new RpcException("Directory already destroyed .url: " + getUrl());
    }
    //模板方法调用子类的 doList 方法
    //RegistryDirectory 的 doList方法中 如果服务没有禁用则从成员变量(Map<String, List<Invoker<T>>> methodInvokerMap)中获取
    List<Invoker<T>> invokers = doList(invocation);
    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;
}

接下来看下 RegistryDirectory 的 notify方法,如果zk中生产者提供的服务信息发生变化则会调用此方法,在方法中根据变化的信息同步到对应的invoker对象,代码如下

public synchronized void notify(List<URL> urls) {
    
    
        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();
            String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
            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());
            }
        }
        // configurators
        if (configuratorUrls != null && !configuratorUrls.isEmpty()) {
    
    
            this.configurators = toConfigurators(configuratorUrls);
        }
        // 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) {
    
    
                this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl);
            }
        }
        // providers
        refreshInvoker(invokerUrls);
    }

上面代码最主要方法是 refreshInvoker(invokerUrls)
代码如下

private void refreshInvoker(List<URL> invokerUrls) {
    
    
        if (invokerUrls != null && invokerUrls.size() == 1 && invokerUrls.get(0) != null
                && Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
    
    
            this.forbidden = true; // Forbid to access
            this.methodInvokerMap = null; // Set the method invoker map to null
            destroyAllInvokers(); // Close all invokers
        } else {
    
    
            this.forbidden = false; // Allow to access
            Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
            if (invokerUrls.isEmpty() && this.cachedInvokerUrls != null) {
    
    
                invokerUrls.addAll(this.cachedInvokerUrls);
            } else {
    
    
                this.cachedInvokerUrls = new HashSet<URL>();
                this.cachedInvokerUrls.addAll(invokerUrls);//Cached invoker urls, convenient for comparison
            }
            if (invokerUrls.isEmpty()) {
    
    
                return;
            }
            Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// Translate url list to Invoker map
            Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // Change method name to map Invoker Map
            // state change
            // If the calculation is wrong, it is not processed.
            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 {
    
    
                destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); // Close the unused Invoker
            } catch (Exception e) {
    
    
                logger.warn("destroyUnusedInvokers error. ", e);
            }
        }
    }

将上面代码是将url信息转换成成invoker存储到 methodInvokerMap 和 urlInvokerMap 成员变量中

服务路由 Router

我们什么时候要用到Router,当我们要配置dubbo服务的白名单黑名单 或一个服务提供给不同的消费者。在服务字典获所有invoker的时候,会通过路由过滤掉不能调用的invoker
先看下Router 的关系图
在这里插入图片描述
在这里插入图片描述
有两个实现类
条件路由 ConditionRouter 和 脚本路由 ScriptRouter
先看下条件路由 ConditionRouter 吧
ConditionRouter 的构造方法代码如下

public ConditionRouter(URL url) {
    
    
    this.url = url;
    // 获取 priority 和 force 配置
    this.priority = url.getParameter(Constants.PRIORITY_KEY, 0);
    this.force = url.getParameter(Constants.FORCE_KEY, false);
    try {
    
    
        // 获取路由规则
        String rule = url.getParameterAndDecoded(Constants.RULE_KEY);
        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);
        // 将解析出的匹配规则分别赋值给 whenCondition 和 thenCondition 成员变量
        this.whenCondition = when;
        this.thenCondition = then;
    } catch (ParseException e) {
    
    
        throw new IllegalStateException(e.getMessage(), e);
    }
}

根据上面代码得出,根据传入的 url 对象,通过 parseRule 方法解析,将解析的信息赋值给成员变量下面我们来看下 parseRula方法吧,代码如下


private static Map<String, MatchPair> parseRule(String rule)
        throws ParseException {
    
    
    // 定义条件映射集合
    Map<String, MatchPair> condition = new HashMap<String, MatchPair>();
    if (StringUtils.isBlank(rule)) {
    
    
        return condition;
    }
    MatchPair pair = null;
    Set<String> values = null;
    // 通过正则表达式匹配路由规则,ROUTE_PATTERN = ([&!=,]*)\s*([^&!=,\s]+)
    // 这个表达式看起来不是很好理解,第一个括号内的表达式用于匹配"&", "!", "=" 和 "," 等符号。
    // 第二括号内的用于匹配英文字母,数字等字符。举个例子说明一下:
    //    host = 2.2.2.2 & host != 1.1.1.1 & method = hello
    // 匹配结果如下:
    //     括号一      括号二
    // 1.  null       host
    // 2.   =         2.2.2.2
    // 3.   &         host
    // 4.   !=        1.1.1.1 
    // 5.   &         method
    // 6.   =         hello
    final Matcher matcher = ROUTE_PATTERN.matcher(rule);
    while (matcher.find()) {
    
    
       	// 获取括号一内的匹配结果
        String separator = matcher.group(1);
        // 获取括号二内的匹配结果
        String content = matcher.group(2);
        // 分隔符为空,表示匹配的是表达式的开始部分
        if (separator == null || separator.length() == 0) {
    
    
            // 创建 MatchPair 对象
            pair = new MatchPair();
            // 存储 <匹配项, MatchPair> 键值对,比如 <host, MatchPair>
            condition.put(content, pair); 
        } 
        
        // 如果分隔符为 &,表明接下来也是一个条件
        else if ("&".equals(separator)) {
    
    
            // 尝试从 condition 获取 MatchPair
            if (condition.get(content) == null) {
    
    
                // 未获取到 MatchPair,重新创建一个,并放入 condition 中
                pair = new MatchPair();
                condition.put(content, pair);
            } else {
    
    
                pair = condition.get(content);
            }
        } 
        
        // 分隔符为 =
        else if ("=".equals(separator)) {
    
    
            if (pair == null)
                throw new ParseException("Illegal route rule ...");

            values = pair.matches;
            // 将 content 存入到 MatchPair 的 matches 集合中
            values.add(content);
        } 
        
        //  分隔符为 != 
        else if ("!=".equals(separator)) {
    
    
            if (pair == null)
                throw new ParseException("Illegal route rule ...");

            values = pair.mismatches;
            // 将 content 存入到 MatchPair 的 mismatches 集合中
            values.add(content);
        }
        
        // 分隔符为 ,
        else if (",".equals(separator)) {
    
    
            if (values == null || values.isEmpty())
                throw new ParseException("Illegal route rule ...");
            // 将 content 存入到上一步获取到的 values 中,可能是 matches,也可能是 mismatches
            values.add(content);
        } else {
    
    
            throw new ParseException("Illegal route rule ...");
        }
    }
    return condition;
}

上面代码做了什么呢,它就是将 url对象中的信息进行解析,然后将解析的数据返回
接下来看ConditionRouter 的 route 方法

public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
    
    
    if (invokers == null || invokers.isEmpty()) {
    
    
        return invokers;
    }
    try {
    
    
        // 先对服务消费者条件进行匹配,如果匹配失败,表明服务消费者 url 不符合匹配规则,
        // 无需进行后续匹配,直接返回 Invoker 列表即可。比如下面的规则:
        //     host = 10.20.153.10 => host = 10.0.0.10
        // 这条路由规则希望 IP 为 10.20.153.10 的服务消费者调用 IP 为 10.0.0.10 机器上的服务。
        // 当消费者 ip 为 10.20.153.11 时,matchWhen 返回 false,表明当前这条路由规则不适用于
        // 当前的服务消费者,此时无需再进行后续匹配,直接返回即可。
        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...");
            return result;
        }
        // 这里可以简单的把 Invoker 理解为服务提供者,现在使用服务提供者匹配规则对 
        // Invoker 列表进行匹配
        for (Invoker<T> invoker : invokers) {
    
    
            // 若匹配成功,表明当前 Invoker 符合服务提供者匹配规则。
            // 此时将 Invoker 添加到 result 列表中
            if (matchThen(invoker.getUrl(), url)) {
    
    
                result.add(invoker);
            }
        }
        
        // 返回匹配结果,如果 result 为空列表,且 force = true,表示强制返回空列表,
        // 否则路由结果为空的路由规则将自动失效
        if (!result.isEmpty()) {
    
    
            return result;
        } else if (force) {
    
    
            logger.warn("The route result is empty and force execute ...");
            return result;
        }
    } catch (Throwable t) {
    
    
        logger.error("Failed to execute condition router rule: ...");
    }
    
    // 原样返回,此时 force = false,表示该条路由规则失效
    return invokers;
}

上面代码主要通过 matchWhen、matchThen 方法进行匹配路由,看传递过来的参数判断是否可以访问

总结

dubbo生产者初始化过程

首先通过 DubboNamespaceHandler 解析配置文件,每有一个dubbo:service标签即创建一个ServiceBean 类
每个servicebean会在项目启动的时候通过 setApplicationContext 方法 向spring添加一个监听器
监听器会初始化生产者的服务
监听器是怎么初始化生产者的服务的下面继续分析
首先循环遍历所有注册中心,然后遍历所有协议,
根据注册中心和协议 对每个服务进行导出
根据配置信息创建URL对象
进行导出服务,如果要导出服务到本地
–》根据实现类ref对象和URL对象创建 invoker对象,然后缓存起来
进行导出服务,如果要导出服务到远程
–》根据实现类ref对象和URL对象创建 invoker对象,invoker就是对ref对象进行简单的代理
–》根据invoker创建DubboExporter对象(用于处理请求),DubboExporter对invoker对象进行包装
–》开启netty服务端
–》获得注册中心地址,连接注册中心,检测注册中心开启情况,如果变化给所有监听器进行回调
–》每有一个服务则在注册中心上创建一条信息
–》然后订阅 override 数据

消费者初始化过程

首先通过 DubboNamespaceHandler 解析配置文件, 每有一个 dubbo:reference 创建一个ReferenceBean 类
ReferenceBean 是一个factorybean, 可以通过它来创建一个单例的代理类(ref 要调用接口的实现类)
这个factorybean会创建一个什么样的代理对象呢,下面进行分析
如果是本地引用则 根据实现类(ref) 创建一个invoker对象
如果是远程引用则 根据实现类(ref) 创建一个DubboInvoker对象
–> 每有一个服务提供者则创建一个 DubboInvoker,DubboInvoker对象的invoker方法会调用netty的client的send方法(DubboInvoker可以向服务端发起调用请求)
–> 一个服务有多个提供者的话,会创建多个DubboInvoker放到集合中,然后创建AbstractClusterInvoker实现类下的对象进行集群管理
–> 集群管理包括(调用服务失败后抛异常还是调用别的生产者、负载均衡策略选择)
开启netty客户端,连接注册中心,向注册中心中写入服务,初始化服务字典RegistryDirectory
在服务字典(服务端每有一个服务则创建一个DubboInvoker存储到服务字典中)中添加监听。
根据invoker创建对应的代理proxy对象返回。 proxy是对invoker对象进行了包装。

服务调用过程

消费者向生产者发送请求

DubboProductInterface obj=(DubboProductInterface)application.getBean("DubboProductInterface") 
obj.sayHello();

上面代码会通过getBean() 方法 创建 DubboProductInterface 接口的代理对象,每次调用这个代理对象的方法都会发起远程调用,这个代理对象是什么样子呢?

在spring容器中通过单例的factorybean会创建一个DubboProductInterface 类型的代理对象
如果生产者非集群部署(一个服务的提供者只有一个)
--->也就是生产者只有一个则直接根据 生产者提供的服务信息 返回一个DubboInvoker对象。
如果生产者是集群部署的话 (一个服务的提供者有多个)
--->会通过AbstractClusterInvoker 将多个DubboInvoker进行包装,包装后对多个DubboInvoker进行集群管理
--->(一个服有几个生产者提供就有几个DubboInvoker)
--->然后返回AbstractClusterInvoker 对象

AbstractClusterInvoker 的 invoke 方法做了什么?

假如调用的AbstractClusterInvoker的实现类是 FailoverClusterInvoker 
他的作用是 调用失败自动切换生产者,也就是调用生产者失败后调用别的生产者
FailoverClusterInvoker 的doinvoke方法会先获得重试次数(失败后最多调用几次)
通过LoadBalance 的实现类进行负责均衡(从多个DubboInvoker中选择一个返回),
调用AbstractInvoker中的invoke方法,也就是DubboInvoker中的doinvoke方法
如果调用失败则根据重试次数进行多次调用,调用了重试次数次 都失败则抛异常。

在DubboInvoker中调用分为很多种包括,异步有返回值调用、异步没有返回值调用、同步调用(同步调用可定有返回值,空也算返回值)
在这就分析同步调用吧,在DubboInvoker 的doinvoke 方法中
currentClient.request(inv, timeout).get(); 在这里的get() 会阻塞等待返回的数据
调用流程如下

> DubboInvoker#doInvoke(Invocation)> ReferenceCountExchangeClient#request(Object, int)   --> 里面有一个计数器
    —> HeaderExchangeClient#request(Object, int)         --> 初始化this.channel = new HeaderExchangeChannel(client);
	                                                     --> 进行心跳检测
      —> HeaderExchangeChannel#request(Object, int)      --> 将请求的信息包装成Request对象
        —> AbstractPeer#send(Object)> AbstractClient#send(Object, boolean)		 -->连接Netty服务端     
		                                                         将连接后的channel对象存储起来(例NioServerSocketChannel)
																 在dubbo中是NettyChannel
                                       		             -->获得channel                 
														 -->调用channel的send方法     
            —> NettyChannel#send(Object, boolean)> NioClientSocketChannel#write(Object)    

消费者向生产者发送参数的编码

接下来分析编码调用流程

在 NettyClient中设置编码器, pipeline.addLast("encoder", adapter.getEncoder());
adapter.getEncoder() 会创建一个内部类 InternalEncoder()
InternalEncoder()encode()方法 调用codec.encode(channel, buffer, msg);
也就是 ExchangeCodec 的encode方法 的 encodeRequest()方法
encodeRequest() 方法中通过channel将对象信息发送给服务端

生产者接收请求解码过程

解码调用过程如下

ExchangeCodec --》decode
	ExchangeCodec --decode(channel, buffer, readable, header)
		ExchangeCodec --decodeBody(channel, is, header)
			DubboCodec    --decodeBody()  创建一个new DecodeableRpcInvocation() 对象返回
				DecodeableRpcInvocation    ---decode()  
				最终会返回一个DecodeableRpcInvocation 对象
解码流程结束,将发送的信息封装到一个request对象返回, 
这个request对象的类型是ecodeableRpcInvocation

生产者根据请求信息调用指定方法

接下来就是将这个request对象交给hanlder进行处理,处理request的handler经过一系列包装,包装类层次如下

new NettyHandler(
	new MultiMessageHandler(
		new HeartbeatHandler(
			new AllChannelHandler(
				new DecodeHandler(
					new HeaderExchangeHandler(
						new ExchangeHandlerAdapter()))))))

接下来看调用栈吧

NettyHandler -messageReceived()   netty框架用来接收请求的入口
	MultiMessageHandler  -》   received
		HeartbeatHandler  -》    received
			AllChannelHandler  -》  received    开启线程
				ChannelEventRunnable -run()    根据通道类型进行调用不同方法
				    DecodeHandler -》received
						HeaderExchangeHandler  -》 received    调用方法,将调用方法的结果返回的结果发送回去channel.send(response);
						HeaderExchangeHandler  -handleRequest(exchangeChannel, request);  处理请求
							ExchangeHandlerAdapter -》reply 
							   也就是 DubboProtocol  -》reply    根据传递的参数 获得对应的DubboExporter
							                                     DubboExporter 中包含着对应的invoker
								    AbstractProxyInvoker  -》invoke   								 
											JavassistProxyFactory  -》getInvoker    即可调用指定方法

消费者对生产者发送的参数解码

消费者接收生产者响应解码

NettyClient 的 adapter.getEncoder() 方法进行解码,
最后会到 ExchangeCodec 的  encodeResponse(channel, buffer, (Response) msg); 方法
然后DubboCodec 的   decodeBody 方法 返回一个new DecodeableRpcResult 请求对象
DecodeableRpcResult ==decode() 方法,将参数存储到DecodeableRpcResult对象中

消费者接收生产者发送的参数

解码完成后,接收使用handler解析DecodeableRpcResult

还是那个包装了很多次的handler
跳过部分handler直接进入主要的逻辑 
HeaderExchangeHandler   received
HeaderExchangeHandler  handleResponse(channel, (Response) message);    
         在这里移除message 同时将请求传递的response参数放到DefaultFuture的成员变量中
		 然后唤醒用户线程

消费者接收生产者响应,如何找到消费者发起请求的线程

消费者调用生产者
生产者没返回调用结果时,消费者会阻塞,在哪里阻塞的呢

DubboInvoker 的 doinvoke 方法 
    --》 currentClient.request(inv, timeout).get();  在这里的get() 会阻塞
也就是DefaultFuture 的get方法

先看下DefaultFuture 中都做了什么

DefaultFuture 中有个静态map,存储着返回信息请求id 和 DefaultFuture对象
DefaultFuture 静态代码块会开启一个守护线程,如果相应超过固定时间则将DefaultFuture从静态map中移除

DefaultFuture 的get方法都做了什么,将代码简写

 private final Lock lock = new ReentrantLock();
 private final Condition done = lock.newCondition();
 private volatile Response response;
 
 public Object get(int timeout) throws RemotingException {
    
    
    //如果返回值为空
    if (!isDone()) {
    
    
        long start = System.currentTimeMillis();
        lock.lock();
        try {
    
    
            while (!isDone()) {
    
    
                //在lock锁中进行阻塞,相当于在synchronized中使用wait,要用notify进行唤醒
                //synchronized 中的notify是随机唤醒,不能指定唤醒
                //lock锁中的 await可以被指定唤醒
                done.await(timeout, TimeUnit.MILLISECONDS);
                //是否超过响应时间
                if (isDone() || System.currentTimeMillis() - start > timeout) {
    
    
                    break;
                }
            }
        } finally {
    
    
           //释放锁
            lock.unlock();
        }
    }
    return returnFromResponse();
}

在生产者返回响应是如何唤醒线程的呢
在解码后交给handler进行处理,直接看核心代码
HeaderExchangeHandler —》 handleResponse(channel, (Response) message);

static void handleResponse(Channel channel, Response response) throws RemotingException {
    
    
    if (response != null && !response.isHeartbeat()) {
    
    
        DefaultFuture.received(channel, response);
    }
}

接下来看 DefaultFuture.received(channel, response)

public static void received(Channel channel, Response response) {
    
    
    try {
    
    
        //根据id获得对应的DefaultFuture 
        //通过id将静态map中的DefaultFuture 移除
        DefaultFuture future = FUTURES.remove(response.getId());
        if (future != null) {
    
    
        	//在方法中调用 done.signal(); 进行唤醒
            future.doReceived(response);
        } 
        }
    } finally {
    
    
        CHANNELS.remove(response.getId());
    }
}

接下来看 future.doReceived(response);

private void doReceived(Response res) {
    
    
    lock.lock();
    try {
    
    
        response = res;
        if (done != null) {
    
    
            //进行唤醒
            done.signal();
        }
    } finally {
    
    
        lock.unlock();
    }
    if (callback != null) {
    
    
        invokeCallback(callback);
    }
}

经典问题

Dubbo 中注册中心可以使用哪些,协议可以使用哪些
Dubbo 中失败重试次数怎么设置
Dubbo 请求是否异步,异步是否需要返回值,怎么配置
Douuo 消费者调用生产者,消费者阻塞生产者返回结果,如何根据结果找到消费者上正在阻塞的线程

dubbo 怎么如何追踪dubbo id的

如果非集群服务则会调用 DubboInvoker 的 doInvoke方法,如果是异步有返回值则调用
ResponseFuture future = currentClient.request(inv, timeout); 方法 //HeaderExchangeChannel中创建Future
首先 DefaultFuture中有一个静态 Map,map的key为id,value为本次请求的DefaultFuture,同时会开一个守护线程来将超时的DefaultFuture移除
假如是异步请求,每当一次请求,就创建一个
本次请求的DefaultFuture还维护着一个lock锁 用于阻塞和释放当前线程(使用wait和notify的话不能唤醒指定线程),
为了防止静态 Map中本次请求的DefaultFuture被移除导致找不到DefaultFuture,
RpcContext将本次请求的DefaultFuture存储到在threadlocal中,DefaultFuture中也有id)

猜你喜欢

转载自blog.csdn.net/fenkanghong9779/article/details/106837433
今日推荐