zoukankan      html  css  js  c++  java
  • Dubbo源码手记-服务注册

    今天来简单做一下Dubbo服务注册部分源码学习手记。

    一、Dubbo配置解析

    目前Dubbo最多的用法就是跟Spring集成,既然跟Spring集成,那么,Dubbo对象的实例化都将交由Spring统一处理。而Dubbo配置,对于Spring来说其实就是自定标签。这里Dubbo自定义标签解析类,在Dubbo配置模块(dubbo-configdubbo-config-springsrcmain esourcesMETA-INF/spring.handlers)进行了声明:

    http://dubbo.apache.org/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
    http://code.alibabatech.com/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
     1 public class DubboNamespaceHandler extends NamespaceHandlerSupport {
     2     static {
     3         Version.checkDuplicate(DubboNamespaceHandler.class);
     4     }
     5     @Override
     6     public void init() {
     7         registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
     8         registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
     9         registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
    10         registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class, true));
    11         registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
    12         registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
    13         registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
    14         registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
    15         registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
    16         registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
    17         registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
    18     }
    19 }

    对应一下Dubbo服务提供方的配置文件:

    <dubbo:application name="demo-provider"/>
    <dubbo:registry address="zookeeper://127.0.0.1:2181" />
    <dubbo:protocol name="dubbo"/>
    <bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl"/>
    <dubbo:service interface="org.apache.dubbo.demo.DemoService" ref="demoService"/>

    就是将这些标签解析成ApplicationConfig...ServiceBean等等对象。而服务注册部分最主要的桥梁就在于ServiceBean初始化解析完毕的时候。有Spring容器调用ServiceBean.afterPropertiesSet方法:

    1 public void afterPropertiesSet() throws Exception {
    2     // 此处省略配置检查
    3     export();
    4 }    

    二、配置检查与缺省填充

    进入export方法中,首先会对ServiceBean进行发布前的配置检查与填充。

    1 public synchronized void export() {
    2         checkAndUpdateSubConfigs(); // 加载并更新配置信息到Bean对象中,并检查
    3 }

    这里会有一系列的配置校验与配置填充的逻辑:

     1 public void checkAndUpdateSubConfigs() {
     2     checkDefault();
     3     if (provider != null) {
     4         inheritIfAbsentFromProvider();
     5     }
     6     if (module != null) {
     7         inheritIfAbsentFromModule();
     8     }
     9     if (application != null) {
    10         inheritIfAbsentFromApplication();
    11     }
    12     checkApplication();
    13     checkRegistry();
    14     checkProtocol();
    15     this.refresh();
    16     checkMetadataReport();
    17     checkRegistryDataConfig();
    18     try {
    19         interfaceClass = Class.forName(interfaceName, true, Thread.currentThread().getContextClassLoader());
    20     } catch (ClassNotFoundException e) {
    21         throw new IllegalStateException(e.getMessage(), e);
    22     }
    23     checkInterfaceAndMethods(interfaceClass, methods);
    24     checkRef();
    25     checkStubAndLocal(interfaceClass);
    26     checkMock(interfaceClass);
    27 }

    这里只列了部分配置检查与填充。总体配置的区分是否配置中心优先,优先级为:

     1 public void refresh() {
     2     // getPrefix为对应配置类的前缀,ProviderConfig->Provider, ServiceBean->Service,
     3     // getId为beanId,
     4     CompositeConfiguration compositeConfiguration = Environment.getInstance().getConfiguration(getPrefix(), getId());
     5     InmemoryConfiguration config = new InmemoryConfiguration(getPrefix(), getId());
     6     config.addProperties(getMetaData()); // Bean对象的初始值
     7     if (Environment.getInstance().isConfigCenterFirst()) {
     8         // -D系统属性 > 配置中心应用配置 > 配置中心全局配置 > Bean对象的初始值 > 属性文件中的信息
     9         compositeConfiguration.addConfiguration(3, config);
    10     } else {
    11         // -D > Bean对象的初始值 > 配置中心应用配置 > 配置中心全局配置 > 属性文件中的信息
    12         compositeConfiguration.addConfiguration(1, config);
    13     }
    14 }

    三、获取和构建注册中心统一资源定位器

    1 private void doExportUrls() {
    2     // 获取注册中心统一资源列表,如果注册中心没有初始化,则先初始化
    3     List<URL> registryURLs = loadRegistries(true);
    4     // 将目标服务所有协议模式注册到每一个注册中心上
    5     for (ProtocolConfig protocolConfig : protocols) {
    6         doExportUrlsFor1Protocol(protocolConfig, registryURLs);
    7     }
    8 }

    四、服务启动与服务注册

    在doExportUrlsForProtocal方法里边其实就做了以下几件事:

     1 private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
     2      Map map = builderUrl();
     3      // 代表一个服务
     4      URL url = new URL(name, host, port, (StringUtils.isEmpty(contextPath) ? "" : contextPath + "/") + path, map);
     5      // 通过代理工厂将ref对象转化成invoker对象
     6      Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
     7      //代理invoker对象
     8      DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
     9      // 使用配置协议启动、暴露服务
    10      Exporter<?> exporter = protocol.export(wrapperInvoker);
    11      //一个服务可能有多个提供者,保存在一起
    12      exporters.add(exporter);
    13 }

    这里,我们着重看第10行服务暴露部分,这里调用export方法,如果是Dubbo协议的话,分别列举一下DubboProtocal、HttpProtocol的export:

     1 public class DubboProtocol extends AbstractProtocol {
     2     public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
     3         URL url = invoker.getUrl();
     4         //忽略若干代码
     5         //打开服务
     6         openServer(url);
     7         optimizeSerialization(url);
     8         return exporter;
     9     }
    10 }
    1 public class HttpProtocol extends AbstractProxyProtocol {
    2     protected <T> Runnable doExport(final T impl, Class<T> type, URL url) throws RpcException {
    3         String addr = getAddr(url);
    4         HttpServer server = serverMap.get(addr);
    5         server = httpBinder.bind(url, new InternalHandler());
    6     }
    7 }

    其实这块就是,根据不同协议分别做了服务启动,如果是Dubbo协议则启动NettyServer,如果是Http协议则启动一个Tomcat。

    那么现在服务启动了,但是在哪里注册了呢?其实我们要关注这个protocol对象的构建:

     1 /**
     2  * The {@link Protocol} implementation with adaptive functionality,it will be different in different scenarios.
     3  * A particular {@link Protocol} implementation is determined by the protocol attribute in the {@link URL}.
     4  * For example:
     5  *
     6  * <li>when the url is registry://224.5.6.7:1234/org.apache.dubbo.registry.RegistryService?application=dubbo-sample,
     7  * then the protocol is <b>RegistryProtocol</b></li>
     8  *
     9  * <li>when the url is dubbo://224.5.6.7:1234/org.apache.dubbo.config.api.DemoService?application=dubbo-sample, then
    10  * the protocol is <b>DubboProtocol</b></li>
    11  * <p>
    12  * Actually,when the {@link ExtensionLoader} init the {@link Protocol} instants,it will automatically wraps two
    13  * layers, and eventually will get a <b>ProtocolFilterWrapper</b> or <b>ProtocolListenerWrapper</b>
    14  */
    15 private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

    这段源码的意思是,在获取Protocol实例的时候,Dubbo框架自动给包装了两个切面,其实服务注册就是在这个切面里边完成的:

     1 public class ProtocolListenerWrapper implements Protocol {
     2     private final Protocol protocol;
     3     @Override
     4     public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
     5         //如果是registerProtocol,则调用RegisterProtocol.export方法
     6         if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
     7             return protocol.export(invoker);
     8         }
     9         return new ListenerExporterWrapper<T>(protocol.export(invoker),
    10                 Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
    11                         .getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
    12     }
    13 }

    再看看RegisterProtocol的export:

    1 public class RegistryProtocol implements Protocol {
    2     @Override
    3     public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
    4         register(registryUrl, registeredProviderUrl); // 服务注册
    5     }
    6 }

    如果注册中心是ZK的话其实就是给ZK写数据:

     1 @Override
     2 public void register(URL url) {
     3          //忽略很多代码
     4         doRegister(url);
     5         //忽略很多代码
     6 }
     7 protected void doRegister(URL url) {
     8     try {
     9         zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true));
    10     } catch (Throwable e) {
    11         throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
    12     }
    13 }
    
    

    相关文章:https://www.jianshu.com/p/7f3871492c71

  • 相关阅读:
    SQL Server 2008R2 附件数据库问题记录
    关于.NET C#调用Sqlite的总结二
    关于.NET C#调用Sqlite的总结一
    MS Server中varchar与nvarchar的区别
    Intellij IDEA中使用Debug调试
    使用idea关联mysql时报错Server returns invalid timezone. Go to 'Advanced' tab and set 'serverTimezon'
    学Redis这篇就够了
    java的动态代理机制详解
    mybatis-sql执行流程源码分析
    mybatis
  • 原文地址:https://www.cnblogs.com/UYGHYTYH/p/13034872.html
Copyright © 2011-2022 走看看