jannal(无名小宝)

没有失败,只有缓慢的成功

导航

Dubbo之服务导出

dubbo版本

  1. dubbo版本2.6.7

暴露前准备

  1. 服务导出的入口在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);
    }
    
  2. 检查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();
        }
    }
    
  3. 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

  1. ServiceConfig#doExport是服务暴露的入口

    ServiceConfig#doExport
    	->ServiceConfig#doExportUrls
        -> ServiceConfig#doExportUrlsFor1Protocol
    
  2. ServiceConfig#doExportUrls:加载注册中心配置,如果服务指定暴露多个协议(Dubbo、 REST),则遍历并依次暴露。每个协议注册元数据都会写入多个注册中心,如果没有配置注册中心,会使用默认的全局配置中心

    private void doExportUrls() {
        //加载注册中心连接
        List<URL> registryURLs = loadRegistries(true);
        // 如果服务指定暴露多个协议(Dubbo Rest),则遍历并依次暴露
        for (ProtocolConfig protocolConfig : protocols) {
            doExportUrlsFor1Protocol(protocolConfig, registryURLs);
        }
    }
    
  3. ServiceConfig#doExportUrlsFor1Protocol主要暴露流程

    • 通过反射获取相关配置对象(ApplicationConfigModuleConfigProviderConfigSerivceConfig等)中的属性,并存入到Map中,用于后续构造URL参数
    • 本地服务暴露:Dubbo默认会把远程服务用inJvm协议暴露一份,这样同一个JVM内部又引入自身服务的情况时,消费方直接消费同一个JVM内部的服务,避免了远程跨网络通信。每个服务默认都会在本地暴露,在引用服务的时候,默认优先引用本地服务。如果希望引用远程服务可以使用配置强制引用远程服务,<dubbo:reference ... scope="remote" />
    • 创建Invoker:通过动态代理(默认JavassistProxyFactory)将接口转换成Invoker
    • 创建Exporter:调用protocol.export(实际调用了Protocol的适配器类Protocol$Adaptive的export方法)导出,并启动Server(ExchangeServer),返回Exporter
  4. 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);
    }
    
  5. 如果为远程服务暴露,则其内部根据URL中Protocol的类型为registry,会选择Protocol的实现类RegistryProtocol。如果为本地服务暴露,则其内部根据URL中Protocol的类型为injvm,会选择Protocol的实现类InjvmProtocol

  6. 由于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
    

本地暴露

  1. 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");
        }
    }
    

远程暴露

  1. 远程暴露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);
    
  2. 远程暴露流程

    • 通过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进行服务暴露,打开服务端口。然后注册元数据到注册中心
  3. 暴露流程简图

    暴露流程简图
  4. 服务发布总图

    服务发布总图

  5. 服务暴露序列图

    服务暴露序列图

创建Invoker

  1. ProxyFactory主要是将 Invoker 与业务接口的转换。内置有两个扩展类JdkProxyFactoryJavassistProxyFactory,默认使用JavassistProxyFactory

    ProxyFactory
  2. 接口源码

    @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;
    
    }
    
  3. 配置

    <dubbo:protocol proxy="xxx" />
    <!-- 缺省值配置,当<dubbo:protocol>没有配置proxy属性时,使用此配置 -->
    <dubbo:provider proxy="xxx" />
    

查看ProxyFactory$Adaptive源码

  1. 每个扩展接口对应一个适配器类,并且这个适配器类是使用动态编译技术生成的,一般情况下只有使用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/
    
  2. 实现类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();
        }
    
    }
    
  3. 动态代理类的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());
        }
    }
    
  4. 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

  1. 源码

    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);
                }
            };
        }
    
    }
    

总结

  1. Dubbo可以延迟暴露,如果本地启动服务调试,可以不进行暴露(别人无法调用)

    <dubbo:provider export="false" />
    
  2. 默认每个服务都会在本地暴露,本地暴露使用InjvmProtocol(保存在Map中),返回InjvmExporter。同一JVM内部引用自身的服务时,直接消费内部服务,避免远程调用

posted on 2021-12-25 22:51  jannal  阅读(66)  评论(0编辑  收藏  举报