目录
1. 前提
2. 认识 Protocol 和 ProxyFactory
Protocal
ProxyFactory
Dubbo服务流程
服务端源码分析
测试代码:
Protocal代理的源码
ProxyFactory源码:
获取invoker对象
具体步骤
1. 我们调用的是ProxyFactory的代理对象的getInvoker方法
2. 我们把断点打到 JavassistProxyFactory 类的对应方法处,进入断点并且封装了一个invoker对象进行返回
服务暴露:
1. 我们调用的是Portocal对象的静态代理类的export方法
2. 直接把断点打到 RegistryProtocal类的export方法上
3. 重点分析服务暴露doLocalExport方法
1. 前提
本来想着一篇文章分析完dubbo主流程源码的,但是写完Dubbo 基于xml文件分析主流程源码 (4)_chen_yao_kerr的博客-CSDN博客以后发现,源码中存在大量的Dubbo SPI思想,不说清楚Dubbo SPI, 源码是看不下去的。因此,紧接着我又补了2篇Dubbo SPI的博客。
所以有了前3篇博客的铺垫,再次来谈源码,相信会简单很多。
2. 认识 Protocol 和 ProxyFactory
不了解这两个接口,今天的源码分析是走不下去的,它是Dubbo SPI思想运用于Dubbo框架非常重要的实践。
Protocal
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.Adaptive;
import com.alibaba.dubbo.common.extension.SPI;
@SPI("dubbo")
public interface Protocol {
int getDefaultPort();
@Adaptive
<T> Exporter<T> export(Invoker<T> var1) throws RpcException;
@Adaptive
<T> Invoker<T> refer(Class<T> var1, URL var2) throws RpcException;
void destroy();
}
他有很多实现类:
而这些子类的key-value形式,都是配置在如下路径中:
先对这些概率有个大概的了解,后面会在代码里面直接提到这个key,希望对照这些图片,能够快速的知道我在说的是什么类。
ProxyFactory
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.Adaptive;
import com.alibaba.dubbo.common.extension.SPI;
@SPI("javassist")
public interface ProxyFactory {
@Adaptive({"proxy"})
<T> T getProxy(Invoker<T> var1) throws RpcException;
@Adaptive({"proxy"})
<T> T getProxy(Invoker<T> var1, boolean var2) throws RpcException;
@Adaptive({"proxy"})
<T> Invoker<T> getInvoker(T var1, Class<T> var2, URL var3) throws RpcException;
}
它和上面的接口一样,也有很多的子类:
而这些子类的key-value形式,都是配置在如下路径中:
Dubbo服务流程
1. 需要区分2个URL,第一个是连接注册中心的URL, 注册中心可能是nacos、euraka等等。我使用的是zookeeper。 第二个URL是服务提供方的URL,也就是实际提供服务的机器。注册中心只是一个管理服务提供方的中间件,在注册中心中,我们可以找到这些真正提供服务的机器的ip、应用名、暴露的服务接口、方法等等信息。
2. 我们需要把想要暴露的服务接口的实现类实例化,经过各种封装、最终成为invoker对象,而invoker对象含有 ip、端口号、接口、方法、应用名等信息
3. 通过协议把invoker给暴露出去 (此时,基本的RPC操作就完成了)
4. 获取注册中心对象,把信息交给注册中心去管理。也就是发布、订阅等。 这一步大量的优化了RPC处理集群的复杂过程。
服务端源码分析
测试代码:
package com.enjoy;
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
import com.alibaba.dubbo.rpc.Exporter;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Protocol;
import com.alibaba.dubbo.rpc.ProxyFactory;
import com.enjoy.service.DemoService;
import com.enjoy.service.ZkDemoServiceImpl;
import org.junit.Test;
import java.io.IOException;
/**
* 注册中心动态服务
*/
public class RpcRegistryProtocolTest {
ExtensionLoader<Protocol> protocolLoader = ExtensionLoader.getExtensionLoader(Protocol.class);
ExtensionLoader<ProxyFactory> proxyLoader = ExtensionLoader.getExtensionLoader(ProxyFactory.class);
//注册中心服务--zk的URL
final URL registryUrl = URL.valueOf("registry://192.168.0.105:2181/com.alibaba.dubbo.registry.RegistryService?registry=zookeeper");
// 模拟服务器的URL连接,服务端、消费端用的是同一个URL
// 支持的协议:dubbo,http,hessian,rmi
URL serviceUrl = URL.valueOf("dubbo://127.0.0.1:9001/com.enjoy.service.DemoService?application=abcd");
@Test
public void serverRpc() throws IOException {
DemoService service = new ZkDemoServiceImpl("peter");
//代理对象
Protocol protocol = protocolLoader.getAdaptiveExtension();
ProxyFactory proxy = proxyLoader.getAdaptiveExtension();
serviceUrl = serviceUrl.setPort(9001);
serviceUrl = serviceUrl.addParameter("loadbalance","first");
serviceUrl = serviceUrl.addParameter("cluster","failsms");
//连接到注册中心,把模拟的服务器的URL添加进连接注册中心的URL中
URL newRegistryUrl = registryUrl.addParameter(Constants.EXPORT_KEY, serviceUrl.toFullString());
//暴露服务
Invoker<DemoService> serviceInvoker = proxy.getInvoker(service, DemoService.class, newRegistryUrl);
Exporter<DemoService> exporter = protocol.export(serviceInvoker);
System.out.println("server 启动协议:"+serviceUrl.getProtocol());
// 保证服务一直开着
System.in.read();
exporter.unexport();
}
}
下面我就对照服务流程逐步的去拆解代码。
关于静态代理对象,代码是相同的,我直接贴出代码。这不是重点流程,想要深入的可以继续跟:
那么我就贴出生成的2个静态代理对象的代码:
Protocal代理的源码
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])");
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() ); //url没有指定处理类,默认用dubbo的。如果url中配置了处理类,则使用url中自带的
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); //又是dubbo spi思想
return extension.export(arg0); //实际的业务处理类
}
}
ProxyFactory源码:
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 java.lang.Object getProxy(com.alibaba.dubbo.rpc.Invoker arg0, boolean arg1) 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, arg1);
}
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"); //如果URL中有proxy,就根据URL配置的信息获取处理类。如果没有,那就是用默认的处理类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); //dubbole spi思路
return extension.getInvoker(arg0, arg1, arg2); //调用具体的子类
}
}
这两个静态代理是通过字符串的形式给拼接出来的,内存中是有的,但是我们无法进行debug操作,只能对照这代码进行分析。
获取invoker对象
我们知道,proxy是一个静态代理对象,是有字符串拼接出来的并且在内存中是存在的,因此需要接口上方贴出的2个代理对象的源码进行分析。
具体步骤
1. 我们调用的是ProxyFactory的代理对象的getInvoker方法
2. 我们把断点打到 JavassistProxyFactory 类的对应方法处,进入断点并且封装了一个invoker对象进行返回
服务暴露:
1. 我们调用的是Portocal对象的静态代理类的export方法
2. 直接把断点打到 RegistryProtocal类的export方法上
3. 重点分析服务暴露doLocalExport方法
其实,所谓的服务暴露,就是将需要暴露的invoker对象与协议进行绑定,并不是真的将invoker对象写到注册中心去。
在步骤2中,我们也提及到了注册与发布,其实他们操作的也都是URL信息而已
注册中心,写入的只是服务器的 URL信息,根据不同的URL信息可以找到不同的invoer对象,然后由invoker对象再解析成对应的业务对象与方法进行调用。