源码分析使用Dubbo2.6.7版本
程序入口
我们现在dubbo的使用大部分开发者会基于spring,让容器来管理我们的对象这样就很方便;
我们使用dubbo的时候会配置相关标签,标签解析入口:
com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
这里我们只需要看service标签的解析逻辑就可以了,当然基础必要的标签我们默认已经配置好了包括:
application,registry,protocol
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
dubbo使用了 DubboBeanDefinitionParser 来统一做解析所有的标签的前置处理,该类主要是将dubbo特有的标签结构解析成标准的 RootBeanDefinition,简单可以理解成向各种BeanDefinition中addPropertyValue;
其标签处理流程公共的封装,可以大致简单看看,重点还是看各个特有标签的处理流程;
我们看看ServiceBean 的继承关系:
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,
ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, BeanNameAware,
ApplicationEventPublisherAware
关键的一点它实现了InitializingBean和 ApplicationListener
,表示在spring容器将该对象实例化并初始化后就会执行 afterPropertiesSet 方法将标签设置的属性设置到bean里,spring refresh完毕后会调用 onApplicationEvent 方法将服务发布到注册中心去:
该方法主要是首先做了一系列的配置校验,最终调用方法
export();
该方法即是发布服务的核心,最终调用父类的
com.alibaba.dubbo.config.ServiceConfig#doExport
然后是一系列的检查,然后是核心方法:
com.alibaba.dubbo.config.ServiceConfig#doExportUrls
private void doExportUrls() {
List<URL> registryURLs = loadRegistries(true);
for (ProtocolConfig protocolConfig : protocols) {
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
这个方法的意思是,以注册配置为驱动 即配置项:
<dubbo:registry address="${zookeeper.list}" protocol="zookeeper"/>
,再以协议为驱动 即配置项:
<dubbo:protocol name="dubbo" port="20880"/>
<dubbo:protocol name="http" port="10086"/>
将所有服务实现注册到注册中心去;例如配置了2类注册中心、3个协议、5个服务类的话,那么会显式地发布 2 x 3 x 5 = 30次(不包括隐式注册的本地服务)
------------------------------------------------------------------------------------------------------------------------
配置文件示例:
---------------------------------------------------------------------------------------------------------------------
debug protocols:
但是一般情况下我们配置一个协议应该就满足业务需求了,如果我们在service标签上配置了其他protocol,会优先使用service标签上的protocols
------------------------------------------------------------------------------------------------------------------------
上图种registryURLs toString结构类似:
registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.0&pid=1784&qos.port=22222®istry=zookeeper×tamp=1572960033726
这里表示一个原始的registry标签解析生成的url,在dubbo中url代表了该服务发布所需的所有信息;
这里url是以registry开头的,在通过临时生成的Protocol$Adaptive类处理时会先执行RegistryProtocol的export方法
------------------------------------------------------------------------------------------------------------------------
RegistryProtocol
方法来到com.alibaba.dubbo.registry.integration.RegistryProtocol#export
标记1:缓存服务连接地址和包装好的ExporterChangeableWrapper
标记2:如果需要注册服务到注册中心(以zk为例),这里会在zk上创建对应的/dubbo/xxxservice/provider 节点
标记3:最终调用的方法:
com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry#doSubscribe
这里订阅主要是服务端订阅类似:/dubbo/com.alibaba.dubbo.demo.DemoService/configurators目录下的节点,主要是响应配置信息的改变(比如在dubbo admin里操作了禁用某些服务,或者修改了配置等)
@Override
protected void doSubscribe(final URL url, final NotifyListener listener) {
try {
if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {
String root = toRootPath();
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
if (listeners == null) {
zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
listeners = zkListeners.get(url);
}
ChildListener zkListener = listeners.get(listener);
if (zkListener == null) {
listeners.putIfAbsent(listener, new ChildListener() {
@Override
public void childChanged(String parentPath, List<String> currentChilds) {
for (String child : currentChilds) {
child = URL.decode(child);
if (!anyServices.contains(child)) {
anyServices.add(child);
subscribe(url.setPath(child).addParameters(Constants.INTERFACE_KEY, child,
Constants.CHECK_KEY, String.valueOf(false)), listener);
}
}
}
});
zkListener = listeners.get(listener);
}
//创建节点
zkClient.create(root, false);
List<String> services = zkClient.addChildListener(root, zkListener);
if (services != null && !services.isEmpty()) {
for (String service : services) {
service = URL.decode(service);
anyServices.add(service);
subscribe(url.setPath(service).addParameters(Constants.INTERFACE_KEY, service,
Constants.CHECK_KEY, String.valueOf(false)), listener);
}
}
} else {
List<URL> urls = new ArrayList<URL>();
for (String path : toCategoriesPath(url)) {
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
if (listeners == null) {
zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
listeners = zkListeners.get(url);
}
ChildListener zkListener = listeners.get(listener);
if (zkListener == null) {
listeners.putIfAbsent(listener, new ChildListener() {
@Override
public void childChanged(String parentPath, List<String> currentChilds) {
ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));
}
});
zkListener = listeners.get(listener);
}
//创建节点
zkClient.create(path, false);
List<String> children = zkClient.addChildListener(path, zkListener);
if (children != null) {
urls.addAll(toUrlsWithEmpty(url, path, children));
}
}
notify(url, listener, urls);
}
} catch (Throwable e) {
throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
这样服务端在zookeeper上的节点就创建完毕了;
SPI
很多人都说:学Dubbo最大的亮点是它的SPI。那么什么是spi呢?spi是一种服务发现机制,我的理解就是dubbo里把很多东西都做了模块化,并且把这些独立的模块取上相应的名字(名字与真实类的关联关系放在配置文件里);
比如:在协议里有个 DubboProtocol,并给该组件取名“dubbo”并保存在配置文件中,在我们使用的时候我们会通过某种(运行时生成一个自适应类,该动态类以名字为参数找到真正的实现类)方式拿到该类;
代码中很多地方可以看到以下代码:
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
这便是上面说的自适应类,即在该类加载时动态生成的自适应类
Protocol
Protocol的实例是 Protocol$Adaptive类型的,通过debug我们获取了它的源码(com.alibaba.dubbo.common.extension.ExtensionLoader#createAdaptiveExtensionClass 源码生成是在这个类里生成的,在这里打个断点就可以看到了),该类作用是消费端生成代理和服务端发布服务:
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
/**
* 协议自适应类
*/
public class Protocol$Adaptive 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])");
// 根据extName找到具体适应类,然后调用方法
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])");
// 根据extName找到具体适应类,然后调用方法
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);
}
}
ProxyFactory
ProxyFactory主要作用是获得消费端的代理对象和生成服务端真实实现类的包装类(Invoker);
ProxyFactory引用的是一个ProxyFactory$Adaptive类实例,通过debug我们获得源码如下(该类主要作用是生成目标类的代理类):
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
/**
* 代理自适应类
*/
public class ProxyFactory$Adaptive implements com.alibaba.dubbo.rpc.ProxyFactory {
/**
* 服务引用获取代理对象
*/
public java.lang.Object getProxy(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.getParameter("proxy", "javassist");
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return extension.getProxy(arg0);
}
/**
* 服务暴露获取暴露接口对象
*/
public com.alibaba.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, com.alibaba.dubbo.common.URL arg2) throws com.alibaba.dubbo.rpc.RpcException {
if (arg2 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg2;
String extName = url.getParameter("proxy", "javassist");
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
// 根据extName找到具体适应类,然后调用方法
com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return extension.getInvoker(arg0, arg1, arg2);
}
}
DubboProtocol
下面以 DubboProtocol为例分析以下服务发布的流程
正常情况下调用流程(简要说明):
com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol#openServer
↓
com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol#createServer
↓
com.alibaba.dubbo.remoting.exchange.Exchangers#bind(com.alibaba.dubbo.common.URL, com.alibaba.dubbo.remoting.exchange.ExchangeHandler)
这个方法里会
1、获得Exchanger接口的自适应类即Exchanger$Adaptive,这个自适应类在调用方法的时候会默认(如果没有特殊工指定)去找
com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchanger,因为在Exchanger类里标注了@SPI(HeaderExchanger.NAME)即 HeaderExchanger;
2、调用HeaderExchanger的bind方法
↓
com.alibaba.dubbo.remoting.Transporters#bind(com.alibaba.dubbo.common.URL, com.alibaba.dubbo.remoting.ChannelHandler...)
这个方法里
1、获得Transporter接口的自适应类 Transporter$Adaptive
2、调用bind方法
调用bind方法的时候,Transporter$Adaptive会默认调用 com.alibaba.dubbo.remoting.transport.netty4.NettyTransporter做具体实现
↓
NettyServer构造方法 -> 父类AbstractServer构造方法 -> NettyServer的 doOpen()方法,
至此就可以看到底层netty标准服务端生成和各种handler注入的代码了
StubProxyFactoryWrapper
ProxyFactory在框架中实例是 StubProxyFactoryWrapper,而 StubProxyFactoryWrapper是对 JavassistProxyFactory和JdkProxyFactory起装饰作用;真实的默认实现是 JavassistProxyFactory
服务端中转对象
待服务发布完成后,框架会将真实服务实现包装成一个Invoker对象,然后再封装缓存起来
1.首先将服务的实现封装成一个Invoker,Invoker中封装了服务的实现类。
2.将Invoker封装成Exporter,并缓存起来,缓存里使用Invoker的url作为key。
3.服务端Server启动,监听端口。(请求来到时,根据请求信息生成key,到缓存查找Exporter,就找到了Invoker,就可以完成调用。)