完整流程圖
一句話概括流程
具體可以概括為以下五點(diǎn)
- 兩種模式(餓漢式/懶漢式)
- 組裝URL并向注冊(cè)中心注冊(cè)
- 獲取服務(wù)提供者信息并根據(jù)協(xié)議(默認(rèn)Dubbo協(xié)議)開始Invoker的創(chuàng)建流程
- 通過Cluster包裝Invoker(默認(rèn)FailoverCluster)
- 返回代理類
服務(wù)引用的兩個(gè)時(shí)機(jī)
- 餓漢式:通過實(shí)現(xiàn)Spring的 IntitalizingBean 接口的 afterProperties 方法,容器通過調(diào)用 ReferenceBean 的 afterPropertiesSet 方法時(shí)引入服務(wù) (ReferenceBean實(shí)現(xiàn)了IntitalizingBean接口)
Bean實(shí)例化完成后填充屬性時(shí)引入服務(wù)拴魄,這里具體的Bean是Invoker的代理對(duì)象 - 懶漢式:只有當(dāng)這個(gè)服務(wù)被注入到其他類中時(shí)啟動(dòng)引入流程
注釋:我們創(chuàng)建出來的遠(yuǎn)程調(diào)用的代理對(duì)象清焕,實(shí)際上是ReferenceBean的一個(gè)成員變量(相當(dāng)于我們?cè)诖韺?duì)象上套了個(gè)殼,把這個(gè)殼作為一個(gè)bean注冊(cè)到spring容器中)
餓漢式:
public void afterPropertiesSet() throws Exception {
// Initializes Dubbo's Config Beans before @Reference bean autowiring
prepareDubboConfigBeans();
// lazy init by default.
if (init == null) {
init = false;
}
// eager init if necessary.
if (shouldInit()) {
getObject();
}
}
懶漢式:
@Override
public Object getObject() {
return get();
}
public synchronized T get() {
if (destroyed) {
throw new IllegalStateException("The invoker of ReferenceConfig(" + url + ") has already destroyed!");
}
if (ref == null) {
init();
}
return ref;
}
關(guān)于這點(diǎn)的解釋卧檐。如果 <dubbo:reference> 的init屬性開啟(餓漢式),創(chuàng)建完ReferenceBean 的時(shí)候焰宣,在afterProperties方法里面調(diào)用對(duì)應(yīng)的getObject方法霉囚,將對(duì)應(yīng)的Invoker的Bean創(chuàng)建出來并注冊(cè)到容器中。而懶漢式則是在其他的Bean去注入它的時(shí)候匕积,才會(huì)去進(jìn)行Invoker的創(chuàng)建流程
然后ContextRefreshedEvent事件那一步的處理會(huì)將我上面沒有去實(shí)例化的那些Invoker也實(shí)例化一次(個(gè)人猜測是那些懶加載沒有被其他Bean注入的Bean)
為什么要是用FactoryBean接口的方式去創(chuàng)建對(duì)應(yīng)的Bean盈罐。因?yàn)镕actoryBean本身的作用是為了能夠進(jìn)行復(fù)雜的Bean邏輯創(chuàng)建,而不通過Spring本身的三級(jí)緩存的方式創(chuàng)建Bean闪唆。而且此處的Invoker還涉及到動(dòng)態(tài)代理之類的情況盅粪,F(xiàn)actoryBean就很適合這種創(chuàng)建Bean場景
同樣的,例如SpringCloud的Feign之類的也是通過這樣的方式注入Bean
入口關(guān)鍵類 ReferenceConfig
public <T> T get(ReferenceConfigBase<T> referenceConfig) {
String key = generator.generateKey(referenceConfig);
Class<?> type = referenceConfig.getInterfaceClass();
proxies.computeIfAbsent(type, _t -> new ConcurrentHashMap<>());
ConcurrentMap<String, Object> proxiesOfType = proxies.get(type);
proxiesOfType.computeIfAbsent(key, _k -> {
// 觸發(fā)的地方悄蕾,最終調(diào)用到init
Object proxy = referenceConfig.get();
referredReferences.put(key, referenceConfig);
return proxy;
});
return (T) proxiesOfType.get(key);
}
public synchronized void init() {
if (initialized) {
return;
}
if (bootstrap == null) {
bootstrap = DubboBootstrap.getInstance();
bootstrap.initialize();
}
checkAndUpdateSubConfigs();
checkStubAndLocal(interfaceClass);
ConfigValidationUtils.checkMock(interfaceClass, this);
Map<String, String> map = new HashMap<String, String>();
map.put(SIDE_KEY, CONSUMER_SIDE);
ReferenceConfigBase.appendRuntimeParameters(map);
if (!ProtocolUtils.isGeneric(generic)) {
String revision = Version.getVersion(interfaceClass, version);
if (revision != null && revision.length() > 0) {
map.put(REVISION_KEY, revision);
}
String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
if (methods.length == 0) {
logger.warn("No method found in service interface " + interfaceClass.getName());
map.put(METHODS_KEY, ANY_VALUE);
} else {
map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), COMMA_SEPARATOR));
}
}
map.put(INTERFACE_KEY, interfaceName);
AbstractConfig.appendParameters(map, getMetrics());
AbstractConfig.appendParameters(map, getApplication());
AbstractConfig.appendParameters(map, getModule());
// remove 'default.' prefix for configs from ConsumerConfig
// appendParameters(map, consumer, Constants.DEFAULT_KEY);
AbstractConfig.appendParameters(map, consumer);
AbstractConfig.appendParameters(map, this);
MetadataReportConfig metadataReportConfig = getMetadataReportConfig();
if (metadataReportConfig != null && metadataReportConfig.isValid()) {
map.putIfAbsent(METADATA_KEY, REMOTE_METADATA_STORAGE_TYPE);
}
Map<String, AsyncMethodInfo> attributes = null;
if (CollectionUtils.isNotEmpty(getMethods())) {
attributes = new HashMap<>();
for (MethodConfig methodConfig : getMethods()) {
AbstractConfig.appendParameters(map, methodConfig, methodConfig.getName());
String retryKey = methodConfig.getName() + ".retry";
if (map.containsKey(retryKey)) {
String retryValue = map.remove(retryKey);
if ("false".equals(retryValue)) {
map.put(methodConfig.getName() + ".retries", "0");
}
}
AsyncMethodInfo asyncMethodInfo = AbstractConfig.convertMethodConfig2AsyncInfo(methodConfig);
if (asyncMethodInfo != null) {
// consumerModel.getMethodModel(methodConfig.getName()).addAttribute(ASYNC_KEY, asyncMethodInfo);
attributes.put(methodConfig.getName(), asyncMethodInfo);
}
}
}
String hostToRegistry = ConfigUtils.getSystemProperty(DUBBO_IP_TO_REGISTRY);
if (StringUtils.isEmpty(hostToRegistry)) {
hostToRegistry = NetUtils.getLocalHost();
} else if (isInvalidLocalHost(hostToRegistry)) {
throw new IllegalArgumentException("Specified invalid registry ip from property:" + DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);
}
map.put(REGISTER_IP_KEY, hostToRegistry);
serviceMetadata.getAttachments().putAll(map);
// 創(chuàng)建代理 即生成invoker(代理中包括invoker)
ref = createProxy(map);
serviceMetadata.setTarget(ref);
serviceMetadata.addAttribute(PROXY_CLASS_REF, ref);
ConsumerModel consumerModel = repository.lookupReferredService(serviceMetadata.getServiceKey());
consumerModel.setProxyObject(ref);
consumerModel.init(attributes);
initialized = true;
checkInvokerAvailable();
// dispatch a ReferenceConfigInitializedEvent since 2.7.4
dispatch(new ReferenceConfigInitializedEvent(this, invoker));
}
創(chuàng)建代理類
主要分為以下幾點(diǎn)
- injvm:走jvm內(nèi)部調(diào)用(自己服務(wù)中的dubbo接口調(diào)用)
- 配置了URL:點(diǎn)對(duì)點(diǎn)
- 未配置URL票顾,從注冊(cè)中心獲取服務(wù)提供者信息
private T createProxy(Map<String, String> map) {
if (shouldJvmRefer(map)) {
// 本地調(diào)用的情況
URL url = new URL(LOCAL_PROTOCOL, LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(map);
invoker = REF_PROTOCOL.refer(interfaceClass, url);
if (logger.isInfoEnabled()) {
logger.info("Using injvm service " + interfaceClass.getName());
}
} else {
urls.clear();
// 配置的url的情況 點(diǎn)對(duì)點(diǎn)/注冊(cè)中心地址
if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address.
String[] us = SEMICOLON_SPLIT_PATTERN.split(url);
if (us != null && us.length > 0) {
for (String u : us) {
URL url = URL.valueOf(u);
if (StringUtils.isEmpty(url.getPath())) {
url = url.setPath(interfaceName);
}
if (UrlUtils.isRegistry(url)) {
// 如果是注冊(cè)中心础浮,將map轉(zhuǎn)換為查詢字符串,并作為refer參數(shù)的值添加到url中
urls.add(url.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
} else {
// 點(diǎn)對(duì)點(diǎn)直連奠骄,合并url豆同,移除一些服務(wù)提供者的配置
urls.add(ClusterUtils.mergeUrl(url, map));
}
}
}
} else { // assemble URL from register center's configuration
// 未配置url的情況
// if protocols not injvm checkRegistry
if (!LOCAL_PROTOCOL.equalsIgnoreCase(getProtocol())) {
checkRegistry();
// 獲取注冊(cè)中心地址
List<URL> us = ConfigValidationUtils.loadRegistries(this, false);
if (CollectionUtils.isNotEmpty(us)) {
for (URL u : us) {
URL monitorUrl = ConfigValidationUtils.loadMonitor(this, u);
if (monitorUrl != null) {
map.put(MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
}
urls.add(u.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map))); // registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-consumer&dubbo=2.0.2&pid=52327&refer=application=dubbo-demo-api-consumer&dubbo=2.0.2&generic=true&interface=org.apache.dubbo.demo.DemoService&pid=52327®ister.ip=10.167.10.19&side=consumer&sticky=false×tamp=1631087606024®istry=zookeeper×tamp=1631087917015
}
}
if (urls.isEmpty()) {
throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
}
}
}
//
if (urls.size() == 1) {
invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
} else {
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
URL registryURL = null;
for (URL url : urls) {
invokers.add(REF_PROTOCOL.refer(interfaceClass, url));
if (UrlUtils.isRegistry(url)) {
registryURL = url; // use last registry 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(cluster, false).join(new StaticDirectory(registryURL, invokers));
} else { // not a registry url, must be direct invoke.
String cluster = CollectionUtils.isNotEmpty(invokers)
? (invokers.get(0).getUrl() != null ? invokers.get(0).getUrl().getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME) : Cluster.DEFAULT)
: Cluster.DEFAULT;
invoker = Cluster.getCluster(cluster).join(new StaticDirectory(invokers));
}
}
}
if (logger.isInfoEnabled()) {
logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
}
URL consumerURL = new URL(CONSUMER_PROTOCOL, map.remove(REGISTER_IP_KEY), 0, map.get(INTERFACE_KEY), map);
MetadataUtils.publishServiceDefinition(consumerURL);
// create service proxy 創(chuàng)建代理服務(wù)
return (T) PROXY_FACTORY.getProxy(invoker, ProtocolUtils.isGeneric(generic));
}
此處細(xì)講一下通過注冊(cè)中心創(chuàng)建Invoker的方式(多個(gè)服務(wù)提供者的情況)
遍歷注冊(cè)中心,將注冊(cè)中心所有的服務(wù)提供者信息(DynamicDirectory)匯集起來含鳞,創(chuàng)建出一個(gè)ClusterInvoker影锈,并將ClusterInvoker的代理對(duì)象T返回給調(diào)用點(diǎn)
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
URL registryURL = null;
for (URL url : urls) {
// 引用服務(wù)
invokers.add(REF_PROTOCOL.refer(interfaceClass, url));
if (UrlUtils.isRegistry(url)) {
registryURL = url; // use last registry 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(cluster, false).join(new StaticDirectory(registryURL, invokers));
} else { // not a registry url, must be direct invoke.
String cluster = CollectionUtils.isNotEmpty(invokers)
? (invokers.get(0).getUrl() != null ? invokers.get(0).getUrl().getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME) : Cluster.DEFAULT)
: Cluster.DEFAULT;
invoker = Cluster.getCluster(cluster).join(new StaticDirectory(invokers));
}
引用
具體協(xié)議的引用方法,以RegisterProtocol#refer為例
@Override
@SuppressWarnings("unchecked")
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
url = getRegistryUrl(url);
// 獲取注冊(cè)中心
Registry registry = registryFactory.getRegistry(url);
if (RegistryService.class.equals(type)) {
return proxyFactory.getInvoker((T) registry, type, url);
}
// group="a,b" or group="*"
Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY));
String group = qs.get(GROUP_KEY);
if (group != null && group.length() > 0) {
if ((COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) {
return doRefer(Cluster.getCluster(MergeableCluster.NAME), registry, type, url);
}
}
Cluster cluster = Cluster.getCluster(qs.get(CLUSTER_KEY));
// 引用具體的服務(wù)
return doRefer(cluster, registry, type, url);
}
protected <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
return interceptInvoker(getInvoker(cluster, registry, type, url), url);
}
protected <T> ClusterInvoker<T> getInvoker(Cluster cluster, Registry registry, Class<T> type, URL url) {
// 封裝成一個(gè)directory (invoker的集合)
DynamicDirectory<T> directory = createDirectory(type, url);
directory.setRegistry(registry);
directory.setProtocol(protocol);
// all attributes of REFER_KEY
Map<String, String> parameters = new HashMap<String, String>(directory.getConsumerUrl().getParameters());
URL urlToRegistry = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters);
if (directory.isShouldRegister()) {
directory.setRegisteredConsumerUrl(urlToRegistry);
registry.register(directory.getRegisteredConsumerUrl());
}
// 建立路由規(guī)則鏈
directory.buildRouterChain(urlToRegistry);
// 訂閱provider
directory.subscribe(toSubscribeUrl(urlToRegistry));
// 包裝集群容錯(cuò)策略到invoker
return (ClusterInvoker<T>) cluster.join(directory);
}
代理類
return (T) PROXY_FACTORY.getProxy(invoker, ProtocolUtils.isGeneric(generic));
// 以Javassist動(dòng)態(tài)代理為例
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
此處創(chuàng)建的代理類蝉绷,會(huì)有一個(gè)成員變量鸭廷,InvocationHandler
當(dāng)進(jìn)行Dubbo遠(yuǎn)程調(diào)用的時(shí)候,就會(huì)被對(duì)應(yīng)的InvokerInvocationHandler進(jìn)行方法攔截處理熔吗,把看似本地調(diào)用的方法變?yōu)檫h(yuǎn)程調(diào)用