consumer bean初始化
以spring 如下配置
<dubbo:reference id="versionConsumerBean" interface="org.apache.dubbo.samples.version.api.VersionService" version="*"/>
为例,先使用spring 的初始化,核心代码
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
初始化为class org.apache.dubbo.config.spring.ReferenceBean。
然后在类似如下代码
VersionService versionService = (VersionService) context.getBean("versionConsumerBean");
处进行了二次变化,创建LazyProxy
核心代码在ReferenceBean的getObject,代码如下:
/**
* Create bean instance.
*
* <p></p>
* Why we need a lazy proxy?
*
* <p/>
* When Spring searches beans by type, if Spring cannot determine the type of a factory bean, it may try to initialize it.
* The ReferenceBean is also a FactoryBean.
* <br/>
* (This has already been resolved by decorating the BeanDefinition: {@link DubboBeanDefinitionParser#configReferenceBean})
*
* <p/>
* In addition, if some ReferenceBeans are dependent on beans that are initialized very early,
* and dubbo config beans are not ready yet, there will be many unexpected problems if initializing the dubbo reference immediately.
*
* <p/>
* When it is initialized, only a lazy proxy object will be created,
* and dubbo reference-related resources will not be initialized.
* <br/>
* In this way, the influence of Spring is eliminated, and the dubbo configuration initialization is controllable.
*
*
* @see DubboConfigBeanInitializer
* @see ReferenceBeanManager#initReferenceBean(ReferenceBean)
* @see DubboBeanDefinitionParser#configReferenceBean
*/
@Override
public T getObject() {
if (lazyProxy == null) {
createLazyProxy();
}
return (T) lazyProxy;
}
变换后返回的是一个aop java对象了。
服务发现
dubbo 的服务调用是proxy通invoker实现,因此核心过程就在如下函数中。
@SuppressWarnings({"unchecked"})
private T createProxy(Map<String, String> referenceParameters) {
urls.clear();
meshModeHandleUrl(referenceParameters);
if (StringUtils.isNotEmpty(url)) {
// user specified URL, could be peer-to-peer address, or register center's address.
parseUrl(referenceParameters);
} else {
// if protocols not in jvm checkRegistry
aggregateUrlFromRegistry(referenceParameters);
}
createInvoker();
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());
// create service proxy
return (T) proxyFactory.getProxy(invoker, ProtocolUtils.isGeneric(generic));
}
在创建proxy的过程中,可以多次看到TargetService和protocolServiceKey的处理,核心是找到两者批量的service
核心批量比较代码如下
public static boolean isMatch(URL consumerUrl, URL providerUrl) {
String consumerInterface = consumerUrl.getServiceInterface();
String providerInterface = providerUrl.getServiceInterface();
if (!"*".equals(consumerInterface) && !"*".equals(providerInterface) && !StringUtils.isEquals(consumerInterface, providerInterface)) {
return false;
} else if (!isMatchCategory(providerUrl.getCategory("providers"), consumerUrl.getCategory("providers"))) {
return false;
} else if (!providerUrl.getParameter("enabled", true) && !"*".equals(consumerUrl.getParameter("enabled"))) {
return false;
} else {
String consumerGroup = consumerUrl.getGroup();
String consumerVersion = consumerUrl.getVersion();
String consumerClassifier = consumerUrl.getParameter("classifier", "*");
String providerGroup = providerUrl.getGroup();
String providerVersion = providerUrl.getVersion();
String providerClassifier = providerUrl.getParameter("classifier", "*");
boolean groupMatches = "*".equals(consumerGroup) || StringUtils.isEquals(consumerGroup, providerGroup) || StringUtils.isContains(consumerGroup, providerGroup);
boolean versionMatches = "*".equals(consumerVersion) || StringUtils.isEquals(consumerVersion, providerVersion);
boolean classifierMatches = consumerClassifier == null || "*".equals(consumerClassifier) || StringUtils.isEquals(consumerClassifier, providerClassifier);
return groupMatches && versionMatches && classifierMatches;
}
}
注意看最后几段boolean的match,即
- group
- version
- class(interface)
要匹配
invoker 是在服务发现后依次进行router-cluster-failover 判定后进行调用,可以参考下图
而服务发现(以zk为例),核心用到以下类
zk 数据结构
dubbo root
[zk: localhost:2181(CONNECTED) 23] ls /dubbo
[config, mapping, metadata, org.apache.dubbo.metadata.MetadataService, org.apache.dubbo.mock.api.MockService, org.apache.dubbo.samples.version.api.VersionService]
provider创建时候负责写入zk的providers
[zk: localhost:2181(CONNECTED) 24] ls /dubbo/org.apache.dubbo.samples.version.api.VersionService
[configurators, providers]
providers可以有多版本
[zk: localhost:2181(CONNECTED) 25] ls /dubbo/org.apache.dubbo.samples.version.api.VersionService/providers
[dubbo%3A%2F%2F192.168.3.28%3A20880%2Forg.apache.dubbo.samples.version.api.VersionService%3Fanyhost%3Dtrue%26application%3Dversion-provider%26background%3Dfalse%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26executor-management-mode%3Disolation%26file-cache%3Dtrue%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.samples.version.api.VersionService%26methods%3DsayHello%26pid%3D8804%26prefer.serialization%3Dfastjson2%2Chessian2%26release%3D3.2.0%26revision%3D1.0.0%26service-name-mapping%3Dtrue%26side%3Dprovider%26timestamp%3D1685160190088%26token%3Db4d3e756-3a85-40ab-b3fd-c4b1352879ce%26version%3D1.0.0, dubbo%3A%2F%2F192.168.3.28%3A20880%2Forg.apache.dubbo.samples.version.api.VersionService%3Fanyhost%3Dtrue%26application%3Dversion-provider%26background%3Dfalse%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26executor-management-mode%3Disolation%26file-cache%3Dtrue%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.samples.version.api.VersionService%26methods%3DsayHello%26pid%3D8804%26prefer.serialization%3Dfastjson2%2Chessian2%26release%3D3.2.0%26revision%3D2.0.0%26service-name-mapping%3Dtrue%26side%3Dprovider%26timestamp%3D1685160186753%26token%3Dbc2534d5-f980-4a41-ab90-e4e9ae688ad9%26version%3D2.0.0]
完整的service下目录
[zk: localhost:2181(CONNECTED) 7] ls -s /dubbo/org.apache.dubbo.samples.version.api.VersionService
[configurators, consumers, providers, routers]