Dubbo之服务导出
dubbo版本
- dubbo版本2.6.7
暴露前准备
-
服务导出的入口在
ServiceBean
中,ServiceBean
实现了ApplicationListener,即在容器初始化的时候执行onApplicationEvent()方法。ServiceBean
是Spring配置与dubbo整合的桥梁@Override public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; //将applicationContext设置到SpringExtensionFactory中,用于后续从SpringExtensionFactory中获取Bean SpringExtensionFactory.addApplicationContext(applicationContext); //兼容Spring2.0.1,如果支持返回true,否则返回false supportedApplicationListener = addApplicationListener(applicationContext, this); } @Override public void onApplicationEvent(ContextRefreshedEvent event) { /** * delay没有设置或者是-1 && 服务没有暴露过 && 没有取消暴露,则进行服务暴露 */ if (isDelay() && !isExported() && !isUnexported()) { if (logger.isInfoEnabled()) { logger.info("The service ready on spring started. service: " + getInterface()); } //导出服务 export(); } } @Override public void onApplicationEvent(ContextRefreshedEvent event) { /** * 是否延迟暴露 && 服务没有暴露过 && 没有取消暴露,则进行服务暴露 * 没有设置delay或者-1表示不需要延迟暴露 */ if (isDelay() && !isExported() && !isUnexported()) { if (logger.isInfoEnabled()) { logger.info("The service ready on spring started. service: " + getInterface()); } //导出服务 export(); } } /** * 如果没有设置或者-1,该方法返回true,表示不延迟暴露。 * 此方法返回值与语义有些不符 * 官方新版本已经重构https://github.com/apache/dubbo/pull/2686 */ private boolean isDelay() { Integer delay = getDelay(); ProviderConfig provider = getProvider(); if (delay == null && provider != null) { delay = provider.getDelay(); } return supportedApplicationListener && (delay == null || delay == -1); }
-
检查
ServiceConfig#export
,如果只是想本地启动服务进行一些调试工作,我们并不希望把本地启动的服务暴露出去给别人调用,可以通过配置<dubbo:provider export="false" />
public synchronized void export() { if (provider != null) { if (export == null) { export = provider.getExport(); } if (delay == null) { delay = provider.getDelay(); } } // 如果 export 为 false,则不导出服务 if (export != null && !export) { return; } //有延迟配置(delay>0),则延迟导出 if (delay != null && delay > 0) { delayExportExecutor.schedule(new Runnable() { @Override public void run() { doExport(); } }, delay, TimeUnit.MILLISECONDS); } else { doExport(); } }
-
ServiceConfig#doExport
导出服务的预检查和预处理方法protected synchronized void doExport() { 1. 检查是否已经暴露或者是否已经unexported ...省略... 2. 检查默认配置 ...省略... 3. 泛化调用设置 //如果是泛化调用,设置generic为true if (ref instanceof GenericService) { interfaceClass = GenericService.class; if (StringUtils.isEmpty(generic)) { generic = Boolean.TRUE.toString(); } } else { try { interfaceClass = Class.forName(interfaceName, true, Thread.currentThread() .getContextClassLoader()); } catch (ClassNotFoundException e) { throw new IllegalStateException(e.getMessage(), e); } // 对 interfaceClass,以及 <dubbo:method> 标签中的必要字段进行检查 checkInterfaceAndMethods(interfaceClass, methods); //检查实现类是否为空或者是否为接口的实例 checkRef(); // 设置 generic = "false" generic = Boolean.FALSE.toString(); } //local属性已被弃用,由stub属性替代 if (local != null) { if ("true".equals(local)) { local = interfaceName + "Local"; } Class<?> localClass; try { localClass = ClassHelper.forNameWithThreadContextClassLoader(local); } catch (ClassNotFoundException e) { throw new IllegalStateException(e.getMessage(), e); } if (!interfaceClass.isAssignableFrom(localClass)) { throw new IllegalStateException("The local implementation class " + localClass.getName() + " not implement interface " + interfaceName); } } // stub设为true,表示使用缺省代理类名,即:接口名 +Stub后缀, // 服务接口客户端本地代理类名,用于在客户端执行本地逻辑,如本地缓存等, // 该本地代理类的构造函数必须允许传入远程代理对象, // 构造函数如:public XxxServiceStub(XxxService xxxService) if (stub != null) { if ("true".equals(stub)) { stub = interfaceName + "Stub"; } Class<?> stubClass; try { stubClass = ClassHelper.forNameWithThreadContextClassLoader(stub); } catch (ClassNotFoundException e) { throw new IllegalStateException(e.getMessage(), e); } if (!interfaceClass.isAssignableFrom(stubClass)) { throw new IllegalStateException("The stub implementation class " + stubClass.getName() + " not implement interface " + interfaceName); } } checkApplication(); checkRegistry(); checkProtocol(); appendProperties(this); checkStub(interfaceClass); checkMock(interfaceClass); if (path == null || path.length() == 0) { path = interfaceName; } //导出服务 doExportUrls(); // ProviderModel 表示服务提供者模型,此对象中存储了与服务提供者相关的信息。 // 比如服务的配置信息,服务实例等。每个被导出的服务对应一个 ProviderModel。 // ApplicationModel 持有所有的 ProviderModel。 ProviderModel providerModel = new ProviderModel(getUniqueServiceName(), this, ref); ApplicationModel.initProviderModel(getUniqueServiceName(), providerModel); }
ServiceConfig#doExport
-
ServiceConfig#doExport
是服务暴露的入口ServiceConfig#doExport ->ServiceConfig#doExportUrls -> ServiceConfig#doExportUrlsFor1Protocol
-
ServiceConfig#doExportUrls
:加载注册中心配置,如果服务指定暴露多个协议(Dubbo、 REST),则遍历并依次暴露。每个协议注册元数据都会写入多个注册中心,如果没有配置注册中心,会使用默认的全局配置中心private void doExportUrls() { //加载注册中心连接 List<URL> registryURLs = loadRegistries(true); // 如果服务指定暴露多个协议(Dubbo Rest),则遍历并依次暴露 for (ProtocolConfig protocolConfig : protocols) { doExportUrlsFor1Protocol(protocolConfig, registryURLs); } }
-
ServiceConfig#doExportUrlsFor1Protocol
主要暴露流程- 通过反射获取相关配置对象(
ApplicationConfig
、ModuleConfig
、ProviderConfig
、SerivceConfig
等)中的属性,并存入到Map中,用于后续构造URL参数 - 本地服务暴露:Dubbo默认会把远程服务用
inJvm
协议暴露一份,这样同一个JVM内部又引入自身服务的情况时,消费方直接消费同一个JVM内部的服务,避免了远程跨网络通信。每个服务默认都会在本地暴露,在引用服务的时候,默认优先引用本地服务。如果希望引用远程服务可以使用配置强制引用远程服务,<dubbo:reference ... scope="remote" />
- 创建Invoker:通过动态代理(默认JavassistProxyFactory)将接口转换成Invoker
- 创建Exporter:调用
protocol.export
(实际调用了Protocol的适配器类Protocol$Adaptive的export方法)导出,并启动Server(ExchangeServer
),返回Exporter
- 通过反射获取相关配置对象(
-
doExportUrlsFor1Protocol
源码private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) { 1. 设置参数到Map中 ...省略.. 2. 解析MethodConfig对象(方法级别的配置参数)保存到map中 ...省略... 3. 泛化调用和普通调用的配置参数 if (ProtocolUtils.isGeneric(generic)) { map.put(Constants.GENERIC_KEY, generic); map.put(Constants.METHODS_KEY, Constants.ANY_VALUE); } else { String revision = Version.getVersion(interfaceClass, version); if (revision != null && revision.length() > 0) { map.put("revision", revision); } String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames(); if (methods.length == 0) { logger.warn("NO method found in service interface " + interfaceClass.getName()); map.put(Constants.METHODS_KEY, Constants.ANY_VALUE); } else { map.put(Constants.METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ",")); } } if (!ConfigUtils.isEmpty(token)) { if (ConfigUtils.isDefault(token)) { map.put(Constants.TOKEN_KEY, UUID.randomUUID().toString()); } else { map.put(Constants.TOKEN_KEY, token); } } if (Constants.LOCAL_PROTOCOL.equals(protocolConfig.getName())) { protocolConfig.setRegister(false); map.put("notify", "false"); } // export service String contextPath = protocolConfig.getContextpath(); if ((contextPath == null || contextPath.length() == 0) && provider != null) { contextPath = provider.getContextpath(); } //获取host,1.从配置中获取 2.返回本地ip 3.获取注册中心的主机名,4.遍历本地网卡,返回第一个合理的IP String host = this.findConfigedHosts(protocolConfig, registryURLs, map); //获取各个协义需要暴露的端口.获取优先级:1.Protocol的实现类的默认端口 2.Protocol的配置端口 3.随机端口 Integer port = this.findConfigedPorts(protocolConfig, name, map); //拼接URL URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map); //ConfiguratorFactory是个扩展,可以用来设计自己的URL组成规则 if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class) .hasExtension(url.getProtocol())) { url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class) .getExtension(url.getProtocol()).getConfigurator(url).configure(url); } // 根据scope的配置决定是作本地暴露还是远程暴露, // 服务暴露就是产生一个特定的Exporter,并将其存储在对应的ServiceBean实例的 exporters属性中 String scope = url.getParameter(Constants.SCOPE_KEY); // don't export when none is configured //配置scope='none'则不暴露 if (!Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) { // export to local if the config is not remote (export to remote only when config is remote) // 如果没有指定scope="remote",每个服务默认都会在本地暴露(injvm协议) if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) { exportLocal(url); } // export to remote if the config is not local (export to local only when config is local) //如果配置不是local,则暴露远程服务,配置为local则只本地暴露 if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) { if (logger.isInfoEnabled()) { logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url); } //如果配置了多注册中心地址,则遍历所有(比如:不同机房 <dubbo:registry address="zookeeper://dubbo-zookeeper:2181 | zookeeper://172.16.163.60:2181"/>) if (registryURLs != null && !registryURLs.isEmpty()) { for (URL registryURL : registryURLs) { url = url.addParameterIfAbsent(Constants.DYNAMIC_KEY, registryURL.getParameter(Constants.DYNAMIC_KEY)); URL monitorUrl = loadMonitor(registryURL); if (monitorUrl != null) { //追加监控地址 url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString()); } if (logger.isInfoEnabled()) { logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL); } // For providers, this is used to enable custom proxy to generate invoker String proxy = url.getParameter(Constants.PROXY_KEY); if (StringUtils.isNotEmpty(proxy)) { registryURL = registryURL.addParameter(Constants.PROXY_KEY, proxy); } //通过动态代理转换成Invoker,registryURL是注册中心地址 Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString())); DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this); //Protocol$Adaptive -> url.getProtocol-> RegistryProtocol Exporter<?> exporter = protocol.export(wrapperInvoker); //RegistryProtocol$DestroyableExporter exporters.add(exporter); } } else { //直连方式 Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url); DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this); Exporter<?> exporter = protocol.export(wrapperInvoker); exporters.add(exporter); } } } this.urls.add(url); }
-
如果为远程服务暴露,则其内部根据URL中Protocol的类型为registry,会选择Protocol的实现类RegistryProtocol。如果为本地服务暴露,则其内部根据URL中Protocol的类型为injvm,会选择Protocol的实现类InjvmProtocol
-
由于Dubbo SPI的扩展点使用了Wrapper自动增强,对于Protocol来说,ProtocolFilterWrapper、ProtocolListenerWrapper、QosProtocolWrapper对其进行了增强,所以需要一层层调用才会调用到RegistryProtocol的export方法。
在dubbo-rpc/dubbo-rpc-api模块 filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper 在dubbo-plugin/dubbo-qos模块 qos=com.alibaba.dubbo.qos.protocol.QosProtocolWrapper
本地暴露
-
ServiceConfig#exportLocal
是本地暴露的核心实现private void exportLocal(URL url) { //如果没有显示指定local(避免重复暴露) if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) { URL local = URL.valueOf(url.toFullString()) .setProtocol(Constants.LOCAL_PROTOCOL) .setHost(LOCALHOST) .setPort(0); //ref是接口的实现。StaticContext是一个Map,存储serviceKey与接口的实现 //ServiceKey为group0/com.alibaba.dubbo.demo.DemoService:1.0.0 StaticContext.getContext(Constants.SERVICE_IMPL_CLASS).put(url.getServiceKey(), getServiceClass(ref)); //Protocol$Adaptive,默认Protocol实现是DubboProtocol //自适应类ProxyFactory$Adaptive,这个类会根据url的ProxyFactory参数选择对应的实现类进行操作,默认ProxyFactory是JavassistProxyFactory //ref是接口实现的实现类,JavassistProxyFactory.getInvoker Exporter<?> exporter = protocol.export( proxyFactory.getInvoker(ref, (Class) interfaceClass, local)); exporters.add(exporter); logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry"); } }
远程暴露
-
远程暴露
ServiceConfig#doExportUrlsFor1Protocol
//通过动态代理转换成Invoker,registryURL是注册中心地址 Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString())); DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this); //Protocol$Adaptive -> url.getProtocol-> RegistryProtocol Exporter<?> exporter = protocol.export(wrapperInvoker); //RegistryProtocol$DestroyableExporter exporters.add(exporter);
-
远程暴露流程
- 通过
ProxyFactory$Adaptive
获取Invoker对象。通过服务的实现类(ref
),生成动态代理类com.alibaba.dubbo.common.bytecode.Wrapper1
。所有调用wrapper的invokeMethod方法,都会转为调用真正的实现类调用。这样就避免的反射的开销,对外统一使用wrapper的invokeMethod方法进行统一调用即可 - 通过
Protocol$Adaptive
获取protocol
(默认是RegistryProtocol
),因为Protocol有默认的Wrapper类,所以先走Wrapper类,然后最终调用RegistryProtocol#export
RegistryProtocol#export
委托DubboProtocol进行服务暴露,打开服务端口。然后注册元数据到注册中心
- 通过
-
暴露流程简图
-
服务发布总图
-
服务暴露序列图
创建Invoker
-
ProxyFactory
主要是将Invoker
与业务接口的转换。内置有两个扩展类JdkProxyFactory
和JavassistProxyFactory
,默认使用JavassistProxyFactory
-
接口源码
@SPI("javassist") public interface ProxyFactory { @Adaptive({Constants.PROXY_KEY}) <T> T getProxy(Invoker<T> invoker) throws RpcException; @Adaptive({Constants.PROXY_KEY}) <T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException; @Adaptive({Constants.PROXY_KEY}) <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException; }
-
配置
<dubbo:protocol proxy="xxx" /> <!-- 缺省值配置,当<dubbo:protocol>没有配置proxy属性时,使用此配置 --> <dubbo:provider proxy="xxx" />
查看ProxyFactory$Adaptive源码
-
每个扩展接口对应一个适配器类,并且这个适配器类是使用动态编译技术生成的,一般情况下只有使用Debug才能看到适配器的源码。也可以使用
Arthas
在服务启动后查看适配器的源码$ sh as.sh Arthas script version: 3.4.4 ...省略... * [1]: 58225 com.alibaba.dubbo.demo.provider.Provider [2]: 2178 [3]: 58223 org.jetbrains.jps.cmdline.Launcher # 输入1 1 [arthas@58225]$ jad com.alibaba.dubbo.rpc.ProxyFactory$Adaptive ClassLoader: +-sun.misc.Launcher$AppClassLoader@14dad5dc +-sun.misc.Launcher$ExtClassLoader@382e66f9 Location: /...省略.../dubbo-common/target/classes/
-
实现类
com.alibaba.dubbo.demo.provider.DemoServiceImpl
public class DemoServiceImpl implements DemoService { @Override public String sayHello(String name) { System.out.println("[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "] Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress()); return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress(); } }
-
动态代理类的
com.alibaba.dubbo.common.bytecode.Wrapper1
。通过服务的实现类,生成动态代理类。所有调用wrapper的invokeMethod方法,都会转为调用真正的实现类调用。这样就避免的反射的开销,对外统一使用wrapper的invokeMethod方法进行统一调用即可/* * Decompiled with CFR. * * Could not load the following classes: * com.alibaba.dubbo.common.bytecode.ClassGenerator$DC * com.alibaba.dubbo.common.bytecode.NoSuchMethodException * com.alibaba.dubbo.common.bytecode.NoSuchPropertyException * com.alibaba.dubbo.common.bytecode.Wrapper * com.alibaba.dubbo.demo.provider.DemoServiceImpl */ package com.alibaba.dubbo.common.bytecode; import com.alibaba.dubbo.common.bytecode.ClassGenerator; import com.alibaba.dubbo.common.bytecode.NoSuchMethodException; import com.alibaba.dubbo.common.bytecode.NoSuchPropertyException; import com.alibaba.dubbo.common.bytecode.Wrapper; import com.alibaba.dubbo.demo.provider.DemoServiceImpl; import java.lang.reflect.InvocationTargetException; import java.util.Map; public class Wrapper1 extends Wrapper implements ClassGenerator.DC { public static String[] pns; public static Map pts; public static String[] mns; public static String[] dmns; public static Class[] mts0; public String[] getPropertyNames() { return pns; } public boolean hasProperty(String string) { return pts.containsKey(string); } public Class getPropertyType(String string) { return (Class)pts.get(string); } public String[] getMethodNames() { return mns; } public String[] getDeclaredMethodNames() { return dmns; } public void setPropertyValue(Object object, String string, Object object2) { try { DemoServiceImpl demoServiceImpl = (DemoServiceImpl)object; } catch (Throwable throwable) { throw new IllegalArgumentException(throwable); } throw new NoSuchPropertyException(new StringBuffer().append("Not found property \"").append(string).append("\" filed or setter method in class com.alibaba.dubbo.demo.provider.DemoServiceImpl.").toString()); } public Object getPropertyValue(Object object, String string) { try { DemoServiceImpl demoServiceImpl = (DemoServiceImpl)object; } catch (Throwable throwable) { throw new IllegalArgumentException(throwable); } throw new NoSuchPropertyException(new StringBuffer().append("Not found property \"").append(string).append("\" filed or setter method in class com.alibaba.dubbo.demo.provider.DemoServiceImpl.").toString()); } public Object invokeMethod(Object object, String string, Class[] classArray, Object[] objectArray) throws InvocationTargetException { DemoServiceImpl demoServiceImpl; try { demoServiceImpl = (DemoServiceImpl)object; } catch (Throwable throwable) { throw new IllegalArgumentException(throwable); } try { if ("sayHello".equals(string) && classArray.length == 1) { return demoServiceImpl.sayHello((String)objectArray[0]); } } catch (Throwable throwable) { throw new InvocationTargetException(throwable); } throw new NoSuchMethodException(new StringBuffer().append("Not found method \"").append(string).append("\" in class com.alibaba.dubbo.demo.provider.DemoServiceImpl.").toString()); } }
-
ProxyFactory$Adaptive
源码,可以看到默认proxy="javassist"
/* * Decompiled with CFR. * * Could not load the following classes: * com.alibaba.dubbo.common.URL * com.alibaba.dubbo.common.extension.ExtensionLoader * com.alibaba.dubbo.rpc.Invoker * com.alibaba.dubbo.rpc.ProxyFactory * com.alibaba.dubbo.rpc.RpcException */ package com.alibaba.dubbo.rpc; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.common.extension.ExtensionLoader; import com.alibaba.dubbo.rpc.Invoker; import com.alibaba.dubbo.rpc.ProxyFactory; import com.alibaba.dubbo.rpc.RpcException; public class ProxyFactory$Adaptive implements ProxyFactory { public Object getProxy(Invoker invoker) throws RpcException { if (invoker == null) { throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null"); } if (invoker.getUrl() == null) { throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null"); } URL uRL = invoker.getUrl(); String string = uRL.getParameter("proxy", "javassist"); if (string == null) { throw new IllegalStateException(new StringBuffer().append("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(").append(uRL.toString()).append(") use keys([proxy])").toString()); } ProxyFactory proxyFactory = (ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension(string); return proxyFactory.getProxy(invoker); } public Object getProxy(Invoker invoker, boolean bl) throws RpcException { if (invoker == null) { throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null"); } if (invoker.getUrl() == null) { throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null"); } URL uRL = invoker.getUrl(); String string = uRL.getParameter("proxy", "javassist"); if (string == null) { throw new IllegalStateException(new StringBuffer().append("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(").append(uRL.toString()).append(") use keys([proxy])").toString()); } ProxyFactory proxyFactory = (ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension(string); return proxyFactory.getProxy(invoker, bl); } public Invoker getInvoker(Object object, Class clazz, URL uRL) throws RpcException { if (uRL == null) { throw new IllegalArgumentException("url == null"); } URL uRL2 = uRL; String string = uRL2.getParameter("proxy", "javassist"); if (string == null) { throw new IllegalStateException(new StringBuffer().append("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(").append(uRL2.toString()).append(") use keys([proxy])").toString()); } ProxyFactory proxyFactory = (ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension(string); return proxyFactory.getInvoker(object, clazz, uRL); } }
JavassistProxyFactory
-
源码
public class JavassistProxyFactory extends AbstractProxyFactory { @Override @SuppressWarnings("unchecked") public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) { return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker)); } @Override public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) { // TODO Wrapper cannot handle this scenario correctly: the classname contains '$' // proxy是业务接口的实现类,dubbo会给每个服务提供者的实现类生产一个wrapper类, // 最终调用服务提供方的接口实现类,wrapper类的存在是为了减少反射的调用 final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type); return new AbstractProxyInvoker<T>(proxy, type, url) { @Override protected Object doInvoke(T proxy, String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Throwable { return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments); } }; } }
总结
-
Dubbo可以延迟暴露,如果本地启动服务调试,可以不进行暴露(别人无法调用)
<dubbo:provider export="false" />
-
默认每个服务都会在本地暴露,本地暴露使用InjvmProtocol(保存在Map中),返回InjvmExporter。同一JVM内部引用自身的服务时,直接消费内部服务,避免远程调用