背景
在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方法会将比如<dubbo: service />标签解析为 ServiceBean class放到BeanDefination中,等spring初始化的时候容器里面就会注入ServiceBean对象,在初始化阶段会将我们配置文件中的<dubbo:service interface="" ref="" id =""> 这些键值对也放到ServiceBean对象的属性中去,这块不是重点我们不再深入。
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<ContextRefreshedEvent> 也就是说spring初始化完成后会调用该对象的事件回调方法onApplicationEvent(),我们点进去
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,
ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, 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 <dubbo:application name=\"...\" /> to your spring config.");
}
ApplicationModel.setApplication(application.getName());
这边有对application 进行判空,那么application 是在哪里赋值的呢,我们回到ServiceBean 这里实现了InitializingBean,等bean初始化完成之后会调用它的 afterPropertiesSet() 钩子方法,方法大致逻辑是从spring 的context 获取对应的bean 然后赋值
isOnlyInJvm 检查
if (!isOnlyInJvm()) { //
checkRegistry();
}
如果<dubbo:protocol name="injvm"> 代表只暴露在jvm,怎么理解呢,如果服务是消费者,但是生产者也是在同一个服务中启动,那么调用是否还需要经过socket,当然是不用的,那么问题又来了,那为啥不直接走bean调用,答案是bean调用不能走dubbo 的调用流程,dubbo有一系列的filter wrapper 等,在统计限流缓存上面都有扩展,只有在injvm上才会走到,单纯的bean调用不会走到。
泛化
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白屏,优点熔断的味道。
<dubbo:service mock="com.poizon.study.api.service.HelloServiceMock"
interface="com.poizon.study.api.service.HelloService" ref="helloService"/>
//返回值
<dubbo:service mock="return 123;"
interface="com.poizon.study.api.service.HelloService" ref="helloService"/>
//抛异常
<dubbo:service mock="throw new Exception()"
interface="com.poizon.study.api.service.HelloService" ref="helloService"/>
上面有三种mock形式,第一种 mock 的类名一定要Mock结尾也要同样实现api接口,真是有种感觉dubbo里面什么都有。
总结
水文终于写完了,后面 服务暴露才是重点。