-
服务引用时机
饿汉式:ReferenceBean实现了InitializingBean,afterPropertiesSet会在spring容器启动的时候调用,其中的isInit方法返回的init是<dubbo:reference />标签中的init配置的,为true的时候在应用启动的时候就会调用getObject()方法加载消费方类。此时就是饿汉式的。
懒汉式:提供服务的类没有被用到的话,都不会加载,只有真正用到的时候才会去加载,例如需要被注入到其他bean中的时候,或者从容器中获取bean的时候,例如context.getBean(beanName)。dubbo默认是懒汉式的引用服务。
public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean { // ......省略 @SuppressWarnings({"unchecked"}) public void afterPropertiesSet() throws Exception { // ......省略 Boolean b = isInit(); if (b == null && getConsumer() != null) { b = getConsumer().isInit(); } if (b != null && b.booleanValue()) { getObject(); } } // ......省略 }
-
创建服务引用类主体流程
当用到bean的时候,spring对实现FactoryBean的类通过getObject获取真实引用的bean,此时会进入创建引用类的过程。
public class ReferenceConfig<T> extends AbstractReferenceConfig { // ......省略 public Object getObject() throws Exception { return get(); } public synchronized T get() { // ......省略 if (ref == null) { init(); } return ref; } private void init() { // ......省略 ref = createProxy(map); ConsumerModel consumerModel = new ConsumerModel(getUniqueServiceName(), this, ref, interfaceClass.getMethods()); ApplicationModel.initConsumerModel(getUniqueServiceName(), consumerModel); } }
createProxy方法如下
private T createProxy(Map<String, String> map) { // ......省略 if (isJvmRefer) { URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map); invoker = refprotocol.refer(interfaceClass, url); } else { // 如果<dubbo:reference />标签中指定了直连模式时的url if (url != null && url.length() > 0) { // ......省略,对直连模式时的url做一些处理,并加入urls中 if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) { urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map))); } else { urls.add(ClusterUtils.mergeUrl(url, map)); } } else { // 加载注册中心配置 List<URL> us = loadRegistries(false); for (URL u : us) { // ......省略, 补充url参数等 urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map))); } } // 如果单注册中心 if (urls.size() == 1) { invoker = refprotocol.refer(interfaceClass, urls.get(0)); } else { // 如果有多个注册中心,每个注册中心创建一个invoker List<Invoker<?>> invokers = new ArrayList<Invoker<?>>(); URL registryURL = null; for (URL url : urls) { invokers.add(refprotocol.refer(interfaceClass, url)); if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) { registryURL = url; } } if (registryURL != null) { URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME); invoker = cluster.join(new StaticDirectory(u, invokers)); } else { invoker = cluster.join(new StaticDirectory(invokers)); } } } // ......省略 return (T) proxyFactory.getProxy(invoker); }
判断,如果是本地引用,此时url形式大致为:injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=24892&qos.port=33333®ister.ip=10.10.10.10&side=consumer×tamp=1599137706187
SPI根据url中的信息injvm,加载InjvmProtocol,调用它的refer方法,直接创建一个InjvmInvoker返回。
public class InjvmProtocol extends AbstractProtocol implements Protocol { // ......省略 public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException { return new InjvmInvoker<T>(serviceType, url, url.getServiceKey(), exporterMap); } }
如果是远程引用,先判断如果指定了直连url,对url补充部分参数后加入ReferenceConfig的urls属性中。没有指定直连url的时候,加载注册中心的配置,对注册中心的url补充参数后加入ReferenceConfig的urls属性中。再看引入服务的urls数量,若 urls 元素数量大于1,即存在多个注册中心或服务直连 url,此时先根据 url 构建 Invoker。然后再通过 Cluster 合并多个 Invoker,最后调用 ProxyFactory 生成代理类。
如果url是注册中心,则通过SPI加载RegistryProtocol类,调用refer方法服务引用。
-
基于注册中心的服务引用,创建Invoker
上面根据注册中心的url通过SPI加载RegistryProtocol类后,调用refer方法引用服务,内部调用doRefer
public class RegistryProtocol implements Protocol { // ......省略 public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException { // ......省略 return doRefer(cluster, registry, type, url); } private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) { RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url); // ......省略 registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY, Constants.CHECK_KEY, String.valueOf(false))); // ......省略 directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY, Constants.PROVIDERS_CATEGORY + "," + Constants.CONFIGURATORS_CATEGORY + "," + Constants.ROUTERS_CATEGORY)); Invoker invoker = cluster.join(directory); ProviderConsumerRegTable.registerConsuemr(invoker, url, subscribeUrl, directory); return invoker; } // ......省略 }
doRefer中的流程与上一篇文章服务导出流程比较像,先是到注册中心注册消费者url,形式大致如下:consumer://10.10.10.10/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=9056&qos.port=33333&side=consumer×tamp=1599182506700
然后订阅注册中心的其他信息。完成订阅后,RegistryDirectory 会收到这几个节点下的子节点信息。由于一个服务可能部署在多台服务器上,这样就会在 providers 产生多个节点,这个时候就需要 Cluster 将多个服务节点合并为一个,并生成一个 Invoker。
-
创建完Invoker后,包装Invoker,生成引用类的代理
也就是createProxy的最后一个步骤
private T createProxy(Map<String, String> map) { // ......省略 return (T) proxyFactory.getProxy(invoker); }
这里,代理工厂通过SPI加载默认的 JavassistProxyFactory, 将invoker包装成InvokerInvocationHandler,再传给一个代理对象再次包装。以后凡是调用引入服务的地方,引用的都是这个代理类。
@SPI("javassist") public interface ProxyFactory {
public class JavassistProxyFactory extends AbstractProxyFactory { @SuppressWarnings("unchecked") public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) { return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker)); } }
调试dubbo-3.服务引用
最新推荐文章于 2023-05-29 23:03:52 发布