(二)dubo源码分析之服务导出

  • 概述
    服务导出就是将服务暴露出去,暴露出去就必须支持网络调用的,也就是启动一个端点,供别的服务来调用。那么这就是我们要讨论的,dubo是如何将一个普通接口实现远程网络调用的,这个原理是啥,让我们带这个疑问去一步步揭开吧。

  • Demo

    //xml方式配置,暴露一个服务
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
             http://dubbo.apache.org/schema/dubbo
             http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    
    	//这会解析为ApplicationConfig对象
        <dubbo:application name="hello-word-app"/> //应用基本信息
    	//这会解析为ApplicationConfig对象
        <dubbo:registry address="zookeeper://localhost:2181"/> //注册中心
    	//这会解析为ProtocolConfig对象
        <dubbo:protocol name="dubbo" port="20880"/> //抓换协议
    
    	//暴露服务
    	//这会解析为ServiceBean对象,ApplicationConfig,ProtocolConfig都会注入到ServiceBean中
        <dubbo:service interface="com.example.dubbo.service.GreetingService" ref="greetingService"/> 
    
    	//服务载体bean
        <bean id="greetingService" class="com.example.dubbo.service.GreetingServiceImpl"/>
    	
    </beans>
    
    //编码方式暴露服务
       public class ProviderTest {
          
          
    
    public static void main(String[] args) throws IOException {
          
          
    	//相当于<dubbo:service/> 
        ServiceConfig<GreetingService> serviceServiceConfig = new ServiceConfig<GreetingService>();
    	//ApplicationConfig相当于 <dubbo:application/>
        serviceServiceConfig.setApplication(new ApplicationConfig("first-dubbo-provider"));
    
        //设置配置中心----RegistryConfig对象相当于<dubbo:registry/>
        serviceServiceConfig.setRegistry(new RegistryConfig("zookeeper://localhost:2181"));
    
        //设置接口与实现
        serviceServiceConfig.setInterface(GreetingService.class);
        //GreetingServiceImpl相当于<bean />
        serviceServiceConfig.setRef(new GreetingServiceImpl());
    
        serviceServiceConfig.setVersion("1.0.0");
        serviceServiceConfig.setGroup("dubbo1");
    
        //导出服务----这一步由spring容器发生容器刷新完成的事件回调对每一个实现了
        //ApplicationListener接口回调方法去自动调用的
        serviceServiceConfig.export();
    
        System.out.println("service is started");
    
        System.in.read();
    }
    }
    //服务暴露接口定义
    public interface GreetingService {
          
          
    	String sayHello(String name);
    }
    //说明
    1. 不管哪种配置服务方式,其实内部原理都是一样的,只是表现形式不同
    2.  目前为止,我们可能在xml中没有发现触发export的动作,在编码实现是手动调了,
         那么xml方式是怎么来触发这个动作呢?那就是采用容器事件触发,发生的回调,如下
         //ServiceBean的onApplicationEvent,实现了ApplicationListener接口
    public void onApplicationEvent(ContextRefreshedEvent event) {
          
          
        if (!isExported() && !isUnexported()) {
          
          
            if (logger.isInfoEnabled()) {
          
          
                logger.info("The service ready on spring started. service: " + getInterface());
            }
            //容器创建完成,发布事件,对实现ApplicationListener的bean发生回调
            export();
        }
    }
    
  • 源码之流程

       //ServiceBean的事件回调方法,也是暴露服务的入口
        public void onApplicationEvent(ContextRefreshedEvent event) {
          
          
            if (!isExported() && !isUnexported()) {
          
          
                if (logger.isInfoEnabled()) {
          
          
                    logger.info("The service ready on spring started. service: " + getInterface());
                }
                //服务导出
                export();
            }
        }
        //ServiceBean的export方法
        @Override
        public void export() {
          
          
        	//调用父类的export方法,也就是ServiceConfig类型,这就与我们使用api调用对应上了
        	//其实ServiceBean也就是spring和dubbo的一个桥梁,利用spring的事件特性来自动在容器初始化完成
        	//自动调用暴露服务
            super.export();
            // Publish ServiceBeanExportedEvent
            publishExportEvent();
        }
        //ServiceConfig的export方法
       public synchronized void export() {
          
          
       		//做一些配置检查,比如 应用名称,配置中心等
            checkAndUpdateSubConfigs();
    		//这里判断是否配置应该暴露,根据ProviderConfig的export属性
    		//对应标签<dubbo:provider export="false" />注入的ProviderConfig类型Bean
            if (!shouldExport()) {
          
          
                return;
            }
    
            if (shouldDelay()) {
          
          
                delayExportExecutor.schedule(this::doExport, delay, TimeUnit.MILLISECONDS);
            } else {
          
          
                doExport();
            }
        }
        //对当前服务接口是否暴露过做个校验,继续做暴露操作(一个服务接口一个ServiceConfig类型Bean)
    	protected synchronized void doExport() {
          
          
            if (unexported) {
          
          
                throw new IllegalStateException("The service " + interfaceClass.getName() + " has already unexported!");
            }
            if (exported) {
          
          
                return;
            }
            exported = true;
    
            if (StringUtils.isEmpty(path)) {
          
          
                path = interfaceName;
            }
            doExportUrls();
        }
        //对多注册中心多协议暴露一个服务
        private void doExportUrls() {
          
          
        	//加载所有注册中心
            List<URL> registryURLs = loadRegistries(true);
            //对一个服务接口进行在多个注册中心,每个注册中心暴露支持多种协议的服务
            for (ProtocolConfig protocolConfig : protocols) {
          
          
            	//生成标识一终协议服务的key 协议类型+/+暴露接口的全路径名称+服务版本
            	//比如当前要生成的是dubbo协议的服务,那么就是 dubb/......
                String pathKey = URL.buildKey(getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), group, version);
                //将一个要暴露的服务封装为一个ProviderModel对象,ProviderModel对象有详细关于
                //这个暴露服务实例相关的信息 比如pathKey,服务实例bean,bean的所有接口名称和参数
                ProviderModel providerModel = new ProviderModel(pathKey, ref, interfaceClass);
                //ApplicationModel里面存的是所有暴露的服务集合
                //ConcurrentMap<String, ProviderModel> providedServices
                //providedServices是所有暴露服务的Map集合,key是pathKey
                ApplicationModel.initProviderModel(pathKey, providerModel);
                doExportUrlsFor1Protocol(protocolConfig, registryURLs);
            }
        }
    
    private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
          
          
            String name = protocolConfig.getName();
            if (StringUtils.isEmpty(name)) {
          
          
                name = Constants.DUBBO;
            }
    
    		//使用一个Map保存当前需要暴露服务的所有相关参数,后面要拼装为一个类型协议的字符串
            Map<String, String> map = new HashMap<String, String>();
            map.put(Constants.SIDE_KEY, Constants.PROVIDER_SIDE);
            appendRuntimeParameters(map);
            appendParameters(map, application);
            appendParameters(map, module);
            appendParameters(map, provider, Constants.DEFAULT_KEY);
            appendParameters(map, protocolConfig);
            appendParameters(map, this);
            
            // export service
            //获取要暴露服务的主机ip,在protocolConfig中可配,否则默认用本机ip
            String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
            //获取要暴露的主机端口,在protocolConfig中可配
            Integer port = this.findConfigedPorts(protocolConfig, name, map);
            //生成对应协议的url,使用当前暴露服务的参数进行拼装,如下示例下我生成的,总之包含所有要的数据
            //dubbo://192.168.1.7:20880/com.example.dubbo.service.GreetingService?
            //anyhost=true&application=hello-word-app&bean.name=com.example.dubbo.service.GreetingService&........
            URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);
    
            if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                    .hasExtension(url.getProtocol())) {
          
          
                url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                        .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
            }
    
            //获取scope参数,是一个作用域的意思,就是服务暴露的范围
            //none 表示不暴露
            //remote 表示暴露在远程
            //local表示暴露在本地
            //null 默认 表示本地和远程都暴露
            String scope = url.getParameter(Constants.SCOPE_KEY);
            // don't export when none is configured
            if (!Constants.SCOPE_NONE.equalsIgnoreCase(scope)) {
          
          
    
    			//暴露在本地
                // export to local if the config is not remote (export to remote only when config is remote)
                if (!Constants.SCOPE_REMOTE.equalsIgnoreCase(scope)) {
          
          
                    exportLocal(url);
                }
                //暴露给远程
                // export to remote if the config is not local (export to local only when config is local)
                if (!Constants.SCOPE_LOCAL.equalsIgnoreCase(scope)) {
          
          
                    if (logger.isInfoEnabled()) {
          
          
                        logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                    }
                    if (CollectionUtils.isNotEmpty(registryURLs)) {
          
          
                        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<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
                            DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
    
                            Exporter<?> exporter = protocol.export(wrapperInvoker);
                            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);
                    }
                    /**
                     * @since 2.7.0
                     * ServiceData Store
                     */
                    MetadataReportService metadataReportService = null;
                    if ((metadataReportService = getMetadataReportService()) != null) {
          
          
                        metadataReportService.publishProvider(url);
                    }
                }
            }
            //添加当前服务的一种类型协议的url
            this.urls.add(url);
        }
    
  • 源码之远程暴露

     if (!Constants.SCOPE_LOCAL.equalsIgnoreCase(scope)) {
          
          
                    if (logger.isInfoEnabled()) {
          
          
                        logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                    }
                    if (CollectionUtils.isNotEmpty(registryURLs)) {
          
          
                        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);
                            }
    
    						//1.proxyFactory是一个扩展点的实现,是ServiceConfig的一个属性,默认赋值
    						//拓展点适配器实现ProxyFactory$Adaptive(自动生成,后面给出代码)
    						//2.proxyFactory.getInvoker实际上是先使用适配器的实现去获取实际需要的拓展点实现
    						//默认获取到的是JavassistProxyFactory
    						//3.在转发调用JavassistProxyFactory的getInvoker方法获取Invoker对象
                            Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
                            DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
    
    						//暴露服务到zookeeper(注册中心)
    						//protocol也是一个拓展点,具体也和proxyFactory差不多,
    						//1.先调用适配器实现的export,获取到需要的具体协议的实现
    						//2.比如dubbo协议,那么调用DubboProtocol的export进行注册服务
                            Exporter<?> exporter = protocol.export(wrapperInvoker);
                            exporters.add(exporter);
                        }
                    } 
    
    
    功能:生成发生远程调用需要执行的执行载体---Invoker(服务之间交互的核心)
    
    ProxyFactory$Adaptive源码
    class ProxyFactory$Adaptive implements org.apache.dubbo.rpc.ProxyFactory {
          
          
        public org.apache.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, org.apache.dubbo.common.URL arg2) throws org.apache.dubbo.rpc.RpcException {
          
          
            if (arg2 == null) throw new IllegalArgumentException("url == null");
            org.apache.dubbo.common.URL url = arg2;
            //默认使用javassist
            String extName = url.getParameter("proxy", "javassist");
            if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
            //根据extName选取ProxyFactory的全部实现中key为javassist-----JavassistProxyFactory
            org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
            //调用getInvoker方法真正去获取Invoker对象
            return extension.getInvoker(arg0, arg1, arg2);
        }
    
    JavassistProxyFactory中获取Invoker
    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) {
          
          
        // 生成Wrapper对象,远程发生调用调用的就是Wrapper的invokeMethod去执行
        //Wrapper对象包装了proxy,是为了减少反射,后面给出Wrapper代码你就明白了
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
        //使用匿名内部类创建Invoker对象,发生远程调用就是调用Invoker对象的doInvoke方法,
        //doInvoke方法内部实现采用的Wrapper的invokeMethod,所以最终发生远程调用,
        //执行的是Wrapper的invokeMethod
        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);
            }
        };
    }}	
    
    Wrapper源码的invokeMethod
    public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException{
          
          
        com.example.dubbo.service.GreetingService w;
        try{
          
          
        	//很明显,这是GreetingService实现的Wrapper的invokeMethod
        	//可以直接强转,直接调用,就不需要再使用反射去调用了
            w = ((com.example.dubbo.service.GreetingService)o);
        }catch(Throwable e){
          
          
            throw new IllegalArgumentException(e);
        }
    	//1.直接根据GreetingService接口生成的代码
    	//2.发生调用直接根据方法名称直接调用,直接做个匹配检测就好
    	//3.参数
    	    a.o是被代理的真正实现的对象实例
    	    b.n表示方法名称
    	    c.v表示方法参数列表
        try{
          
           if( "sayHello".equals( n )  && v.length == 1 ) {
          
          
            return w.sayHello((java.lang.String)v[0]); }
        } catch(Throwable e) {
          
          
            throw new java.lang.reflect.InvocationTargetException(e);
        }
        throw new org.apache.dubbo.common.bytecode.NoSuchMethodException("Not found method \""+n+"\" in class com.example.dubbo.service.GreetingService.");
    }
    
    功能:发布服务启动netty, 将服务注册到注册中心
    
    //比如我们用的是dubbo协议进行发布服务---DubboProtocol
     @Override
        public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
          
          
            URL url = invoker.getUrl();
    
            // export service.
            //key是一个服务的多个参数拼接的唯一标识,服务调用使用这个key来找到DubboExporter
            //比如 dubboGroup/com.example.dubbo.service.GreetingService:1.0.0:20880
            String key = serviceKey(url);
            //将invoker转换为DubboExporter
            DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
            //将DubboExporter存储在DubboProtocol中,并且和key做了映射关系
            exporterMap.put(key, exporter);
    
            //export an stub service for dispatching event
            Boolean isStubSupportEvent = url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT);
            Boolean isCallbackservice = url.getParameter(Constants.IS_CALLBACK_SERVICE, false);
            if (isStubSupportEvent && !isCallbackservice) {
          
          
                String stubServiceMethods = url.getParameter(Constants.STUB_EVENT_METHODS_KEY);
                if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
          
          
                    if (logger.isWarnEnabled()) {
          
          
                        logger.warn(new IllegalStateException("consumer [" + url.getParameter(Constants.INTERFACE_KEY) +
                                "], has set stubproxy support event ,but no stub methods founded."));
                    }
    
                } else {
          
          
                    stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
                }
            }
    
    		//开启默认netty作为服务传输层,使用zookeeper作为默认的注册中心
    		//这里使用到了两个拓展点  Transporter((传输),RegistryFactory(注册中心)
            openServer(url);
            optimizeSerialization(url);
    
            return e
    

    在这里插入图片描述
    说明:注册后,zookeeper显示的内容

猜你喜欢

转载自blog.csdn.net/weixin_38312719/article/details/106065541