Dubbo源码之Provider的服务发布源码解析(Java版)

1.Java版Provider启动内容

public class Provider {
	public static void main(String[] args) throws IOException {
		// 服务实现
		DemoService demoService = new DemoServiceImpl();
		 
		// 当前应用配置
		ApplicationConfig application = new ApplicationConfig();
		application.setName("hello-world-app-provider");
		 
		// 连接注册中心配置
		RegistryConfig registry = new RegistryConfig();
		registry.setAddress("zookeeper://192.168.133.129:2181");
		 
		// 服务提供者协议配置
		ProtocolConfig protocol = new ProtocolConfig();
		protocol.setName("dubbo");
		protocol.setPort(20880);
		//protocol.setThreads(200);
		 
		// 注意:ServiceConfig为重对象,内部封装了与注册中心的连接,以及开启服务端口
		 
		// 服务提供者暴露服务配置
		ServiceConfig<DemoService> service = new ServiceConfig<DemoService>(); // 此实例很重,封装了与注册中心的连接,请自行缓存,否则可能造成内存和连接泄漏
		service.setApplication(application);
		service.setRegistry(registry); // 多个注册中心可以用setRegistries()
		service.setProtocol(protocol); // 多个协议可以用setProtocols()
		service.setInterface(DemoService.class);
		service.setRef(demoService);
		service.setVersion("1.0.0");
		 
		// 暴露及注册服务
		service.export();
		System.in.read(); //需要等待
	}
}

2.ApplicationConfig的源码查看

 public ApplicationConfig() {
 }
 
  public void setName(String name) {
        checkName(NAME, name);//检查程序的名称
        this.name = name;
        if (StringUtils.isEmpty(id)) {
            id = name;
        }
    }

发现当前的setName就是设置当前程序的名称,没有特殊的操作

3.查看RegistryConfig的源码

  //当前RegistryConfig的构造函数
  public RegistryConfig() {
  }
  //当前调用的setAddress
  public void setAddress(String address) {
        this.address = address;
        if (address != null) {//当前的address肯定不为null
            int i = address.indexOf("://");//获取"://"所在的位置
            if (i > 0) {
                this.updateIdIfAbsent(address.substring(0, i));//这里实际上调用的是AbstractConfig中的方法
            }
        }
    }
	//当前父类AbstractConfig类中的updateIdIfAbsent方法
    public void updateIdIfAbsent(String value) {
        if (StringUtils.isNotEmpty(value) && StringUtils.isEmpty(id)) {
            this.id = value;//显示zookeeper
        }
    }

通过debug发现当前的setAddress中就是设置address,而updateIdIfAbsent是用来设置当前父辈的id为zookeeper

当前的RegistryConfig也没有什么特殊的

4.查看ProtocolConfig源码

 public ProtocolConfig() {
 }

 public final void setName(String name) {
        checkName("name", name);
        this.name = name;
        this.updateIdIfAbsent(name);//实际上还是调用AbstractConfig中的updateIdIfAbsent方法
 }

 public final void setPort(Integer port) {
        this.port = port;
 }

通过查看源码发现当前的ProtocolConfig就是用来设置端口和协议名称的类,没有特殊的方法

5.查看ServiceConfig源码(dubbo连接zookeeper的方式)

5.1 查看set方法和构造方法

 //构造方法
 public ServiceConfig() {
 }
 //set等方法
 public void setApplication(ApplicationConfig application) {
    ConfigManager.getInstance().setApplication(application);//通过当前的ConfigManager源码发现这个是一个懒汉的单例模式,这个类中具有基本上所有的配置信息
    this.application = application;
 }
 
 //setRegistry方法
 public void setRegistry(RegistryConfig registry) {
        List<RegistryConfig> registries = new ArrayList<RegistryConfig>(1);
        registries.add(registry);
        setRegistries(registries);
 }
 //查看setRegistries方法
  @SuppressWarnings({"unchecked"})
  public void setRegistries(List<? extends RegistryConfig> registries) {
      ConfigManager.getInstance().addRegistries((List<RegistryConfig>) registries);//发现还是调用ConfigManager单例类中的addRegistries方法
      this.registries = (List<RegistryConfig>) registries;//设置当前的ServiceConfig中设置registries
  }

 //setProtocol方法
 public void setProtocol(ProtocolConfig protocol) {
       setProtocols(new ArrayList<>(Arrays.asList(protocol)));
 }
 @SuppressWarnings({"unchecked"})
 public void setProtocols(List<? extends ProtocolConfig> protocols) {
     ConfigManager.getInstance().addProtocols((List<ProtocolConfig>) protocols);//发现调用了ConfigManager中添加Protocols
     this.protocols = (List<ProtocolConfig>) protocols;//设置当前ServiceConfig类的协议
 }

 //setInterface方法
 public void setInterface(Class<?> interfaceClass) {
 		//判断当前色通知的接口类是否为null和当前的类是否为接口类,如果不满足则抛出异常
        if (interfaceClass != null && !interfaceClass.isInterface()) {
            throw new IllegalStateException("The interface class " + interfaceClass + " is not a interface!");
        }
        this.interfaceClass = interfaceClass;
        setInterface(interfaceClass == null ? null : interfaceClass.getName());
 }
 public void setInterface(String interfaceName) {
        this.interfaceName = interfaceName;//设置当前的接口名
        if (StringUtils.isEmpty(id)) {
            id = interfaceName;
        }
 }

 //setRef方法
 public void setRef(T ref) {
        this.ref = ref;
 }
 
 //setVersion方法
 public void setVersion(String version) {
        checkKey(VERSION_KEY, version);//检查版本
        this.version = version;
 }

