今天来就带大家看看 Dubbo 服务暴露过程,这个过程在 Dubbo 中其实是很核心的过程之一,关乎到你的 Provider 如何能被 Consumer 得知并调用。
今天还是会进行源码解析,毕竟我们需要深入的去了解 Dubbo 是如何做的,只有深入它才能了解它。
如果要实现服务发布和注册,需要做哪些事情?
- 1. 配置文件解析或者注解解析
- 2. 服务注册
- 3. 启动 netty 服务实现远程监听
一、前奏 URL
不过在进行服务暴露流程分析之前有必要先谈一谈 URL,有人说这 URL 和 Dubbo 啥关系?有关系,有很大的关系!
一般而言我们说的 URL 指的就是统一资源定位符,在网络上一般指代地址,本质上看其实就是一串包含特殊格式的字符串,标准格式如下:
protocol://username:password@host:port/path?key=value&key=value
Dubbo 就是采用 URL 的方式来作为约定的参数类型,被称为公共契约,就是我们都通过 URL 来交互,来交流。
你想一下如果没有一个约束,没有指定一个都公共的契约那么不同的接口就会以不同的参数来传递信息,一会儿用 Map、一会儿用特定分隔的字符串,这就是导致整体很乱,并且解析不能统一。
而用了一个统一的契约之后,那么代码就更加的规范化、形成一种统一的格式,所有人对参数就一目了然,不用去揣测一些参数的格式等等。
而且用 URL 作为一个公共约束充分的利用了我们对已有概念的印象,通俗易懂并且容易扩展,我们知道 URL 要加参数只管往后面拼接就完事儿了。
因此 Dubbo 用 URL 作为配置总线,贯穿整个体系,源码中 URL 的身影无处不在。
URL 具体的参数如下:
-
protocol:指的是 dubbo 中的各种协议,如:dubbo thrift http
-
username/password:用户名/密码
-
host/port:主机/端口
-
path:接口的名称
-
parameters:参数键值对
二、配置解析
2.1、Dubbo 对于 sping 的扩展
最早我们使用 Spring 的配置,来实现 dubbo 服务的发布,方便大家的同时,也意味着 Dubbo 里面和 Spring 肯定有那种说不清的关系。
2.1.1、Spring 的标签扩展
在 spring 中定义了两个接口
NamespaceHandler: 注册一堆 BeanDefinitionParser,利用他们来进行解析
BeanDefinitionParser:用于解析每个 element 的内容。
Spring 默认会加载 jar 包下的 META-INF/spring.handlers 文件寻找对应的 NamespaceHandler。 Dubbo-config 模块下的 dubboconfig-spring\src\main\resources\META-INF\spring.handlers。
2.2、Dubbo 的实现
Dubbo 中 spring 扩展就是使用 spring 的自定义类型,所以同样也有 NamespaceHandler、BeanDefinitionParser。而 NamespaceHandler 是 DubboNamespaceHandler。
public class DubboNamespaceHandler extends NamespaceHandlerSupport {
static {
Version.checkDuplicate(DubboNamespaceHandler.class);
}
@Override
public void init() {
registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class, true));
registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class, true));
registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class, true));
registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.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());
}
}
BeanDefinitionParser 全部都使用了 DubboBeanDefinitionParser,如果我们想看 dubbo:service 的配置,就直接看 DubboBeanDefinitionParser(ServiceBean.class,true)。
这个里面主要做了一件事,把不同的配置分别转化成 spring 容器中的 bean 对象。
application 对应 ApplicationConfig
registry 对应 RegistryConfig
monitor 对应 MonitorConfig
provider 对应 ProviderConfig
consumer 对应 ConsumerConfig
我们仔细看,发现涉及到服务发布和服务调用的两个配置的解析,使用的是 ServiceBean 和 referenceBean。并不是 config 结尾 的,这两个类稍微特殊些,当然他同时也继承了 ServiceConfig 和 ReferenceConfig。
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true)); registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
DubboBeanDefinitionParser
这里面是实现具体配置文件解析的入口,它重写了 parse 方法,对 spring 的配置进行解析。我们关注一下 ServiceBean 的解析. 实际就是解析 dubbo:service 这个标签中对应的属性。
// 解析 dubbo:service 这个标签中对应的属性
else if (ServiceBean.class.equals(beanClass)) {
String className = element.getAttribute("class");
if (className != null && className.length() > 0) {
//封装BeanDefinition 对象
RootBeanDefinition classDefinition = new RootBeanDefinition();
classDefinition.setBeanClass(ReflectUtils.forName(className));
classDefinition.setLazyInit(false);
parseProperties(element.getChildNodes(), classDefinition);
beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
}
}
2.3、ServiceBean 的实现
ServiceBean 这个类,分别实现了 InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener, BeanNameAware, ApplicationEventPublisherAware
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,
ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, BeanNameAware,
ApplicationEventPublisherAware {}
InitializingBean
接口为 bean 提供了初始化方法的方式,它只包括 afterPropertiesSet 方法,凡是继承该接口的类,在初始化 bean 的时候会执行 该方法。被重写的方法为 afterPropertiesSet。
DisposableBean
被重写的方法为 destroy,bean 被销毁的时候,spring 容器会自动执行 destory 方法,比如释放资源。
ApplicationContextAware
实现了这个接口的 bean,当 spring 容器初始化的时候,会自动的将 ApplicationContext 注入进来
ApplicationListener