目录
dubbo拓展机制 SPI
dubbo 自适应拓展机制
dubbo 服务导出
dubbo 服务引用
dubbo 服务字典
dubbo 服务路由
dubbo 集群
dubbo 负载均衡
dubbo 服务调用过程
本文大量引用官网的描述
dubbo官网
美团字节码增强探秘
在 Dubbo 中,很多拓展都是通过 SPI 机制进行加载的,比如 Protocol、Cluster、LoadBalance 等。有时,有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载。这听起来有些矛盾。拓展未被加载,那么拓展方法就无法被调用(静态方法除外)。拓展方法未被调用,拓展就无法被加载。对于这个矛盾的问题,Dubbo 通过自适应拓展机制很好的解决了。自适应拓展机制的实现逻辑比较复杂,首先 Dubbo 会为拓展接口生成具有代理功能的代码。然后通过 javassist 或 jdk 编译这段代码,得到 Class 类。最后再通过反射创建代理类,整个过程比较复杂。
@SuppressWarnings("unchecked")
public static ExtensionLoader getExtensionLoader(Class type) {
if (type == null) {
throw new IllegalArgumentException("Extension type == null");
}
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
}
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type (" + type +
") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
}
ExtensionLoader loader = (ExtensionLoader) EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));
loader = (ExtensionLoader) EXTENSION_LOADERS.get(type);
}
return loader;
}
获取自适应的拓展实现是在ExtensionLoader的构造方法中
private ExtensionLoader(Class> type) {
this.type = type;
// 如果不判断type!=ExtensionFactory,这里将是死循环
// 获取ExtensionFactory的自适应拓展实现对象
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
接着看getAdaptiveExtension
public T getAdaptiveExtension() {
// 从缓存中获取自适应拓展类实现
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
// 如果曾经创建自适应拓展失败,直接返回错误
if (createAdaptiveInstanceError != null) {
throw new IllegalStateException("Failed to create adaptive instance: " +
createAdaptiveInstanceError.toString(),
createAdaptiveInstanceError);
}
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
// 创建自适应拓展
instance = createAdaptiveExtension();
// 设置自适应拓展到缓存中
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
// 保存创建自适应拓展实现失败原因
createAdaptiveInstanceError = t;
throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
}
}
}
}
return (T) instance;
}
接着看createAdaptiveExtension
private T createAdaptiveExtension() {
try {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
createAdaptiveExtension 方法的代码比较少,但却包含了三个逻辑,分别如下:
1.调用 getAdaptiveExtensionClass 方法获取自适应拓展 Class 对象
2.通过反射进行实例化
3.调用 injectExtension 方法向拓展实例中注入依赖
前两个逻辑比较好理解,第三个逻辑用于向自适应拓展对象中注入依赖。这个逻辑看似多余,但有存在的必要,这里简单说明一下。前面说过,Dubbo 中有两种类型的自适应拓展,一种是手工编码的,一种是自动生成的。手工编码的自适应拓展中可能存在着一些依赖,而自动生成的 Adaptive 拓展则不会依赖其他类。这里调用 injectExtension 方法的目的是为手工编码的自适应拓展注入依赖,这一点需要大家注意一下。关于 injectExtension 方法,前文已经分析过了,这里不再赘述。接下来,分析 getAdaptiveExtensionClass 方法的逻辑。
private Class> getAdaptiveExtensionClass() {
// 通过spi加载所有的拓展类
getExtensionClasses();
// 获取缓存
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
// 创建自适应拓展类
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
getAdaptiveExtensionClass 方法同样包含了三个逻辑,如下:
1.调用 getExtensionClasses 获取所有的拓展类
2.检查缓存,若缓存不为空,则返回缓存
3.若缓存为空,则调用 createAdaptiveExtensionClass 创建自适应拓展类
这三个逻辑看起来平淡无奇,似乎没有多讲的必要。但是这些平淡无奇的代码中隐藏了着一些细节,需要说明一下。首先从第一个逻辑说起,getExtensionClasses 这个方法用于获取某个接口的所有实现类。比如该方法可以获取 Protocol 接口的 DubboProtocol、HttpProtocol、InjvmProtocol 等实现类。在获取实现类的过程中,如果某个某个实现类被 Adaptive 注解修饰了,那么该类就会被赋值给 cachedAdaptiveClass 变量。此时,上面步骤中的第二步条件成立(缓存不为空),直接返回 cachedAdaptiveClass 即可。如果所有的实现类均未被 Adaptive 注解修饰,那么执行第三步逻辑,创建自适应拓展类。
private Class> createAdaptiveExtensionClass() {
// 构建自适应拓展代码
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
// 编译代码,并返回该类
ClassLoader classLoader = findClassLoader();
// 获取编译器实现类,Compiler的默认实现为javassist
// 默认使用javassist编译代码生成类
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
// 编译代码,生成 Class
return compiler.compile(code, classLoader);
}
AdaptiveClassCodeGenerator用于保存type和cachedDefaultName(@SPI的配置),接下来看看generate方法
public String generate() {
// no need to generate adaptive class since there's no adaptive method found.
// 必须至少一个方法有@Adaptive注解
if (!hasAdaptiveMethod()) {
throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");
}
StringBuilder code = new StringBuilder();
// 用于拼接一个字符串,格式与java类相同
code.append(generatePackageInfo());
// 包名=type的包名,引用(import) ExtensionLoader类
code.append(generateImports());
// 生成类代码:public class + type简单名称 + $Adaptive + implements + type全限定名 + {
code.append(generateClassDeclaration());
// 例如生成代码:
// package com.alibaba.dubbo.rpc;
//import com.alibaba.dubbo.common.extension.ExtensionLoader;
//public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
// // 省略方法代码
//}
Method[] methods = type.getMethods();
for (Method method : methods) {
// 实现type的每个方法
code.append(generateMethod(method));
}
code.append("}");
if (logger.isDebugEnabled()) {
logger.debug(code.toString());
}
return code.toString();
}
代码都比较简单,generateMethod方法逻辑比较复杂,接下来看看generateMethod
private String generateMethod(Method method) {
// 方法返回类型
String methodReturnType = method.getReturnType().getCanonicalName();
// 方法名
String methodName = method.getName();
// 方法体
String methodContent = generateMethodContent(method);
// 方法参数
String methodArgs = generateMethodArguments(method);
// 方法抛出异常
String methodThrows = generateMethodThrows(method);
// 组成方法字符串
return String.format(CODE_METHOD_DECLARATION, methodReturnType, methodName, methodArgs, methodThrows, methodContent);
}
主要看看generateMethodContent的代码,这一部分是用来生成方法体的
private String generateMethodContent(Method method) {
Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
StringBuilder code = new StringBuilder(512);
// 如果方法没有Adaptive 注解
// 生成的方法只会抛出一个异常,如
// throw new UnsupportedOperationException(
// "method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
if (adaptiveAnnotation == null) {
return generateUnsupported(method);
} else {
// 获取参数中URL类型参数的index
int urlTypeIndex = getUrlTypeIndex(method);
// found parameter in URL type
if (urlTypeIndex != -1) {
// Null Point check
// url类型参数非空判断
// 如:if (arg1 == null)
// throw new IllegalArgumentException("url == null");
// com.alibaba.dubbo.common.URL url = arg1;
code.append(generateUrlNullCheck(urlTypeIndex));
} else {
// did not find parameter in URL type
// 循环检测方法每个参数是否有一个getUrl的方法
// 2.方法中无这样的参数,而是在参数集合的属性中
// 如: 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();
code.append(generateUrlAssignmentIndirectly(method));
}
// 若 value 为非空数组,直接获取数组内容即可。若 value 为空数组,则需进行额外处理。
// 处理过程是将类名转换为字符数组,然后遍历字符数组,并将字符放入 StringBuilder 中。
// 若字符为大写字母,则向 StringBuilder 中添加点号,随后将字符变为小写存入 StringBuilder 中
String[] value = getMethodAdaptiveValue(adaptiveAnnotation);
// 是否含有Invocation类型的参数
boolean hasInvocation = hasInvocationArgument(method);
// 生成Invocation类型参数的判空逻辑
code.append(generateInvocationArgumentNullCheck(method));
code.append(generateExtNameAssignment(value, hasInvocation));
// check extName == null?
code.append(generateExtNameNullCheck(value));
code.append(generateExtensionAssignment());
// return statement
code.append(generateReturnAndInvocation(method));
}
return code.toString();
}
其中generateExtNameAssignment较复杂
private String generateExtNameAssignment(String[] value, boolean hasInvocation) {
// TODO: refactor it
String getNameCode = null;
// 此处循环目的是生成从 URL 中获取拓展名的代码,生成的代码会赋值给 getNameCode 变量。注意这
// 个循环的遍历顺序是由后向前遍历的。
for (int i = value.length - 1; i >= 0; --i) {
// 当 i 为最后一个元素的坐标时
if (i == value.length - 1) {
// @SPI value不为空
if (null != defaultExtName) {
// protocol 是 url 的一部分,可通过 getProtocol 方法获取,其他的则是从
// URL 参数中获取。因为获取方式不同,所以这里要判断 value[i] 是否为 protocol
if (!"protocol".equals(value[i])) {
if (hasInvocation) {
// 生成的代码功能等价于下面的代码:
// url.getMethodParameter(methodName, value[i], defaultExtName)
getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
} else {
// 生成的代码功能等价于下面的代码:
// url.getParameter(value[i], defaultExtName)
getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName);
}
} else {
// 生成的代码功能等价于下面的代码:
// ( url.getProtocol() == null ? defaultExtName : url.getProtocol() )
getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName);
}
} else {
if (!"protocol".equals(value[i])) {
if (hasInvocation) {
getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
} else {
// 生成的代码功能等价于下面的代码:
// url.getParameter(value[i])
getNameCode = String.format("url.getParameter(\"%s\")", value[i]);
}
} else {
// 生成从 url 中获取协议的代码,比如 "dubbo"
getNameCode = "url.getProtocol()";
}
}
} else {
if (!"protocol".equals(value[i])) {
if (hasInvocation) {
getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
} else {
// 生成的代码功能等价于下面的代码:
// url.getParameter(value[i], getNameCode)
getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode);
}
} else {
getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode);
}
}
}
// 生成 extName 赋值代码
return String.format(CODE_EXT_NAME_ASSIGNMENT, getNameCode);
}
几个重要接口的自适应实现(可以利用arthas反编译jvm中已加载的类):
- Protocol
public class Protocol$Adaptive
implements Protocol {
public void destroy() {
throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {
throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
}
public Exporter export(Invoker invoker) throws RpcException {
String string;
if (invoker == null) {
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
}
if (invoker.getUrl() == null) {
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
}
URL uRL = invoker.getUrl();
String string2 = string = uRL.getProtocol() == null ? "dubbo" : uRL.getProtocol();
if (string == null) {
throw new IllegalStateException(new StringBuffer().append("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (").append(uRL.toString()).append(") use keys([protocol])").toString());
}
Protocol protocol = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(string);
return protocol.export(invoker);
}
public Invoker refer(Class class_, URL uRL) throws RpcException {
String string;
if (uRL == null) {
throw new IllegalArgumentException("url == null");
}
URL uRL2 = uRL;
String string2 = string = uRL2.getProtocol() == null ? "dubbo" : uRL2.getProtocol();
if (string == null) {
throw new IllegalStateException(new StringBuffer().append("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (").append(uRL2.toString()).append(") use keys([protocol])").toString());
}
Protocol protocol = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(string);
return protocol.refer(class_, uRL);
}
}
2.cluster
public class Cluster$Adaptive
implements Cluster {
public Invoker join(Directory directory) throws RpcException {
if (directory == null) {
throw new IllegalArgumentException("org.apache.dubbo.rpc.cluster.Directory argument == null");
}
if (directory.getUrl() == null) {
throw new IllegalArgumentException("org.apache.dubbo.rpc.cluster.Directory argument getUrl() == null");
}
URL uRL = directory.getUrl();
String string = uRL.getParameter("cluster", "failover");
if (string == null) {
throw new IllegalStateException(new StringBuffer().append("Failed to get extension (org.apache.dubbo.rpc.cluster.Cluster) name from url (").append(uRL.toString()).append(") use keys([cluster])").toString());
}
Cluster cluster = (Cluster)ExtensionLoader.getExtensionLoader(Cluster.class).getExtension(string);
return cluster.join(directory);
}
}
3.ProxyFactory
public class ProxyFactory$Adaptive
implements ProxyFactory {
public Invoker getInvoker(Object object, Class class_, URL uRL) throws RpcException {
if (uRL == null) {
throw new IllegalArgumentException("url == null");
}
URL uRL2 = uRL;
String string = uRL2.getParameter("proxy", "javassist");
if (string == null) {
throw new IllegalStateException(new StringBuffer().append("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (").append(uRL2.toString()).append(") use keys([proxy])").toString());
}
ProxyFactory proxyFactory = (ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension(string);
return proxyFactory.getInvoker(object, class_, uRL);
}
public Object getProxy(Invoker invoker, boolean bl) throws RpcException {
if (invoker == null) {
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
}
if (invoker.getUrl() == null) {
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
}
URL uRL = invoker.getUrl();
String string = uRL.getParameter("proxy", "javassist");
if (string == null) {
throw new IllegalStateException(new StringBuffer().append("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (").append(uRL.toString()).append(") use keys([proxy])").toString());
}
ProxyFactory proxyFactory = (ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension(string);
return proxyFactory.getProxy(invoker, bl);
}
public Object getProxy(Invoker invoker) throws RpcException {
if (invoker == null) {
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
}
if (invoker.getUrl() == null) {
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
}
URL uRL = invoker.getUrl();
String string = uRL.getParameter("proxy", "javassist");
if (string == null) {
throw new IllegalStateException(new StringBuffer().append("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (").append(uRL.toString()).append(") use keys([proxy])").toString());
}
ProxyFactory proxyFactory = (ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension(string);
return proxyFactory.getProxy(invoker);
}
}
4.RegistryFactory
public class RegistryFactory$Adaptive
implements RegistryFactory {
public Registry getRegistry(URL uRL) {
String string;
if (uRL == null) {
throw new IllegalArgumentException("url == null");
}
URL uRL2 = uRL;
String string2 = string = uRL2.getProtocol() == null ? "dubbo" : uRL2.getProtocol();
if (string == null) {
throw new IllegalStateException(new StringBuffer().append("Failed to get extension (org.apache.dubbo.registry.RegistryFactory) name from url (").append(uRL2.toString()).append(") use keys([protocol])").toString());
}
RegistryFactory registryFactory = (RegistryFactory)ExtensionLoader.getExtensionLoader(RegistryFactory.class).getExtension(string);
return registryFactory.getRegistry(uRL);
}
}
5.Transporter
public class Transporter$Adaptive
implements Transporter {
public Client connect(URL uRL, ChannelHandler channelHandler) throws RemotingException {
if (uRL == null) {
throw new IllegalArgumentException("url == null");
}
URL uRL2 = uRL;
String string = uRL2.getParameter("client", uRL2.getParameter("transporter", "netty"));
if (string == null) {
throw new IllegalStateException(new StringBuffer().append("Failed to get extension (org.apache.dubbo.remoting.Transporter) name from url (").append(uRL2.toString()).append(") use keys([client, transporter])").toString());
}
Transporter transporter = (Transporter)ExtensionLoader.getExtensionLoader(Transporter.class).getExtension(string);
return transporter.connect(uRL, channelHandler);
}
public Server bind(URL uRL, ChannelHandler channelHandler) throws RemotingException {
if (uRL == null) {
throw new IllegalArgumentException("url == null");
}
URL uRL2 = uRL;
String string = uRL2.getParameter("server", uRL2.getParameter("transporter", "netty"));
if (string == null) {
throw new IllegalStateException(new StringBuffer().append("Failed to get extension (org.apache.dubbo.remoting.Transporter) name from url (").append(uRL2.toString()).append(") use keys([server, transporter])").toString());
}
Transporter transporter = (Transporter)ExtensionLoader.getExtensionLoader(Transporter.class).getExtension(string);
return transporter.bind(uRL, channelHandler);
}
}
6.ThreadPool
public class ThreadPool$Adaptive
implements ThreadPool {
public Executor getExecutor(URL uRL) {
if (uRL == null) {
throw new IllegalArgumentException("url == null");
}
URL uRL2 = uRL;
String string = uRL2.getParameter("threadpool", "fixed");
if (string == null) {
throw new IllegalStateException(new StringBuffer().append("Failed to get extension (org.apache.dubbo.common.threadpool.ThreadPool) name from url (").append(uRL2.toString()).append(") use keys([threadpool])").toString());
}
ThreadPool threadPool = (ThreadPool)ExtensionLoader.getExtensionLoader(ThreadPool.class).getExtension(string);
return threadPool.getExecutor(uRL);
}
}
以上几个类的自适应类会在后面的文章经常用到