通过源码发现当前的set方法与ConfigManager这个单例类关系密切,当前的ConfigManager这个类中具有当前添加的属性

5.2 查看ServiceConfig中的export方法

 public synchronized void export() {
        checkAndUpdateSubConfigs();//通过debug发现当前的这个方法是最重要的方法

        if (!shouldExport()) {//是否应该暴露,false表示结束函数
            return;
        }

        if (shouldDelay()) {//判断当前是否有日程
            DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
        } else {
            doExport();//通过debug发现当前调用了doExprot方法而doExport方法调用了 doExportUrls()方法
        }
    }

1.通过debug发现实际上调用的是checkAndUpdateSubConfigs方法,才会再控制台打印一大堆输出,并连接到了当前的zookeeper中

2.通过debug发现当前的doExport方法是在连接到zookeeper后才会执行的方法,这个方法会调用doExportUrls方法暴露访问的url

5.3 查看ServiceConfig中的checkAndUpdateSubConfigs方法

    public void checkAndUpdateSubConfigs() {
        // 使用在全局配置上显式定义的默认配置
        completeCompoundConfigs();
        // Config Center should always being started first.
        //首先启动配置中心
        startConfigCenter();
        //检查默认配置
        checkDefault();
        //然后检查协议
        checkProtocol();
        //检查程序
        checkApplication();
        // if protocol is not injvm checkRegistry
        //如果当前的协议不是injvm就开始检查注册
        if (!isOnlyInJvm()) {
            checkRegistry();//通过debug发现当前的方法进入这里后就不执行了,说明这里才是核心方法
        }
        this.refresh();//刷新
        checkMetadataReport();//检查mate数据的报道

        if (StringUtils.isEmpty(interfaceName)) {//再次判断接口是否为空空
            throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
        }
		
		//如果当前的接口是GenericService,则进行处理
        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);
            }
            //然后检查接口和接口中的方法
            checkInterfaceAndMethods(interfaceClass, methods);
            //检查当前接口提供的实现类
            checkRef();
            generic = Boolean.FALSE.toString();
        }
        if (local != null) {
            if ("true".equals(local)) {
                local = interfaceName + "Local";
            }
            Class<?> localClass;
            try {
                localClass = ClassUtils.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);
            }
        }
        if (stub != null) {
            if ("true".equals(stub)) {
                stub = interfaceName + "Stub";
            }
            Class<?> stubClass;
            try {
            	//加载子类
                stubClass = ClassUtils.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);
            }
        }
        //检查子类和Local
        checkStubAndLocal(interfaceClass);
        checkMock(interfaceClass);
    }

通过当前的方法发现checkRegistry()这个方法才是连接Zookeeper的返回

5.4 查看ServiceConfig中的checkRegistry方法

  protected void checkRegistry() {
        loadRegistriesFromBackwardConfig();//通过配置加载当前注册中心

        convertRegistryIdsToRegistries();//转换注册到注册中心

        for (RegistryConfig registryConfig : registries) {
            if (!registryConfig.isValid()) {
                throw new IllegalStateException("No registry config found or it's not a valid config! " +
                        "The registry config is: " + registryConfig);
            }
        }

        useRegistryForConfigIfNecessary();//使用注册配置,通过debug发现这里才是重点
    }

