背景
在dubbo的使用过程中,我们更多的是结合基于spring容器整合,既然有用到spring,dubbo的对象初始化自然也会托管到容器上。
spring容器启动过程
这块不是文章重点,画一张流程图,我把dubbo 切入的时间点标注上,红色背景的流程是xml配置方式的启动事件,绿色背景的是注解形式的启动事件,我们只分析xml的配置方式。
spring 自定义标签解析
自定义标签解析主要是扩展spring 的handlers和schemas(这两个文件在dubbo包下面的/META-INF)下面,是spring自定义标签的扩展规范,我们看看spring.handlers,还做了alibaba的包兼容
http\://dubbo.apache.org/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
http\://code.alibabatech.com/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
点开类 DubboNamespaceHandler,registerBeanDefinitionParser方法会将比如
public class DubboNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
//......
registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
}
ServiceBean的事件处理
这里我们看看 ServiceBean 类,实现了很多spring能力,其中 ApplicationListener
public class ServiceBean extends ServiceConfig implements InitializingBean, DisposableBean,
ApplicationContextAware, ApplicationListener, BeanNameAware,
ApplicationEventPublisherAware {
//org.apache.dubbo.config.spring.ServiceBean#onApplicationEvent
public void onApplicationEvent(ContextRefreshedEvent event) {
if (!isExported() && !isUnexported()) {
export();
}
}
//org.apache.dubbo.config.spring.ServiceBean#export
public void export() {
super.export();
// 发布 ServiceBeanExportedEvent 服务暴露事件
publishExportEvent();
}
//org.apache.dubbo.config.ServiceConfig#export
public synchronized void export() {//线程安全
checkAndUpdateSubConfigs();//检查配置
if (shouldDelay()) {
//延迟启动,如果有空dubbo2.4.8 之前的版本这边还是用的Thread.sleep()很粗暴
DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
} else {
doExport();//服务暴露
}
}
doExport(); 方法是服务暴露,这里我们后面文章分析,checkAndUpdateSubConfigs();这个方法才是我们关心的
初始化
dubbo 在暴露之前还有一系列的初始化工作
//org.apache.dubbo.config.ServiceConfig#checkAndUpdateSubConfigs
public void checkAndUpdateSubConfigs() {
// Use default configs defined explicitly on global configs
completeCompoundConfigs();
// 启动配置中心
startConfigCenter();
checkDefault();
checkProtocol(); //检查协议配置
checkApplication();//检查应用配置 下面有说明
// if protocol is not injvm checkRegistry
if (!isOnlyInJvm()) { //下面有说明
checkRegistry();
}
this.refresh();//从配置中心刷新配置
checkMetadataReport();
if (ref instanceof GenericService) {//下面有说明
interfaceClass = GenericService.class;
if (StringUtils.isEmpty(generic)) {
generic = Boolean.TRUE.toString();
}
} else {
try {
interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
.getContextClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
checkInterfaceAndMethods(interfaceClass, methods);
checkRef();
generic = Boolean.FALSE.toString();
}
if (local != null) {
if ("true".equals(local)) {
local = interfaceName + "Local";
}
Class> localClass;
try {
localClass = ClassUtils.forNameWithThreadContextClassLoader(local);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
if (!interfaceClass.isAssignableFrom(localClass)) {
throw new IllegalStateException("The local implementation class " + localClass.getName() + " not implement interface " + interfaceName);
}
}
if (stub != null) {
if ("true".equals(stub)) {
stub = interfaceName + "Stub";
}
Class> stubClass;
try {
stubClass = ClassUtils.forNameWithThreadContextClassLoader(stub);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
if (!interfaceClass.isAssignableFrom(stubClass)) {
throw new IllegalStateException("The stub implementation class " + stubClass.getName() + " not implement interface " + interfaceName);
}
}
//存根验证
checkStubAndLocal(interfaceClass);
//mock验证 下面分析
checkMock(interfaceClass);
}
应用配置检查
checkApplication();
//org.apache.dubbo.config.AbstractInterfaceConfig#checkApplication
protected void checkApplication() {
// for backward compatibility
createApplicationIfAbsent();
if (!application.isValid()) {
throw new IllegalStateException("No application config found or it's not a valid config! " +
"Please add to your spring config.");
}
ApplicationModel.setApplication(application.getName());
这边有对application 进行判空,那么application 是在哪里赋值的呢,我们回到ServiceBean 这里实现了InitializingBean,等bean初始化完成之后会调用它的 afterPropertiesSet() 钩子方法,方法大致逻辑是从spring 的context 获取对应的bean 然后赋值
isOnlyInJvm 检查
if (!isOnlyInJvm()) { //
checkRegistry();
}
如果
泛化
if (ref instanceof GenericService) {
interfaceClass = GenericService.class;
if (StringUtils.isEmpty(generic)) {
generic = Boolean.TRUE.toString();
}
}
dubbo 泛化用的可能比较少,但是多半都听过主要是为了“省事”吧,我这样理解,业内经常有这样用法,网关不依赖dubbo api 包,通过泛化来实现调用,这样也避免频繁更新网关导致的上线下,二者也不需要做兼容,这个只是dubbo 消费者做的泛化,在dubbo生产者中也可以使用泛化,比如这样
public class MyGenericService implements GenericService {
public Object $invoke(String methodName, String[] parameterTypes, Object[] args) throws GenericException {
if ("sayHello".equals(methodName)) {
return "Welcome " + args[0];
} elseif()//.....
}}
这样服务端也不需要麻烦,而且对于暴露到zk的节点字节也会减少,因为这样没有了方法的记录。所以dubbo 泛化在生产者,消费者都是适用的。
MOCK
checkMock(interfaceClass);这个mock 并不是接口api没开发之前mock给前端的意思,mock主要是用在生产者调用失败的时候执行的逻辑,比如调用超时,然后希望返回一段自己的临时数据,不希望app白屏,优点熔断的味道。
//返回值
//抛异常
上面有三种mock形式,第一种 mock 的类名一定要Mock结尾也要同样实现api接口,真是有种感觉dubbo里面什么都有。
总结
水文终于写完了,后面 服务暴露才是重点。