1、URL数据总线
dubbo为以URL为主线的一个框架,即根据URL中的相关参数,在各个扩展点之间传递数据,自适应选择对应的spi实现类。
URL 在 dubbo 中被当做是公共契约,所有扩展点参数都包含 URL 参数(直接或间接)作为上下文信息贯穿整个扩展点设计体系。
URL 相当于参数的集合(相当于一个 Map),他所表达的含义比单个参数更丰富,当我们在扩展代码时,可以将新的参数追加到 URL 之中,而不需要改变入参,返参的结构。
统一模型,各个扩展模块都可以使用它作为参数的表达形式,简化了概念,降低了代码的理解成本。
URL类伪代码:
public class URL implements Serializable {
protected String protocol;
protected String username;
protected String password;
protected String host;
protected int port;
protected String path;
private final Map<String, String> parameters;
private final Map<String, Map<String, String>> methodParameters;
private volatile transient String ip;
private transient String address;
...
}
举例说明一下:比如在服务导出时,先根据host和port初始化一个URL对象
String host = findConfigedHosts(protocolConfig, registryURLs, map);
Integer port = findConfigedPorts(protocolConfig, name, map, protocolConfigNum);
URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);
之后在整个导出过程中会在不同的处理阶段来更改URL中的参数值,进而根据参数值的不同来自适应选择不同实现类来处理具体的逻辑
例如导出本地服务时的处理如下
URL local = URLBuilder.from(url).setProtocol(LOCAL_PROTOCOL).setHost(LOCALHOST_VALUE).setPort(0).build();
Exporter<?> exporter = PROTOCOL.export(PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, local));
先将protocol的值切换成"injvm",然后在调用PROTOCOL.export时会根据该参数值获取真正的Protocol实现类InjvmProtocol,当然并不是必须要URL对象本身直接作为参数,而是也可以URL作为实际参数的其中一个属性值,当然这里也会有Wrapper类的参与,后面会细说。
2、SPI扩展和ExtensionLoader
SPI机制(Service Provider Interface),是一种将接口与实现类分离以实现程序解耦增加可扩展性的机制。具体的实现方式是默认在META-INF/services添加一个文件名为接口全路径的配置文件,文件内容为所有扩展实现类的全路径名,并使用java.util.ServiceLoader来加载。
这种原生的SPI实现方式存在以下问题:
- 获取实现类的方式不够灵活,ServiceLoader每次加载时只能通过Iterator形式遍历,也就是接口的实现类全部加载并实例化一遍。如果你并不想用某些实现类,它也被加载并实例化了,这就造成了资源浪费。
- 多个并发多线程使用ServiceLoader类的实例是不安全的
Dubbo框架对原生SPI机制进行了扩展,可以通过key-value的方式去配置实现类,同时可以在运行时根据key动态的获取需要的实现类,避免了一次性全部加载造成了内存资源浪费。此外,增加了存放spi定义文件的目录,包括:META-INF/dubbo,META-INF/dubbo/internal,META-INF/services。
ExtensionLoader类为Dubbo框架SPI的实现类,相当于JDK的ServiceLoader,也实现了Spring的IOC功能,通过ExtensionLoader配合URL完成对应类实现的加载,是Dubbo框架高度可拓展性实现的基础。
下面以Protocol协议接口详细说明下具体实现原理
@SPI("dubbo")
public interface Protocol {
int getDefaultPort();
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
void destroy();
default List<ProtocolServer> getServers() {
return Collections.emptyList();
}
}
要想接口支持Dubbo的SPI扩展,需要满足以下三点:
- 接口使用@SPI注解;
- 至少有一个方法使用@Adaptive注解。有此注解的方法可以在运行时根据URL参数自适应调用具体实现类中的对应方法。此注解也可以用在实现类上,但是只能放在接口的一个实现类上,表示接口Adaptive类不再自动生成一个新类而是直接使用该子类。比如Dubbo里ExtensionFactory接口实现类AdaptiveExtensionFactory,后面会细说。
- 编写接口实现类。
- 在META-INF/dubbo/internal 下面添加配置文件,文件名为接口全路径名,文件内容为"Key=Value"的形式,其中Key为实现类的别名,用于跟URL中的参数值匹配,Value为实现类的全路径名。
通过以上几步配置就可以使接口支持Dubbo的SPI扩展,接下来就可以在代码中使用ExtensionLoader来开启自适应之旅,具体使用方式如下:
private static final Protocol PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
下面一步步揭开ExtensionLoader的自适应机制面纱。
首先,ExtensionLoader.getExtensionLoader(Protocol.class)的结果是获取Protocol接口的ExtensionLoader实例。在ExtensionLoader内部,每个支持SPI的接口都有一份自己的ExtensionLoader实例,保存在Map集合 EXTENSION_LOADERS中
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);
看一下ExtensionLoader的构造函数
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory =
(type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
type即要处理的接口类;
objectFactory为Extension工厂类,默认的实现类有三个,看下面类图。其中AdaptiveExtensionFactory使用的@Adaptve注解,表示该子类即ExtensionFactory的自适应类
如果type不是ExtensionFactory的话会获取ExtensionFactory的自适应类,即AdaptiveExtensionFactory,看下面AdaptiveExtensionFactory的构造函数,其主要用于加载上面两个ExtensionFactory,然后在创建完扩展类进行属性注入的时候(injectExtension方法)调用getExtension方法通过这两个工厂类来获取ExtensionLoader中被SPI注解的类实例或Spring bean工厂中不被SPI注解的实例对象,其过程类似spring的IOC.
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
private final List<ExtensionFactory> factories;
public AdaptiveExtensionFactory() {
//加载Extension工厂类实现,默认SpiExtensionFactory和SpringExtensionFactory
ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
for (String name : loader.getSupportedExtensions()) {
list.add(loader.getExtension(name));
}
factories = Collections.unmodifiableList(list);
}
@Override
public <T> T getExtension(Class<T> type, String name) {
for (ExtensionFactory factory : factories) {
//遍历工厂类并调用getExtension方法来获取实现类
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
}
接下来是重点,调用getAdaptiveExtension方法获取接口的Adaptive自适应子类。
在ExtensionLoader内部定义了很多缓存对象,其中有两个创建或获取接口adptive类的缓存
//adaptive自适应类实例缓存
private final Holder<Object> cachedAdaptiveInstance = new Holder<>();
//扩展类缓存 key为类的全路径名,value为实现类Class
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
这里有一个设计上的细节。首先缓存类型使用了一个Holder对象,其实就是简单的setget, 只不过缓存的具体对象value字段使用了volitile修饰符,其次在getAdaptiveExtension方法内部使用了双重检查,与单例模式的双重检查实现机制相同, 这样设计保证了SPI的Singleton+ThreadSafe两个特性。
public class Holder<T> {
private volatile T value; //防止指令重排序
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
}
public T getAdaptiveExtension() {
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
…
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
… }
}
}
}
return (T) instance;
}
假设现在是程序第一次获取Protocol接口的Adaptive类,即两个缓存都为空,后面会经过以下几个步骤:
-
加载所有扩展类。 Dubbo定义了三个加载策略分别为LoadingStrategy接口的三个实现类,对应上面提到的META-INF下面的三个文件路径,在 loadDirectory方法中使用类加载器遍历三个文件路径,获取所有路径下的文件名为接口的全路径名的所有文件,之后进入loadResource方法读取配置文件中的所有key-value,然后使用类的全路径名value调用Class.forName(clazz, true, classLoader)获取类的class对象,最后进入loadClass方法对Class对象做处理。
-
对Class对象的处理过程直接看注释
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name,
boolean overridden) throws NoSuchMethodException {
//1、确定此Class对象表示的类是否是type接口的子类
if (!type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Error occurred when loading extension class (interface: " +
type + ", class line: " + clazz.getName() + "), class "
+ clazz.getName() + " is not subtype of interface.");
}
//2、判断class类是否使用了Adaptive注解,如果是则存入cachedAdaptiveClass,表示该类是一个Adptive自适应类
if (clazz.isAnnotationPresent(Adaptive.class)) {
cacheAdaptiveClass(clazz, overridden);
//3、判断该类是否是Wrapper增强类,判断的依据是实现该接口并且只包含一个以该接口作为唯一参数的构造函数的类,如果是则存入cachedWrapperClasses缓存
} else if (isWrapperClass(clazz)) {
cacheWrapperClass(clazz);
} else {
clazz.getConstructor();
if (StringUtils.isEmpty(name)) {
// name为上面提到的文件中的key,如果key为空,及文件中没有按照Key=Value的形式配置,而是只有Value的话,此处需要重新获取name
// 读取实现类上的@Extension注解的value值作为name,如果为空则根据实现类和接口的SimpleName获取
name = findAnnotationName(clazz);
if (name.length() == 0) {
throw new IllegalStateException(
"No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
}
}
String[] names = NAME_SEPARATOR.split(name);
if (ArrayUtils.isNotEmpty(names)) {
// 4、获取被@Active注解的子类存入缓存cachedActivates
cacheActivateClass(clazz, names[0]);
for (String n : names) {
cacheName(clazz, n);
//5、存入extensionClasses中返回,最后是赋给缓存cachedClasses
saveInExtensionClass(extensionClasses, clazz, n, overridden);
}
}
}
}
- 经过上述过程的处理,得到所有扩展类Class对象的信息并保存到ExtensionLoader中相应的缓存中, 之后进入如下处理,还是看注释
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
//如果是Adative自适应类则直接返回
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
//否则创建
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
private Class<?> createAdaptiveExtensionClass() {
//构造自适应类代码code, 构造前先判断接口中至少有一个使用@Adative注解的方法,然后根据接口类来生成code,包含package、import以及类体和方法
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
ClassLoader classLoader = findClassLoader();
//获取编译器对象,默认使用JavassistCompiler
org.apache.dubbo.common.compiler.Compiler compiler =
ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
//执行代码编译,获取自定义类的Class对象
return compiler.compile(code, classLoader);
}
- 调用newInstance()实例化,然后调用injectExtension方法属性注入,具体的处理逻辑是获取set方法,使用前面提到的ExtensionFactory.getExtension注入对象,类似spring的IOC
private T injectExtension(T instance) {
//省略...
for (Method method : instance.getClass().getMethods()) {
//省略...
String property = getSetterProperty(method);
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
method.invoke(instance, object);
}
}
return instance;
}
最终得到的实例是一个名称以"$Adaptive"结尾的子类对象
其生成的代码
可以看到,Protocol接口的两个有@Adaptive注解的方法export和refer在自适应子类中的实现逻辑:
- 先获取URL中的Protocol协议类型;
- 再调用getExtension获取真正需要的实现类(如何获取后面细说)
- 最后使用实现类调用方法
以上,即整个自适应的过程。
3、Wrapper增强包装类
前面在分析loadClass源码时已经提到了Wrapper包装类,即实现该接口并且只包含一个以该接口作为唯一参数的构造函数的类,其作用是用于增强、装饰该接口实际提供功能的类的实例。
以Protocol接口的其中一个包装类ProtocolFilterWrapper来说明下如何实现功能增强。
@Activate(order = 100)
public class ProtocolFilterWrapper implements Protocol {
private final Protocol protocol;
public ProtocolFilterWrapper(Protocol protocol) {
if (protocol == null) {
throw new IllegalArgumentException("protocol == null");
}
this.protocol = protocol;
}
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
//省略...
}
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
//省略...
return protocol.export(buildInvokerChain(invoker, SERVICE_FILTER_KEY, CommonConstants.PROVIDER));
}
//省略...
}
主要看两点:
1、都实现同一个接口,并且wrapper类包含一个以该接口作为唯一参数的构造函数,通过构造函数把要增强的子类注入进来;
2、以export方法为例,先执行wrapper类中的增强逻辑buildInvokerChain(构造FIlter调用链),然后再调用注入的子类的相同方法,类似spring的AOP。
接下来看wrapper类是如何使用的
回到上面生成的Protocol$Adaptive类中的getExtension方法,通过该方法来获取真正需要的实现类,比如,protocol的值为“dubbo”的话,按照我们上面分析的逻辑,获取到的子类应该是DubboPrococol,但是实际上是什么呢?
看源码:
getExtension的第一步依然相同的处理方式,先取缓存,这里是cachedInstances,缓存如果没有的话则进入下面的方法
private T createExtension(String name, boolean wrap) {
// 获取实现类的Class类对象,接口所有的实现类已经在创建自适应类的时候全部保存到缓存cachedClasses中
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null || unacceptableExceptions.contains(name)) {
throw findException(name);
}
try {
//从实现类实例缓存中取
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
//缓存中如果没有则实例化并存入缓存
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.getDeclaredConstructor().newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
//IOC注入
injectExtension(instance);
//wrap默认为true, 表示加载wrap增强类
if (wrap) {
List<Class<?>> wrapperClassesList = new ArrayList<>();
if (cachedWrapperClasses != null) {
//从缓存中取所有wrapper增强类
wrapperClassesList.addAll(cachedWrapperClasses);
wrapperClassesList.sort(WrapperComparator.COMPARATOR);
Collections.reverse(wrapperClassesList);
}
if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
//遍历所有增强类
for (Class<?> wrapperClass : wrapperClassesList) {
Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
if (wrapper == null
|| (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
//使用增强类的构造函数来实例化,
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
}
}
initExtension(instance);
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
type + ") couldn't be instantiated: " + t.getMessage(), t);
}
}
重点在于if (wrap) {…}代码块中对wrapper类的处理,经过上述处理,最终得到的protocol的实例对象并不是DubboProtocol,而是一个wrapper类,如果存在多个wrapper类的话会一层层嵌套增强,最后一层才是真正的实现类DubboProtocol.
在这里我们就可以初步了解到,当执行导出或引用引用Dobbu协议的服务时都会默认依次经过以下几步处理。其他类型扩展点的增强原理与其一致。
另外,除了使用Wrapper类在SPI自适应框架中做逻辑增强,在Dubbo源码中还用到通过硬编码new实例化的形式,比如
Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler)))
4、动态编译原理
正常情况下,我们编写的java类需要先使用javac编译为class字节码文件,之后在运行时JVM使用ClassLoader类加载器把class字节码文件加载到内存,然后进行后续的创建Class对象以及创建对象实例,这个过程是由JVM启动时统一处理的。而动态编译则是在JVM进程【运行过程中】把指定的源文件编译为字节码文件,然后使用字节码文件创建对象实例。比如我们前面在分析ExtensionLoader获取适配器类DubboProtocol$Adaptive类时提到的编译器org.apache.dubbo.common.compiler.Compiler。
Dubbo提供Compiler的实现有JavassistCompiler和JdkCompiler两种,默认使用的JavassistCompiler。Dubbo框架会为每个扩展接口生成其对应的适配器类的源码,然后选择具体的动态编译类的扩展实现对源码进行编译以生成适配器类的Class对象,然后就可以调用Class对象的newInstance()方法生成扩展接口对应的适配器类的实例。
我们简单分析JavassistCompiler是如何编译生成适配器类的
public Class<?> doCompile(String name, String source) throws Throwable {
//首先初始化一个CtClassBuilder对象,用于承载类的基本信息
CtClassBuilder builder = new CtClassBuilder();
builder.setClassName(name);
// 添加import类
Matcher matcher = IMPORT_PATTERN.matcher(source);
while (matcher.find()) {
builder.addImports(matcher.group(1).trim());
}
//添加父类
matcher = EXTENDS_PATTERN.matcher(source);
if (matcher.find()) {
builder.setSuperClassName(matcher.group(1).trim());
}
// 添加实现接口信息
matcher = IMPLEMENTS_PATTERN.matcher(source);
if (matcher.find()) {
String[] ifaces = matcher.group(1).trim().split("\\,");
Arrays.stream(ifaces).forEach(i -> builder.addInterface(i.trim()));
}
// 构造函数、字段、方法
String body = source.substring(source.indexOf('{') + 1, source.length() - 1);
String[] methods = METHODS_PATTERN.split(body);
String className = ClassUtils.getSimpleClassName(name);
Arrays.stream(methods).map(String::trim).filter(m -> !m.isEmpty()).forEach(method -> {
if (method.startsWith(className)) {
builder.addConstructor("public " + method);
} else if (FIELD_PATTERN.matcher(method).matches()) {
builder.addField("private " + method);
} else {
builder.addMethod("public " + method);
}
});
// 获取ClassLoader 执行compile
ClassLoader classLoader = org.apache.dubbo.common.utils.ClassUtils.getCallerClassLoader(getClass());
//调用build方法获取Class对象
CtClass cls = builder.build(classLoader);
return cls.toClass(classLoader, JavassistCompiler.class.getProtectionDomain());
}
build的过程主要使用了javassist的ClassPool来获取一个CtClass
public CtClass build(ClassLoader classLoader) throws NotFoundException, CannotCompileException {
ClassPool pool = new ClassPool(true);
pool.appendClassPath(new LoaderClassPath(classLoader));
// create class
CtClass ctClass = pool.makeClass(className, pool.get(superClassName));
// add imported packages
imports.forEach(pool::importPackage);
// add implemented interfaces
for (String iface : ifaces) {
ctClass.addInterface(pool.get(iface));
}
// add constructors
for (String constructor : constructors) {
ctClass.addConstructor(CtNewConstructor.make(constructor, ctClass));
}
// add fields
for (String field : fields) {
ctClass.addField(CtField.make(field, ctClass));
}
// add methods
for (String method : methods) {
ctClass.addMethod(CtNewMethod.make(method, ctClass));
}
return ctClass;
}
此外,Dubbo在生成服务提供者的Invoker时,为了减少反射调用,会使用JavaAssist生产一个Wrapper类,这个Wrapper类里面最终调用服务提供者的接口实现类,Wrapper类的存在是为了减少反射的调用。当服务提供方收到消费方发来的请求后,需要根据消费者传递过来的方法名和参数反射调用服务提供者的实现类,而反射本身是有性能开销的,Dubbo把每个服务提供者的实现类通过JavaAssist包装为一个Wrapper类以减少反射调用开销。
获取Invoker的代码如下,可以看到当服务提供者provider的Invoker执行时会进入doInvoke内部时,真正的执行逻辑是调用wrapper类的invokeMethod方法
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
// TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
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);
}
};
}
下面我们看看这个invokeMethod干了点啥。
简单写个测试接口和实现类:
public interface UserService {
String getName();
String sayHi(String name);
}
public class UserServiceImpl implements UserService {
private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
@Override
public String getName() {
return "provider name";
}
@Override
public String sayHi(String name) {
return "hi " + name;
}
}
生成的Wrapper类如下:
package org.apache.dubbo.common.bytecode;
import com.example.springboot.dubbo.dubboprovider.service.UserServiceImpl;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import org.apache.dubbo.common.bytecode.ClassGenerator;
import org.apache.dubbo.common.bytecode.NoSuchMethodException;
import org.apache.dubbo.common.bytecode.NoSuchPropertyException;
import org.apache.dubbo.common.bytecode.Wrapper;
public class Wrapper1 extends Wrapper implements ClassGenerator.DC {
public static String[] pns;
public static Map pts;
public static String[] mns;
public static String[] dmns;
public static Class[] mts0;
public static Class[] mts1;
@Override
public String[] getPropertyNames() {
return pns;
}
@Override
public boolean hasProperty(String string) {
return pts.containsKey(string);
}
public Class getPropertyType(String string) {
return (Class)pts.get(string);
}
@Override
public String[] getMethodNames() {
return mns;
}
@Override
public String[] getDeclaredMethodNames() {
return dmns;
}
@Override
public void setPropertyValue(Object object, String string, Object object2) {
try {
UserServiceImpl userServiceImpl = (UserServiceImpl)object;
}
catch (Throwable throwable) {
throw new IllegalArgumentException(throwable);
}
throw new NoSuchPropertyException(new StringBuffer().append("Not found property \"").append(string).append("\" field or setter method in class com.example.springboot.dubbo.dubboprovider.service.UserServiceImpl.").toString());
}
@Override
public Object getPropertyValue(Object object, String string) {
UserServiceImpl userServiceImpl;
try {
userServiceImpl = (UserServiceImpl)object;
}
catch (Throwable throwable) {
throw new IllegalArgumentException(throwable);
}
if (string.equals("name")) {
return userServiceImpl.getName();
}
throw new NoSuchPropertyException(new StringBuffer().append("Not found property \"").append(string).append("\" field or getter method in class com.example.springboot.dubbo.dubboprovider.service.UserServiceImpl.").toString());
}
public Object invokeMethod(Object object, String string, Class[] classArray, Object[] objectArray) throws InvocationTargetException {
UserServiceImpl userServiceImpl;
try {
userServiceImpl = (UserServiceImpl)object;
}
catch (Throwable throwable) {
throw new IllegalArgumentException(throwable);
}
try {
if ("getName".equals(string) && classArray.length == 0) {
return userServiceImpl.getName();
}
if ("sayHi".equals(string) && classArray.length == 1) {
return userServiceImpl.sayHi((String)objectArray[0]);
}
}
catch (Throwable throwable) {
throw new InvocationTargetException(throwable);
}
throw new NoSuchMethodException(new StringBuffer().append("Not found method \"").append(string).append("\" in class com.example.springboot.dubbo.dubboprovider.service.UserServiceImpl.").toString());
}
}
可以看到,invokeMethod实际上是直接执行实现类userServiceImpl的方法,因为Wrapper类是在Dubbo服务启动时生成的,没有在运行时对实现类再做反射调用,从而避免了反射开销。
5、Dubbo分层架构体系
Dubbo的整体架构采用分层设计,每层执行固定的功能,上层依赖下层提供的功能,下层的改变对上层不可见,并且每层都是一个可被替换的组件。
下图是官方提供的架构图
- Service和Config层为API接口层,是为了让Dubbo使用方方便地发布服务和引用服务;对于服务提供方来说需要实现服务接口,然后使用ServiceConfig来发布该服务;对于服务消费方来说需要使用ReferenceConfig对服务接口进行代理。Dubbo服务发布与引用方可以直接初始化配置类,也可以通过Spring配置自动生成配置类。
其他各层均为SPI层,均为组件化,可替换,具有高度可扩展性。
-
Proxy服务代理层:该层主要是对服务消费端使用的接口进行代理,把本地调用透明地转换为远程调用;另外对服务提供方的服务实现类进行代理,把服务实现类转换为Wrapper类,这是为了减少反射的调用。Proxy层的SPI扩展接口为ProxyFactory,Dubbo提供的实现类主要有JavassistProxyFactory(默认使用)和JdkProxyFactory,用户可以实现ProxyFactory SPI接口,自定义代理服务层的实现。
-
Registry服务注册中心层:服务提供者启动时会把服务注册到服务注册中心,消费者启动时会去服务注册中心获取服务提供者的地址列表,Registry层主要功能是封装服务地址的注册与发现逻辑,扩展接口Registry对应的扩展实现为ZookeeperRegistry、RedisRegistry、MulticastRegistry、DubboRegistry等。扩展接口RegistryFactory对应的扩展接口实现为DubboRegistryFactory、DubboRegistryFactory、RedisRegistryFactory、ZookeeperRegistryFactory。另外,该层扩展接口Directory实现类有RegistryDirectory、StaticDirectory,用来透明地把Invoker列表转换为一个Invoker;用户可以实现该层的一系列扩展接口,自定义该层的服务实现。
-
Cluster路由层:封装多个服务提供者的路由规则、负载均衡、集群容错的实现,并桥接服务注册中心;扩展接口Cluster对应的实现类有FailoverCluster(失败重试)、FailbackCluster(失败自动恢复)、FailfastCluster(快速失败)、FailsafeCluster(失败安全)、ForkingCluster(并行调用)等;负载均衡扩展接口LoadBalance对应的实现类为RandomLoadBalance(随机)、RoundRobinLoadBalance(轮询)、LeastActiveLoadBalance(最小活跃数)、ConsistentHashLoadBalance(一致性Hash)等。用户可以实现该层的一系列扩展接口,自定义集群容错和负载均衡策略。
-
Monitor监控层:用来统计RPC调用次数和调用耗时时间,扩展接口为MonitorFactory,对应的实现类为DubboMonitorFactroy。用户可以实现该层的MonitorFactory扩展接口,实现自定义监控统计策略。
-
Protocol远程调用层:封装RPC调用逻辑,扩展接口为Protocol,对应实现有RegistryProtocol、DubboProtocol、InjvmProtocol等
-
Exchange信息交换层:封装请求响应模式,同步转异步,扩展接口为Exchanger,对应的扩展实现有HeaderExchanger等。
-
Transport网络传输层:Mina和Netty抽象为统一接口。扩展接口为Channel,对应的实现有NettyChannel(默认)、MinaChannel等;扩展接口Transporter对应的实现类有GrizzlyTransporter、MinaTransporter、NettyTransporter(默认实现);扩展接口Codec2对应的实现类有DubboCodec、ThriftCodec等。
-
Serialize数据序列化层:提供可以复用的一些工具,扩展接口为Serialization,对应的扩展实现有DubboSerialization、FastJsonSerialization、Hessian2Serialization、JavaSerialization等,扩展接口ThreadPool对应的扩展实现有FixedThreadPool、CachedThreadPool、LimitedThreadPool等。