通过debug发现当前的useRegistryForConfigIfNecessary方法才是重点

5.5 查看AbstractInterfaceConfig中的useRegistryForConfigIfNecessary方法

  private void useRegistryForConfigIfNecessary() {
        registries.stream().filter(RegistryConfig::isZookeeperProtocol).findFirst().ifPresent(rc -> {
            // we use the loading status of DynamicConfiguration to decide whether ConfigCenter has been initiated.
            Environment.getInstance().getDynamicConfiguration().orElseGet(() -> {
                //获取配置管理器
                ConfigManager configManager = ConfigManager.getInstance();
                ConfigCenterConfig cc = configManager.getConfigCenter().orElse(new ConfigCenterConfig());
                cc.setProtocol(rc.getProtocol());//创建配置中心的配置
                cc.setAddress(rc.getAddress());
                cc.setHighestPriority(false);
                setConfigCenter(cc);
                startConfigCenter();//这里是启动配置中心
                return null;
            });
        });
    }

这里的启动配置中心才是启动连接的关键startConfigCenter

5.6 查看AbstractInterfaceConfig中的startConfigCenter方法

void startConfigCenter() {
        if (configCenter == null) {//用于检查配置中心是否初始化
            ConfigManager.getInstance().getConfigCenter().ifPresent(cc -> this.configCenter = cc);//初始化配置中心
        }

        if (this.configCenter != null) {//这里肯定不为null
            // TODO there may have duplicate refresh
            this.configCenter.refresh();//刷新当前的配置中心
            prepareEnvironment();//准备环境
        }
        ConfigManager.getInstance().refreshAll();//通过配置管理刷新所有的数据
    }

debug发现当前的prepareEnvironment方法为重要方法

5.7 查看AbstractInterfaceConfig的prepareEnvironment方法

private void prepareEnvironment() {
        if (configCenter.isValid()) {//校验当前的配置中心
            if (!configCenter.checkOrUpdateInited()) {//如果配置中心为初始化就停止函数
                return;
            }
            DynamicConfiguration dynamicConfiguration = getDynamicConfiguration(configCenter.toUrl());//通过这里发现dynamicConfiguration 的实例为:ZookeeperDynamicConfiguration
            String configContent = dynamicConfiguration.getProperties(configCenter.getConfigFile(), configCenter.getGroup());

            String appGroup = application != null ? application.getName() : null;
            String appConfigContent = null;
            if (StringUtils.isNotEmpty(appGroup)) {
                appConfigContent = dynamicConfiguration.getProperties
                        (StringUtils.isNotEmpty(configCenter.getAppConfigFile()) ? configCenter.getAppConfigFile() : configCenter.getConfigFile(),
                         appGroup
                        );
            }
            try {
                Environment.getInstance().setConfigCenterFirst(configCenter.isHighestPriority());
                Environment.getInstance().updateExternalConfigurationMap(parseProperties(configContent));
                Environment.getInstance().updateAppExternalConfigurationMap(parseProperties(appConfigContent));
            } catch (IOException e) {
                throw new IllegalStateException("Failed to parse configurations from Config Center.", e);
            }
        }
    }

5.8 ZookeeperDynamicConfiguration这个类的的构造函数

ZookeeperDynamicConfiguration(URL url, ZookeeperTransporter zookeeperTransporter) {
        this.url = url;
        rootPath = PATH_SEPARATOR + url.getParameter(CONFIG_NAMESPACE_KEY, DEFAULT_GROUP) + "/config";

        initializedLatch = new CountDownLatch(1);
        this.cacheListener = new CacheListener(rootPath, initializedLatch);
        this.executor = Executors.newFixedThreadPool(1, new NamedThreadFactory(this.getClass().getSimpleName(), true));

        zkClient = zookeeperTransporter.connect(url);//发现这里有一个connect方法,这个就是连接Zookeeper的主要方法
        zkClient.addDataListener(rootPath, cacheListener, executor);//然后为当前的zookeeper客户端添加数据监听
        try {
            // Wait for connection
            this.initializedLatch.await();//等待连接
        } catch (InterruptedException e) {
            logger.warn("Failed to build local cache for config center (zookeeper)." + url);
        }
    }

1.通过ZookeeperDynamicConfiguration发现当前的这个类被实例化的时候会连接注册中心,并且还有添加数据监听!

2.然后会回到 5.6中的ConfigManager.getInstance().refreshAll();刷新所有的配置信息

