Dubbo 服务引入过程解析
Dubbo 引入服务的方式是在对应的接口成员变量上加上@DubboReference注解来实现本地的调用。
服务引入整个类的架构和服务暴露类似,Provider Service对应的ServiceBean,Consumer Service对应的是ReferenceBean
整个服务的引入的过程概括起来就是两个步骤,分别是:
- 扫描所有bean中的DubboReference注解并根据接口构建对应的ReferenceBean
- 通过ReferenceBean获取对应的服务代理对象
DubboReference 扫描
这里要分析的是ReferenceAnnotationBeanPostProcessor,Dubbo在ReferenceAnnotationBeanPostProcessor上分别实现了Spring三个接口下方法,分别是
- BeanFactoryPostProcessor的postProcessBeanFactory方法
- InstantiationAwareBeanPostProcessor的postProcessPropertyValues方法
- MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition方法
这三个方法都会触发bean中的DubboReference注解扫描,同时会注册对应的ReferenceBean
ReferenceBean生成
根据Spring容器刷新的顺序,BeanFactoryPostProcessor接口会优先被执行,先来看下 Dubbo 是针对于这个接口来进行bean的一个扫描加载的
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 获取容器内所有被扫描加载到的bean名称
String[] beanNames = beanFactory.getBeanDefinitionNames();
// 遍历
for (String beanName : beanNames) {
Class<?> beanType;
// 判断是否FactoryBean
if (beanFactory.isFactoryBean(beanName)){
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
// 校验Bean是否为ReferenceBean本身,是则跳过
if (isReferenceBean(beanDefinition)) {
continue;
}
// 校验是否是Configuration中通过@Bean注解声明出来的Bean,同时携带了@DubboRefernce注解,即自定义ReferenceBean
if (isAnnotatedReferenceBean(beanDefinition)) {
// process @DubboReference at java-config @bean method
// 处理自定义ReferenceBean
processReferenceAnnotatedBeanDefinition(beanName, (AnnotatedBeanDefinition) beanDefinition);
continue;
}
// 获取bean的class全限定名
String beanClassName = beanDefinition.getBeanClassName();
// 通过ClassLoader获取bean对应的class
beanType = ClassUtils.resolveClass(beanClassName, getClassLoader());
} else {
// 普通bean直接从容器内获取class
beanType = beanFactory.getType(beanName);
}
if (beanType != null) {
// 解析class获取加上了@DubboReference注解的方法或成员变量并封装成dubbo的元数据
AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null);
try {
// 装配ReferenceBean并注册到Spring中
prepareInjection(metadata);
} catch (BeansException e) {
throw e;
} catch (Exception e) {
throw new IllegalStateException("Prepare dubbo reference injection element failed", e);
}
}
}
if (beanFactory instanceof AbstractBeanFactory) {
List<BeanPostProcessor> beanPostProcessors = ((AbstractBeanFactory) beanFactory).getBeanPostProcessors();
for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
if (beanPostProcessor == this) {
// This bean has been registered as BeanPostProcessor at org.apache.dubbo.config.spring.context.DubboInfraBeanRegisterPostProcessor.postProcessBeanFactory()
// so destroy this bean here, prevent register it as BeanPostProcessor again, avoid cause BeanPostProcessorChecker detection error
beanDefinitionRegistry.removeBeanDefinition(BEAN_NAME);
break;
}
}
}
try {
// this is an early event, it will be notified at org.springframework.context.support.AbstractApplicationContext.registerListeners()
// 发布DubboConfig初始化事件
applicationContext.publishEvent(new DubboConfigInitEvent(applicationContext));
} catch (Exception e) {
// if spring version is less than 4.2, it does not support early application event
logger.warn("publish early application event failed, please upgrade spring version to 4.2.x or later: " + e);
}
}
继续看 Bean 的 Class解析以及AnnotatedInjectionMetadata的组装
protected AnnotatedInjectionMetadata findInjectionMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
// 根据bean名称构建缓存key
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// Quick check on the concurrent map first, with minimal locking.
// 校验缓存中是否已经存在bean对应的AnnotatedInjectionMetadata
AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
// 检查bean对应的class是否更变过,如果更变过代表需要重新解析生成InjectionMetadata
if (needsRefreshInjectionMetadata(metadata, clazz)) {
// 双重检索
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (needsRefreshInjectionMetadata(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
try {
// 解析clazz并生成AnnotatedInjectionMetadata
metadata = buildAnnotatedMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
} catch (NoClassDefFoundError err) {
throw new IllegalStateException("Failed to introspect object class [" + clazz.getName() +
"] for annotation metadata: could not find class that it depends on", err);
}
}
}
}
return metadata;
}
解析 Class 并生成AnnotatedInjectionMetadata
private AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class<?> beanClass) {
// 获取所有
Collection<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> fieldElements = findFieldAnnotationMetadata(beanClass);
Collection<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> methodElements = findAnnotatedMethodMetadata(beanClass);
return new AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements);
}
获取 Class 中所有加了DubboReference.class,Reference.class,com.alibaba.dubbo.config.annotation.Reference.class
三个注解的成员变量
解析完所有带有Reference注解后代表已经找到需要包装生成服务类,并注入的服务了,接下来就进入注入阶段了,来看prepareInjection
protected void prepareInjection(AnnotatedInjectionMetadata metadata) throws BeansException {
try {
//find and register bean definition for @DubboReference/@Reference
// 遍历所有扫描出来的所有需要注入的字段元素,也就是成员变量加了DubboReference注解的对象
for (AnnotatedFieldElement fieldElement : metadata.getFieldElements()) {
// 检查是否完成过注入,即需要注入的变量已经有对应的实例了,防止重复注入
if (fieldElement.injectedObject != null) {
continue;
}
// 获取需要注入的服务class信息
Class<?> injectedType = fieldElement.field.getType();
// 获取dubboRefernce注解配置的相关信息,例如{protocol、scope等}
AnnotationAttributes attributes = fieldElement.attributes;
// 注册referenceBean,即完成引入
String referenceBeanName = registerReferenceBean(fieldElement.getPropertyName(), injectedType, attributes, fieldElement.field);
// 关联注入的字段与引入的服务,避免重复加载
fieldElement.injectedObject = referenceBeanName;
// 缓存
injectedFieldReferenceBeanCache.put(fieldElement, referenceBeanName);
}
// 与成员变量注入方式类似,只不过此处是通过set方法设置服务
for (AnnotatedMethodElement methodElement : metadata.getMethodElements()) {
if (methodElement.injectedObject != null) {
continue;
}
Class<?> injectedType = methodElement.getInjectedType();
AnnotationAttributes attributes = methodElement.attributes;
String referenceBeanName = registerReferenceBean(methodElement.getPropertyName(), injectedType, attributes, methodElement.method);
//associate methodElement and reference bean
methodElement.injectedObject = referenceBeanName;
injectedMethodReferenceBeanCache.put(methodElement, referenceBeanName);
}
} catch (ClassNotFoundException e) {
throw new BeanCreationException("prepare reference annotation failed", e);
}
}
接下来来看真正的服务引入入口registerReferenceBean
,此方法作用是根据解析出来的服务类信息、服务引入相关参数来进行ReferenceBean的注解,然后由ReferenceBean完成服务的引入和注入
public String registerReferenceBean(String propertyName, Class<?> injectedType, Map<String, Object> attributes, Member member) throws BeansException {
boolean renameable = true;
// 检查是否配置了id参数,如果是则代表是否允许重命名
String referenceBeanName = getAttribute(attributes, ReferenceAttributes.ID);
// 配置过id参数的,则以id对应的属性值为引入的Bean名称,否则重命名,以字段名作为引入的服务bean名称
if (hasText(referenceBeanName)) {
renameable = false;
} else {
referenceBeanName = propertyName;
}
String checkLocation = "Please check " + member.toString();
// 适配Reference注解中配置的属性值,转换成ReferenceBean需要的属性值(名称或者值)
ReferenceBeanSupport.convertReferenceProps(attributes, injectedType);
// 获取接口名称,来源于{reference.interfaceName或reference.interfaceClass或filed.class}
String interfaceName = (String) attributes.get(ReferenceAttributes.INTERFACE);
if (StringUtils.isBlank(interfaceName)) {
throw new BeanCreationException("Need to specify the 'interfaceName' or 'interfaceClass' attribute of '@DubboReference' if enable generic. " + checkLocation);
}
// check reference key
String referenceKey = ReferenceBeanSupport.generateReferenceKey(attributes, applicationContext);
// 检查服务是否已被加载过
List<String> registeredReferenceBeanNames = referenceBeanManager.getBeanNamesByKey(referenceKey);
if (registeredReferenceBeanNames.size() > 0) {
// found same name and reference key
if (registeredReferenceBeanNames.contains(referenceBeanName)) {
return referenceBeanName;
}
}
boolean isContains;
if ((isContains = beanDefinitionRegistry.containsBeanDefinition(referenceBeanName)) || beanDefinitionRegistry.isAlias(referenceBeanName)) {
String preReferenceBeanName = referenceBeanName;
if (!isContains){
// Look in the alias for the origin bean name
String[] aliases = beanDefinitionRegistry.getAliases(referenceBeanName);
if (ArrayUtils.isNotEmpty(aliases)) {
for (String alias : aliases) {
if (beanDefinitionRegistry.containsBeanDefinition(alias)) {
preReferenceBeanName = alias;
break;
}
}
}
}
BeanDefinition prevBeanDefinition = beanDefinitionRegistry.getBeanDefinition(preReferenceBeanName);
String prevBeanType = prevBeanDefinition.getBeanClassName();
String prevBeanDesc = referenceBeanName + "[" + prevBeanType + "]";
String newBeanDesc = referenceBeanName + "[" + referenceKey + "]";
if (isReferenceBean(prevBeanDefinition)) {
//check reference key
String prevReferenceKey = ReferenceBeanSupport.generateReferenceKey(prevBeanDefinition, applicationContext);
if (StringUtils.isEquals(prevReferenceKey, referenceKey)) {
//found matched dubbo reference bean, ignore register
return referenceBeanName;
}
//get interfaceName from attribute
Assert.notNull(prevBeanDefinition, "The interface class of ReferenceBean is not initialized");
prevBeanDesc = referenceBeanName + "[" + prevReferenceKey + "]";
}
// bean name from attribute 'id' or java-config bean, cannot be renamed
if (!renameable) {
throw new BeanCreationException("Already exists another bean definition with the same bean name [" + referenceBeanName + "], " +
"but cannot rename the reference bean name (specify the id attribute or java-config bean), " +
"please modify the name of one of the beans: " +
"prev: " + prevBeanDesc + ", new: " + newBeanDesc + ". " + checkLocation);
}
// the prev bean type is different, rename the new reference bean
int index = 2;
String newReferenceBeanName = null;
while (newReferenceBeanName == null || beanDefinitionRegistry.containsBeanDefinition(newReferenceBeanName)
|| beanDefinitionRegistry.isAlias(newReferenceBeanName)) {
newReferenceBeanName = referenceBeanName + "#" + index;
index++;
// double check found same name and reference key
if (registeredReferenceBeanNames.contains(newReferenceBeanName)) {
return newReferenceBeanName;
}
}
newBeanDesc = newReferenceBeanName + "[" + referenceKey + "]";
logger.warn(CONFIG_DUBBO_BEAN_INITIALIZER, "", "", "Already exists another bean definition with the same bean name [" + referenceBeanName + "], " +
"rename dubbo reference bean to [" + newReferenceBeanName + "]. " +
"It is recommended to modify the name of one of the beans to avoid injection problems. " +
"prev: " + prevBeanDesc + ", new: " + newBeanDesc + ". " + checkLocation);
referenceBeanName = newReferenceBeanName;
}
attributes.put(ReferenceAttributes.ID, referenceBeanName);
// If registered matched reference before, just register alias
if (registeredReferenceBeanNames.size() > 0) {
beanDefinitionRegistry.registerAlias(registeredReferenceBeanNames.get(0), referenceBeanName);
referenceBeanManager.registerReferenceKeyAndBeanName(referenceKey, referenceBeanName);
return referenceBeanName;
}
Class interfaceClass = injectedType;
// 将当前的要引入的服务制作成BeanDefinition交给beanFactory去加载
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClassName(ReferenceBean.class.getName());
beanDefinition.getPropertyValues().add(ReferenceAttributes.ID, referenceBeanName);
// set attribute instead of property values
beanDefinition.setAttribute(Constants.REFERENCE_PROPS, attributes);
beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_CLASS, interfaceClass);
beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_NAME, interfaceName);
// create decorated definition for reference bean, Avoid being instantiated when getting the beanType of ReferenceBean
// see org.springframework.beans.factory.support.AbstractBeanFactory#getTypeForFactoryBean()
GenericBeanDefinition targetDefinition = new GenericBeanDefinition();
targetDefinition.setBeanClass(interfaceClass);
beanDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, referenceBeanName + "_decorated"));
// signal object type since Spring 5.2
beanDefinition.setAttribute(Constants.OBJECT_TYPE_ATTRIBUTE, interfaceClass);
beanDefinitionRegistry.registerBeanDefinition(referenceBeanName, beanDefinition);
referenceBeanManager.registerReferenceKeyAndBeanName(referenceKey, referenceBeanName);
logger.info("Register dubbo reference bean: " + referenceBeanName + " = " + referenceKey + " at " + member);
return referenceBeanName;
}
ReferenceBean作用及触发点
前面讲述DubboReference的扫描和ReferenceBean的加载,那ReferenceBean真正发生作用的时机是哪里呢?
ReferenceBean的作用即是生成服务的LazyProxyObject(JDK动态代理)并交由Spring注入到Bean中,触发点还是在ReferenceAnnotationBeanPostProcessor
中,ReferenceAnnotationBeanPostProcessor
实现了Spring的InstantiationAwareBeanPostProcessor
接口
当bean被实例化前,会调用postProcessPropertyValues
方法将Bean名称和class信息传递进来,这个时候Dubbo就能感知并生成对应的服务注入到bean中,完成服务的引入
调用栈
引入图解
需要注意这里的ProxyObject只是一个代理类,在饿汉模式下,服务未真正被引入,只有真正发生调用时才会去引入服务
InstantiationAwareBeanPostProcessor
InstantiationAwareBeanPostProcessor接口是BeanPostProcessor的子接口,通过接口字面意思翻译该接口的作用是感知Bean实例话的处理器。实际上该接口的作用也是确实如此。
所有bean的实例化都会调用上下文中所有的BeanPostProcessor接口进行bean的前后置处理,而Dubbo正是使用了Spring的这个机制来完成服务的引入
FactoryBean的应用
虽然Dubbo能够感知到所有的bean创建也能寻找出需要引入的服务的元数据信息,那么Dubbo又是如何根据引入的服务的元数据信息获取对应的代理服务对象的呢?
可以看到ReferenceBean是FactoryBean的一个实现类,FactoryBean是用于构建Bean的,简单来讲就是一个Bean的创建可能极为复杂,FactoryBean就是对外屏蔽了Bean创建的所有细节,最终只需要具体的Bean实例即可,是一种简单工厂模式的实现。
现在知道ReferenceBean的创建代理对象的基本链路原理,回到服务引入本身,由于已经有对应的引入服务的ReferenceBean作为FactoryBean被注册到Spring中。Dubbo只需要通过Spring BeanFactory根据对应的服务的Class获取服务代理类并注入到Bean中即可
AnnotatedInjectElement#inject()
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
// 根据注入的服务类型获取服务代理类
Object injectedObject = getInjectedObject(attributes, bean, beanName, getInjectedType(), this);
// 反射注入服务
if (member instanceof Field) {
Field field = (Field) member;
ReflectionUtils.makeAccessible(field);
field.set(bean, injectedObject);
} else if (member instanceof Method) {
Method method = (Method) member;
ReflectionUtils.makeAccessible(method);
method.invoke(bean, injectedObject);
}
}
protected Object getInjectedObject(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
AnnotatedInjectElement injectedElement) throws Exception {
return doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement);
}
protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
AnnotatedInjectElement injectedElement) throws Exception {
if (injectedElement.injectedObject == null) {
throw new IllegalStateException("The AnnotatedInjectElement of @DubboReference should be inited before injection");
}
// 获取Spring BeanFactory并调用getBean()方法获取对应的服务代理对象
return getBeanFactory().getBean((String) injectedElement.injectedObject);
}
由于Dubbo将引入的服务声明为一个FactoryBean在Spring,所以Spring根据服务名称获取Bean名称实际上获取到的是ReferenceBean,也即是FactoryBean,当发现获取的BeanInstance是一个FactoryBean时,会调用FactoryBean的getObject()方法获取真正的Bean
可以看下这个调用链路
ReferenceBean#getObject()
ReferenceBean#getObject()方法就是创建一个JDK动态代理对象,被代理对象是真正服务Proxy对象
这么做的主要原因是,可以利用Spring的代理的懒加载机制,实现服务的懒加载
来看一下getObject方法
public T getObject() {
if (lazyProxy == null) {
// 创建代理对象
createLazyProxy();
}
return (T) lazyProxy;
}
private void createLazyProxy() {
//set proxy interfaces
//see also: org.apache.dubbo.rpc.proxy.AbstractProxyFactory.getProxy(org.apache.dubbo.rpc.Invoker<T>, boolean)
ProxyFactory proxyFactory = new ProxyFactory();
// 设置被代理对象创建工厂
proxyFactory.setTargetSource(new DubboReferenceLazyInitTargetSource());
// 设置被代理对象Class信息
proxyFactory.addInterface(interfaceClass);
Class<?>[] internalInterfaces = AbstractProxyFactory.getInternalInterfaces();
for (Class<?> anInterface : internalInterfaces) {
proxyFactory.addInterface(anInterface);
}
if (!StringUtils.isEquals(interfaceClass.getName(), interfaceName)) {
//add service interface
try {
Class<?> serviceInterface = ClassUtils.forName(interfaceName, beanClassLoader);
proxyFactory.addInterface(serviceInterface);
} catch (ClassNotFoundException e) {
// generic call maybe without service interface class locally
}
}
this.lazyProxy = proxyFactory.getProxy(this.beanClassLoader);
}
// 懒加载工厂
private class DubboReferenceLazyInitTargetSource extends AbstractLazyCreationTargetSource {
// 只有代理对象真正发生调用时才会去调用此方法
@Override
protected Object createObject() throws Exception {
return getCallProxy();
}
@Override
public synchronized Class<?> getTargetClass() {
return getInterfaceClass();
}
}
private Object getCallProxy() throws Exception {
if (referenceConfig == null) {
throw new IllegalStateException("ReferenceBean is not ready yet, please make sure to call reference interface method after dubbo is started.");
}
//get reference proxy
// 获取Dubbo的引入的服务对象(也是个代理对象)
return referenceConfig.get();
}
所以ReferenceBean的作用就是创建一个代理对象,真正的服务引入的对象是ReferenceConfig
服务引入的时机
前面讲到的ReferenceBean只是用于创建服务的动态代理类
服务的引入时机有两种,第一种是饿汉式,第二种是懒汉式。
懒汉式是只有当这个服务被调用时,才启动引入流程,也就是说用到了才会开始服务引入。 默认情况下,Dubbo 使用懒汉式引入服务,如果需要使用饿汉式,可以通过配置 @DubboReference:init = false 饿汉模式是只要服务在类中被引用,就直接启动引入流程
饿汉模式
饿汉模式的加载点是DefaultModuleDeployer#referServices
方法,会在Spring容器完成refreshed后,通过ContextRefreshedEvent事件来触发
来看下refereServices方法
private void referServices() {
// 获取所有需要引入的服务
configManager.getReferences().forEach(rc -> {
try {
ReferenceConfig<?> referenceConfig = (ReferenceConfig<?>) rc;
if (!referenceConfig.isRefreshed()) {
referenceConfig.refresh();
}
// 判断是否需要初始化,这个返回值配置就是由 @DubboReference#init 来配置的,默认为true
// 所以所有服务默认都是饿汉模式
if (rc.shouldInit()) {
// 如果允许异步初始化,则使用线程池去完成服务引入
if (referAsync || rc.shouldReferAsync()) {
ExecutorService executor = executorRepository.getServiceReferExecutor();
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
referenceCache.get(rc);
} catch (Throwable t) {
logger.error(CONFIG_FAILED_EXPORT_SERVICE, "", "", "Failed to async export service config: " + getIdentifier() + " , catch error : " + t.getMessage(), t);
}
}, executor);
asyncReferringFutures.add(future);
} else {
// 服务引入的入口,最终调用的就是ReferenceConfig#get()方法
// referenceCache做的是服务的缓存
referenceCache.get(rc);
}
}
} catch (Throwable t) {
logger.error(CONFIG_FAILED_REFERENCE_MODEL, "", "", "Model reference failed: " + getIdentifier() + " , catch error : " + t.getMessage(), t);
referenceCache.destroy(rc);
throw t;
}
});
}
懒汉模式
懒汉模式和饿汉模式的区别就是什么时候调用ReferenceConfig#get()方法
懒汉模式下,DefaultModuleDeployer#referServices扫描到懒加载的服务时,不会触发服务引入。
同时可以从ReferenceBean的getObject方法看出,只有真正发生调用时,ProxyFactory才会去调用代理工厂的createObject()方法,才会触发ReferenceConfig#get()方法,即开始引入服务
服务引入
通过前面可以知道服务引入的引入是从ReferenceConfig#get()方法作为入口开始,这里主要主要是获取对应的invoker,并且生成代理类
public T get() {
if (destroyed) {
throw new IllegalStateException("The invoker of ReferenceConfig(" + url + ") has already destroyed!");
}
if (ref == null) {
// ensure start module, compatible with old api usage
// 兼容旧的3.0以前的引入方式
getScopeModel().getDeployer().start();
// 初始化服务
init();
}
return ref;
}
init方法,int方法大部分都是检查配置以及构建相关前置参数,大部分可以略过,主要还是分析服务引入的具体流程
protected synchronized void init() {
if (initialized && ref != null) {
return;
}
try {
if (!this.isRefreshed()) {
// 检查并设置相关参数
this.refresh();
}
// init serviceMetadata
// 这一步主要是设置服务的版本号、引入的服务所属组、服务接口等信息
initServiceMetadata(consumer);
// 解析真实的一个接口Class
serviceMetadata.setServiceType(getServiceInterfaceClass());
// TODO, uncomment this line once service key is unified
serviceMetadata.generateServiceKey();
// 组装服务引入所需的相关配置,例如注册的ip、接口信息、方法信息、版本、引入的方式等
Map<String, String> referenceParameters = appendConfig();
ModuleServiceRepository repository = getScopeModel().getServiceRepository();
ServiceDescriptor serviceDescriptor;
if (CommonConstants.NATIVE_STUB.equals(getProxy())) {
serviceDescriptor = StubSuppliers.getServiceDescriptor(interfaceName);
repository.registerService(serviceDescriptor);
} else {
// 解析服务接口Class,得到方法相关的信息集合,并注册到服务管理中心
serviceDescriptor = repository.registerService(interfaceClass);
}
// 构建消费者模型
consumerModel = new ConsumerModel(serviceMetadata.getServiceKey(), proxy, serviceDescriptor,
getScopeModel(), serviceMetadata, createAsyncMethodInfo(), interfaceClassLoader);
// Compatible with dependencies on ServiceModel#getReferenceConfig() , and will be removed in a future version.
consumerModel.setConfig(this);
repository.registerConsumer(consumerModel);
serviceMetadata.getAttachments().putAll(referenceParameters);
// 创建服务代理对象
ref = createProxy(referenceParameters);
serviceMetadata.setTarget(ref);
serviceMetadata.addAttribute(PROXY_CLASS_REF, ref);
// 设置用于销毁的runner
consumerModel.setDestroyRunner(getDestroyRunner());
consumerModel.setProxyObject(ref);
consumerModel.initMethodModels();
checkInvokerAvailable();
} catch (Throwable t) {
try {
if (invoker != null) {
invoker.destroy();
}
} catch (Throwable destroy) {
logger.warn(CONFIG_FAILED_DESTROY_INVOKER, "", "", "Unexpected error occurred when destroy invoker of ReferenceConfig(" + url + ").", t);
}
if (consumerModel != null) {
ModuleServiceRepository repository = getScopeModel().getServiceRepository();
repository.unregisterConsumer(consumerModel);
}
initialized = false;
invoker = null;
ref = null;
consumerModel = null;
serviceMetadata.setTarget(null);
serviceMetadata.getAttributeMap().remove(PROXY_CLASS_REF);
// Thrown by checkInvokerAvailable().
if (t.getClass() == IllegalStateException.class &&
t.getMessage().contains("No provider available for the service")) {
// 2-2 - No provider available.
logger.error(CLUSTER_NO_VALID_PROVIDER, "server crashed", "", "No provider available.", t);
}
throw t;
}
initialized = true;
}
referenceParameters构建比较复杂,直接看下构建完成的样子
里面的参数包含了全局的配置,例如metadata-service-protocol、check等,也包含了每个服务自定义的参数,例如timeout、scope等信息
接下来就进入服务引入的重点方法 -- createProxy()
private T createProxy(Map<String, String> referenceParameters) {
// 根据scope判断是否为进行本地服务引入
if (shouldJvmRefer(referenceParameters)) {
// 构建url并创建invoker
createInvokerForLocal(referenceParameters);
} else {
urls.clear();
meshModeHandleUrl(referenceParameters);
if (StringUtils.isNotEmpty(url)) {
// url存在则为点对点引用,也就是直连引用
// user specified URL, could be peer-to-peer address, or register center's address.
parseUrl(referenceParameters);
} else {
// if protocols not in jvm checkRegistry
// 这里不是local协议默认这里为空
if (!LOCAL_PROTOCOL.equalsIgnoreCase(getProtocol())) {
//从注册表中获取URL并将其聚合。这个其实就是初始化一下注册中心的url配置
aggregateUrlFromRegistry(referenceParameters);
}
}
//这个代码非常重要 创建远程引用,创建远程引用调用器
createInvokerForRemote();
}
if (logger.isInfoEnabled()) {
logger.info("Referred dubbo service: [" + referenceParameters.get(INTERFACE_KEY) + "]." +
(Boolean.parseBoolean(referenceParameters.get(GENERIC_KEY)) ?
" it's GenericService reference" : " it's not GenericService reference"));
}
URL consumerUrl = new ServiceConfigURL(CONSUMER_PROTOCOL, referenceParameters.get(REGISTER_IP_KEY), 0,
referenceParameters.get(INTERFACE_KEY), referenceParameters);
consumerUrl = consumerUrl.setScopeModel(getScopeModel());
consumerUrl = consumerUrl.setServiceModel(consumerModel);
MetadataUtils.publishServiceDefinition(consumerUrl, consumerModel.getServiceModel(), getApplicationModel());
// 根据invoker创建代理对象
return (T) proxyFactory.getProxy(invoker, ProtocolUtils.isGeneric(generic));
}
创建远程引用,创建远程引用调用器
ReferenceConfig类型的createInvokerForRemote方法
private void createInvokerForRemote() {
//这个url 为注册协议如registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-consumer&dubbo=2.0.2&pid=6204&qos.enable=false&qos.port=-1®istry=zookeeper&release=3.0.9×tamp=1657439419495
if (urls.size() == 1) {
URL curUrl = urls.get(0);
// 生成调用器,这里是SPI生成的动态的Protocol$Adaptive字节码,在源码中看不到,作用是用来后续调用
invoker = protocolSPI.refer(interfaceClass, curUrl);
// registry url, mesh-enable and unloadClusterRelated is true, not need Cluster.
if (!UrlUtils.isRegistry(curUrl) &&
!curUrl.getParameter(UNLOAD_CLUSTER_RELATED, false)) {
List<Invoker<?>> invokers = new ArrayList<>();
invokers.add(invoker);
invoker = Cluster.getCluster(scopeModel, Cluster.DEFAULT).join(new StaticDirectory(curUrl, invokers), true);
}
} else {
// 有多个url地址时,比如多注册中心
List<Invoker<?>> invokers = new ArrayList<>();
URL registryUrl = null;
// 生成多个invoker
for (URL url : urls) {
// For multi-registry scenarios, it is not checked whether each referInvoker is available.
// Because this invoker may become available later.
invokers.add(protocolSPI.refer(interfaceClass, url));
if (UrlUtils.isRegistry(url)) {
// use last registry url
registryUrl = url;
}
}
if (registryUrl != null) {
// registry url is available
// for multi-subscription scenario, use 'zone-aware' policy by default
String cluster = registryUrl.getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME);
// The invoker wrap sequence would be: ZoneAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker
// (RegistryDirectory, routing happens here) -> Invoker
invoker = Cluster.getCluster(registryUrl.getScopeModel(), cluster, false).join(new StaticDirectory(registryUrl, invokers), false);
} else {
// not a registry url, must be direct invoke.
if (CollectionUtils.isEmpty(invokers)) {
throw new IllegalArgumentException("invokers == null");
}
// 获取第一个作为主invoker
URL curUrl = invokers.get(0).getUrl();
String cluster = curUrl.getParameter(CLUSTER_KEY, Cluster.DEFAULT);
invoker = Cluster.getCluster(scopeModel, cluster).join(new StaticDirectory(curUrl, invokers), true);
}
}
}
invoker生成流程图
简述一下就是先检查配置,通过配置构建一个 map ,然后利用 map 来构建 URL ,再通过 URL 上的协议利用自适应扩展机制调用对应的 protocol.refer 得到相应的 invoker 。
在有多个 URL 的时候,先遍历构建出 invoker 然后再由 StaticDirectory 封装一下,然后通过 cluster 进行合并,只暴露出一个 invoker 。
然后再构建代理,封装 invoker 返回服务引用,之后 Comsumer 调用的就是这个代理类。
服务引入方式
根据流程图可以看到,构建URL的时候分为三块,分别是 本地引入、直连引入、通过注册中心引入
为什么生成服务代理类
这里就需要谈到RPC是什么的问题了。RPC本质就是远程调用,相对传统的HTTP而言,RPC总结起来的区别就是,RPC使开发者在远程调用外部接口时,跟调用本地接口方法一样。
基于这种实现理念,所以RPC需要做到可以发布接口服务,并且引入接口服务。对于引用方而言,无法实例化一个独立的接口。这个时候代理的作用就出来,Dubbo为对应的服务接口创建一个代理对象,作为实例对象注入到到具体的类中,使得开发者能够顺利调用外部接口,同时做到了无代码入侵、屏蔽了底层相关细节
invoker创建服务全流程
先来看两种图,分别是注册中心创建invoker对象的链路和invoker进行真正的服务引入流程链路
RegistryInvoker对象创建过程
根据流程图可以看到整个调用链路包含许多装饰类,但不是服务引入的重点,主要的方法是从RegistryProtocol#refer方法开始
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
// 根据注册中心协议替换注册中心头,比如使用的是zookeeper,那url会被替换成zookeeper开头。zookeeper://......
url = getRegistryUrl(url);
// 根据url头部协议获取注册中心实例
Registry registry = getRegistry(url);
if (RegistryService.class.equals(type)) {
return proxyFactory.getInvoker((T) registry, type, url);
}
// group="a,b" or group="*"
Map<String, String> qs = (Map<String, String>) url.getAttribute(REFER_KEY);
String group = qs.get(GROUP_KEY);
if (StringUtils.isNotEmpty(group)) {
if ((COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) {
return doRefer(Cluster.getCluster(url.getScopeModel(), MergeableCluster.NAME), registry, type, url, qs);
}
}
Cluster cluster = Cluster.getCluster(url.getScopeModel(), qs.get(CLUSTER_KEY));
// 执行真正的refer
return doRefer(cluster, registry, type, url, qs);
}
接下来来看下 doRefer 方法
protected <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url, Map<String, String> parameters) {
Map<String, Object> consumerAttribute = new HashMap<>(url.getAttributes());
consumerAttribute.remove(REFER_KEY);
String p = isEmpty(parameters.get(PROTOCOL_KEY)) ? CONSUMER : parameters.get(PROTOCOL_KEY);
// 生成消费者URL,指的是真正需要被引入的服务的url
URL consumerUrl = new ServiceConfigURL (
p,
null,
null,
parameters.get(REGISTER_IP_KEY),
0, getPath(parameters, type),
parameters,
consumerAttribute
);
// 将消费者url放到原始url中,原始url也就是注册中心的url
url = url.putAttribute(CONSUMER_URL_KEY, consumerUrl);
// 生成migrationInvoker,这个invoker也就是最终被包装成proxy的invoker
ClusterInvoker<T> migrationInvoker = getMigrationInvoker(this, cluster, registry, type, url, consumerUrl);
// 拦截invoker并启动服务的订阅
return interceptInvoker(migrationInvoker, url, consumerUrl);
}
这里代码比较重要的其实只有两行getMigrationInvoker和interceptInvoker方法 比较核心也是Dubbo3比较重要的消费者启动逻辑基本都在这个方法里面interceptInvoker,这个方法执行了消费者应用级发现和接口级发现迁移的逻辑,会自动帮忙决策一个Invoker类型对象,
我们继续看RegistryProtocol类型的interceptInvoker方法:
具体代码如下: RegistryProtocol类型的interceptInvoker方法
protected <T> Invoker<T> interceptInvoker(ClusterInvoker<T> invoker, URL url, URL consumerUrl) {
//获取激活的注册协议监听器扩展里面registry.protocol.listener,这里激活的类型为MigrationRuleListener
List<RegistryProtocolListener> listeners = findRegistryProtocolListeners(url);
if (CollectionUtils.isEmpty(listeners)) {
return invoker;
}
for (RegistryProtocolListener listener : listeners) {
//这里执行MigrationRuleListener类型的onRefer方法
listener.onRefer(this, invoker, consumerUrl, url);
}
return invoker;
}
该方法尝试加载所有RegistryProtocolListener定义,这些定义通过与定义的交互来控制调用器的行为,然后使用这些侦听器更改MigrationInvoker的状态和行为。 当前可用的监听器是MigrationRuleListener,用于通过动态变化的规则控制迁移行为。
可以看到核心的逻辑集中在了这个位置MigrationRuleListener类型的onRefer方法,里面涉及到整个远程服务的引入和远程调用的invoker创建
这里只介绍了单个url的创建,如果有多个url,那就是生成多个invoker并与url进行绑定,后续产生的服务调用直接生成对应的url寻找到对应的远程调用invoker进行远程服务调用即可
完成所有的远程调用invoker的创建,整个流程就算是走完了,最终RegistryInvoker会返回到ReferenceConfig中,并通过(T) proxyFactory.getProxy(invoker, ProtocolUtils.isGeneric(generic))
方法创建出代理对象
服务协议invoker的创建的核心位于MigrationRuleListener的onRefer方法,有兴趣的可以看 消费端服务发现智能决策原理
小结
整个流程分析下来 总结以下几点
- 生成服务引入的相关配置
- 生成RegistryInvoker
- 生成DynamicDirectory
- 向注册中心注册相关信息、订阅providers,同时注册监听器到注册中心里
- 根据providers信息一一生成远程调用invoker并且缓存
- 完成所有创建返回RegistryInvoker并生成代理类