一、Dubbo简介
总体架构:
分层结构:
Dubbo核心领域模型:
- Protocol是服务域,它是Invoker暴露和引用的主功能入口,它负责Invoker的生命周期管理;
- Invoker是实体域,它是Dubbo的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起invoke调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现;
- Invocation是会话域,它持有调用过程中的变量,比如方法名,参数等。
基本设计原则:
- 采用Microkernel+Plugin模式,Microkernel只负责组装Plugin,Dubbo自身的功能也是通过扩展点实现的,也就是Dubbo的所有功能点都可被用户自定义扩展所替换;
- 采用URL作为配置信息的统一格式,所有扩展点都通过传递URL携带配置信息。
SPI(Service Provider Interface),是JDK内置的一个服务发现机制,它使得接口和具体实现完全解耦。我们只声明接口,具体的实现类在配置中选择。
服务暴露/消费者调用过程:
框架设计:https://dubbo.apache.org/zh/docs/v2.7/dev/design/
二、Dubbo SPI
通过ExtensionLoader的getExtensionLoader方法获取一个ExtensionLoader实例,然后再通过ExtensionLoader的getExtension方法获取拓展类对象。
public static ExtensionLoader getExtensionLoader(Class type) {
if (type == null) {
throw new IllegalArgumentException("Extension type == null");
} else if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
} else if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
} else {
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;
}
}
public T getExtension(String name) {
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
if ("true".equals(name)) {
// 获取默认的拓展实现类
return getDefaultExtension();
}
// Holder,顾名思义,用于持有目标对象
Holder
private T injectExtension(T instance) {
try {
if (objectFactory != null) {
// 遍历目标类的所有方法
for (Method method : instance.getClass().getMethods()) {
// 检测方法是否以 set 开头,且方法仅有一个参数,且方法访问级别为 public
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
// 获取 setter 方法参数类型
Class> pt = method.getParameterTypes()[0];
try {
// 获取属性名,比如 setName 方法对应属性名 name
String property = method.getName().length() > 3 ?
method.getName().substring(3, 4).toLowerCase() +
method.getName().substring(4) : "";
// 从 ObjectFactory 中获取依赖对象
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
// 通过反射调用 setter 方法设置依赖
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("fail to inject via method...");
}
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
三、SPI自适应扩展
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
String[] value() default {};
}
当Adaptive注解在类上时,Dubbo不会为该类生成代理类。注解在方法(接口方法)上时,Dubbo则会为该方法生成代理逻辑。Adaptive注解在类上的情况很少,在Dubbo中,仅有两个类被 Adaptive注解了,分别是AdaptiveCompiler和AdaptiveExtensionFactory。此种情况,表示拓展的加载逻辑由人工编码完成。更多时候,Adaptive是注解在接口方法上的,表示拓展的加载逻辑需由框架自动生成。
public T getAdaptiveExtension() {
// 从缓存中获取自适应拓展
Object instance = cachedAdaptiveInstance.get();
if (instance == null) { // 缓存未命中
if (createAdaptiveInstanceError == null) {
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
// 创建自适应拓展
instance = createAdaptiveExtension();
// 设置自适应拓展到缓存中
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
createAdaptiveInstanceError = t;
throw new IllegalStateException("fail to create adaptive instance: ...");
}
}
}
} else {
throw new IllegalStateException("fail to create adaptive instance: ...");
}
}
return (T) instance;
}
private T createAdaptiveExtension() {
try {
// 获取自适应拓展类,并通过反射实例化
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extension ...");
}
}
四、服务导出
服务导出的入口方法是ServiceBean的onApplicationEvent。onApplicationEvent是一个事件响应方法,该方法会在收到Spring上下文刷新事件后执行服务导出操作。
public void onApplicationEvent(ContextRefreshedEvent event) {
// 是否有延迟导出 && 是否已导出 && 是不是已被取消导出
if (isDelay() && !isExported() && !isUnexported()) {
// 导出服务
export();
}
}
ServiceBean是Dubbo与Spring框架进行整合的关键,可以看做是两个框架之间的桥梁。具有同样作用的类还有ReferenceBean。
采用URL作为配置信息的统一格式,所有扩展点都通过传递URL携带配置信息。
public synchronized void export() {
if (provider != null) {
// 获取 export 和 delay 配置
if (export == null) {
export = provider.getExport();
}
if (delay == null) {
delay = provider.getDelay();
}
}
// 如果 export 为 false,则不导出服务
if (export != null && !export) {
return;
}
// delay > 0,延时导出服务
if (delay != null && delay > 0) {
delayExportExecutor.schedule(new Runnable() {
@Override
public void run() {
doExport();
}
}, delay, TimeUnit.MILLISECONDS);
// 立即导出服务
} else {
doExport();
}
}
private void doExportUrls() {
// 加载注册中心链接
List registryURLs = loadRegistries(true);
// 遍历 protocols,并在每个协议下导出服务
for (ProtocolConfig protocolConfig : protocols) {
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
Invoker 是实体域,它是Dubbo的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起invoke调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。
public Invoker getInvoker(T proxy, Class type, URL url) {
// 为目标类创建 Wrapper
final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
// 创建匿名 Invoker 类对象,并实现 doInvoke 方法。
return new AbstractProxyInvoker(proxy, type, url) {
@Override
protected Object doInvoke(T proxy, String methodName,
Class>[] parameterTypes,
Object[] arguments) throws Throwable {
// 调用 Wrapper 的 invokeMethod 方法,invokeMethod 最终会调用目标方法
return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
}
};
}
五、服务引用
Dubbo 服务引用的时机有两个:
- 在 Spring容器调用ReferenceBean的afterPropertiesSet 方法时引用服务(饿汉式)
- 在ReferenceBean对应的服务被注入到其他类中时引用(懒汉式,默认)
先进行配置检查与收集工作。接着根据收集到的信息决定服务用的方式,有三种:
- 第一种是引用本地 (JVM) 服务
- 第二是通过直连方式引用远程服务
- 第三是通过注册中心引用远程服务
服务引用的入口方法为ReferenceBean的getObject 方法,该方法定义在Spring的FactoryBean接口中,ReferenceBean实现了这个方法。
public Object getObject() throws Exception {
return get();
}
public synchronized T get() {
if (destroyed) {
throw new IllegalStateException("Already destroyed!");
}
// 检测 ref 是否为空,为空则通过 init 方法创建
if (ref == null) {
// init 方法主要用于处理配置,以及调用 createProxy 生成代理类
init();
}
return ref;
}
Invoker是Dubbo的核心模型,代表一个可执行体。在服务提供方,Invoker用于调用服务提供类。在服务消费方,Invoker用于执行远程调用。
public Invoker refer(Class serviceType, URL url) throws RpcException {
optimizeSerialization(url);
// 创建 DubboInvoker
DubboInvoker invoker = new DubboInvoker(serviceType, url, getClients(url), invokers);
invokers.add(invoker);
return invoker;
}