3.然后回到ServiceConfig这个类的checkAndUpdateSubConfigs方法中的interfaceClass = Class.forName()创建接口类,然后检查当前接口的实例类,完成当前的配置信息的更新

6.查看ServiceConfig的doExport方法(暴露服务让外界调用)

 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();//实际调用doExportUrls()
    }

6.1 查看ServiceConfig的doExportUrls方法

 @SuppressWarnings({"unchecked", "rawtypes"})
    private void doExportUrls() {
        List<URL> registryURLs = loadRegistries(true);//获取组测中心的路劲
        for (ProtocolConfig protocolConfig : protocols) {
        	//获取访问的路径的key
            String pathKey = URL.buildKey(getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), group, version);
            //通过当前的接口类和实现类创建服务提供者模型
            ProviderModel providerModel = new ProviderModel(pathKey, ref, interfaceClass);
            ApplicationModel.initProviderModel(pathKey, providerModel);
            doExportUrlsFor1Protocol(protocolConfig, registryURLs);//这里是通过当前的协议暴露可以访问的urls
        }
    }

通过源码发现暴露服务其实是通过doExportUrlsFor1Protocol实现的

6.2 查看ServiceConfig的doExportUrlsFor1Protocol方法

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
        String name = protocolConfig.getName();//获取协议的名称
        if (StringUtils.isEmpty(name)) {//如果名称为null则设置协议名称为dubbo
            name = DUBBO;
        }

        Map<String, String> map = new HashMap<String, String>();
        map.put(SIDE_KEY, PROVIDER_SIDE);

        appendRuntimeParameters(map);
        appendParameters(map, metrics);
        appendParameters(map, application);
        appendParameters(map, module);
        // remove 'default.' prefix for configs from ProviderConfig
        // appendParameters(map, provider, Constants.DEFAULT_KEY);
        appendParameters(map, provider);
        appendParameters(map, protocolConfig);
        appendParameters(map, this);
        //通过集合工具半段当前的方法不是空的
     
        //协议检查,添加方法或者其他的版本信息
        if (ProtocolUtils.isGeneric(generic)) {
            map.put(GENERIC_KEY, generic);
            map.put(METHODS_KEY, ANY_VALUE);
        } else {
            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)), ","));//向当前的map集合中添加当前被映射的方法名称
            }
        }
        if (!ConfigUtils.isEmpty(token)) {
            if (ConfigUtils.isDefault(token)) {
                map.put(TOKEN_KEY, UUID.randomUUID().toString());
            } else {
                map.put(TOKEN_KEY, token);
            }
        }
        // export service,开始暴露服务
        String host = this.findConfigedHosts(protocolConfig, registryURLs, map);//获取当前配置的host地址
        //获取访问的端口
        Integer port = this.findConfigedPorts(protocolConfig, name, map);
        //创建可访问的url
        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);
        }

        String scope = url.getParameter(SCOPE_KEY);
        // don't export when none is configured
        if (!SCOPE_NONE.equalsIgnoreCase(scope)) {

            // export to local if the config is not remote (export to remote only when config is remote)
            if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
                exportLocal(url);
            }
            // export to remote if the config is not local (export to local only when config is local)
            if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
                if (!isOnlyInJvm() && logger.isInfoEnabled()) {
                    logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                }
                if (CollectionUtils.isNotEmpty(registryURLs)) {
                    for (URL registryURL : registryURLs) {
                        //if protocol is only injvm ,not register
                        if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
                            continue;
                        }
                        url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));
                        URL monitorUrl = loadMonitor(registryURL);
                        if (monitorUrl != null) {
                            url = url.addParameterAndEncoded(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(PROXY_KEY);
                        if (StringUtils.isNotEmpty(proxy)) {
                            registryURL = registryURL.addParameter(PROXY_KEY, proxy);
                        }

                        Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
                        DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                        Exporter<?> exporter = protocol.export(wrapperInvoker);
                        exporters.add(exporter);
                    }
                } else {
                    Invoker<?> invoker = PROXY_FACTORY.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);
                }
            }
        }
        this.urls.add(url);
    }

通过这个方法发现其实dubbo就是通过发布一个基于dubbo协议的url,通过一个可访问的url的方式调用当前的对应接口实现类的方法达到效果

7.总结

1.dubbo需要连接zookeeper注册中心

2.dubbo创建当前发布的接口中的方法名来创建自己的基于dubbo协议的url

3.dubbo开始发布这个可以访问的url,即通过这个url就能访问对应的接口实现类的的方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值