• 第二章 dubbo源码解析目录


    6.1 如何在spring中自定义xml标签

    dubbo自定义了很多xml标签,例如<dubbo:application>,那么这些自定义标签是怎么与spring结合起来的呢?我们先看一个简单的例子。

    一 编写模型类

     1 package com.hulk.testdubbo.model;
     2 
     3 public class Hero {
     4     private  String name;
     5     private  int    age;
     6 
     7     public String getName() {
     8         return name;
     9     }
    10 
    11     public void setName(String name) {
    12         this.name = name;
    13     }
    14 
    15     public int getAge() {
    16         return age;
    17     }
    18 
    19     public void setAge(int age) {
    20         this.age = age;
    21     }
    22 }

    二 定义xsd文件

     1 <xsd:schema
     2         xmlns="http://hulk.com/schema"
     3         xmlns:xsd="http://www.w3.org/2001/XMLSchema"
     4         targetNamespace="http://hulk.com/schema">
     5     <xsd:complexType name="elementname1complexType">
     6         <xsd:attribute name="name" type="xsd:string">
     7             <xsd:annotation>
     8                 <xsd:documentation><![CDATA[ The elementname1 name. ]]></xsd:documentation>
     9             </xsd:annotation>
    10         </xsd:attribute>
    11         <xsd:attribute name="age" type="xsd:int">
    12             <xsd:annotation>
    13                 <xsd:documentation><![CDATA[ The elementname1 age. ]]></xsd:documentation>
    14             </xsd:annotation>
    15         </xsd:attribute>
    16     </xsd:complexType>
    17 
    18     <xsd:element name="elementname1" type="elementname1complexType">
    19         <xsd:annotation>
    20             <xsd:documentation><![CDATA[ elementname1的文档 ]]></xsd:documentation>
    21         </xsd:annotation>
    22     </xsd:element>
    23 </xsd:schema>

    说明:

    • 定义targetNamespace(目标命名空间),xmlns的值要与这个相同
    • xsd:element定义的就是将来会在xml文件中用到的元素,例如<dubbo:application>中的application
    • xsd:attribute定义的就是模型类中的属性,例如<dubbo:application name="xxx">中的name,并且可以指定属性类型,进而起到检测的作用(当我们定义的是int,如果在xml中的值是非int型的,直接会报错)。

    三 编写spring.schemas

    作用:该文件用来指定xsd文件的位置。

    http\://hulk.com/schema/hero.xsd=META-INF/hero.xsd

    注意:红色部分要与xsd文件中的targetNamespace相同。

    四 编写BeanDefinition解析器

    作用:主要用来解析自定义的xml标签。

     1 package com.hulk.testdubbo.schema;
     2 
     3 import org.springframework.beans.factory.config.BeanDefinition;
     4 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
     5 import org.springframework.beans.factory.support.RootBeanDefinition;
     6 import org.springframework.beans.factory.xml.BeanDefinitionParser;
     7 import org.springframework.beans.factory.xml.ParserContext;
     8 import org.w3c.dom.Element;
     9 
    10 public class HeroBeanDefinitionParser implements BeanDefinitionParser {
    11     private final Class<?> beanClass;
    12 
    13     public HeroBeanDefinitionParser(Class<?> beanClass) {
    14         this.beanClass = beanClass;
    15     }
    16 
    17     public BeanDefinition parse(Element element, ParserContext parserContext) {
    18         RootBeanDefinition beanDefinition = new RootBeanDefinition();
    19         beanDefinition.setBeanClass(beanClass);
    20         beanDefinition.setLazyInit(false);
    21         beanDefinition.getPropertyValues().add("name", element.getAttribute("name"));
    22         beanDefinition.getPropertyValues().add("age", element.getAttribute("age"));
    23         BeanDefinitionRegistry beanDefinitionRegistry = parserContext.getRegistry();
    24         beanDefinitionRegistry.registerBeanDefinition(beanClass.getName(),beanDefinition);//注册bean到BeanDefinitionRegistry中
    25         return beanDefinition;
    26     }
    27 }

    五 编写命名空间处理器

    作用:主要用来注册BeanDefinition解析器。

     1 package com.hulk.testdubbo.schema;
     2 
     3 import com.hulk.testdubbo.model.Hero;
     4 import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
     5 
     6 public class HeroNamespaceHandler extends NamespaceHandlerSupport {
     7     public void init() {
     8         registerBeanDefinitionParser("elementname1", new HeroBeanDefinitionParser(Hero.class));
     9     }
    10 }

    说明:通常为每一个xsd:element都要注册一个BeanDefinitionParser。

    六 编写spring.handlers文件

    作用:主要用于关联命名空间处理器和xsd中的targetNamespace。

    http\://hulk.com/schema=com.hulk.testdubbo.schema.HeroNamespaceHandler

    说明:key是xsd文件中的targetNamespace。

    七 测试 - 编写hero.xml

    1 <?xml version="1.0" encoding="UTF-8"?>
    2 <beans xmlns="http://www.springframework.org/schema/beans"
    3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4        xmlns:hero="http://hulk.com/schema"
    5        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    6        http://hulk.com/schema http://hulk.com/schema/hero.xsd">
    7     <hero:elementname1 name="xiaona" age="18"/>
    8 </beans>

    说明:

    • xmlns:hero的value是xsd文件中的targetNamespace。
    • xmlns:hero可以写成xmlns:xxx,此时<hero:elementname1/>就要写成<xxx:elementname1/>

    八 测试 - 编写测试主类

     1 package com.hulk.testdubbo.test;
     2 
     3 import com.hulk.testdubbo.model.Hero;
     4 import org.springframework.context.ApplicationContext;
     5 import org.springframework.context.support.ClassPathXmlApplicationContext;
     6 
     7 public class Main {
     8     public static void main(String[] args) {
     9         ApplicationContext applicationContext = new ClassPathXmlApplicationContext("hero.xml");
    10         Hero hero = (Hero) applicationContext.getBean(Hero.class.getName());
    11         System.out.println("name: " + hero.getName() + " age: " + hero.getAge());
    12     }
    13 }

    如何在spring中自定义xml标签的方法就结束了。在实际中,随着注解和javaconfg的盛行,xml的方式渐渐的会淡出舞台,但是spring的启动流程还是会的。来看一下上述代码涉及到的流程。

    • 使用ResourceLoader将配置文件xml装载为Resource对象;
    • 使用BeanDefinitionReader解析配置信息:将每一个<bean>解析为一个BeanDefinition对象,然后存储到BeanDefinitionRegistry中
      • 实际上是BeanDefinitionReader调用BeanDefinitionParser进行了解析操作,解析完成后注册到BeanDefinitionRegistry(代码看上边的HeroBeanDefinitionParser)

    6.2 dubbo在spring中自定义xml标签源码解析

    6.1 如何在spring中自定义xml标签中我们看到了在spring中自定义xml标签的方式。dubbo也是这样来实现的。

    一 META_INF/dubbo.xsd

    比较长,只列出<dubbo:applicaton>元素相关的。

     1 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
     2 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
     3             xmlns:beans="http://www.springframework.org/schema/beans"
     4             xmlns:tool="http://www.springframework.org/schema/tool"
     5             xmlns="http://code.alibabatech.com/schema/dubbo"
     6             targetNamespace="http://code.alibabatech.com/schema/dubbo">
     7 
     8     。。。
     9 
    10    <xsd:complexType name="applicationType">
    11         <xsd:attribute name="id" type="xsd:ID">
    12             <xsd:annotation>
    13                 <xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation>
    14             </xsd:annotation>
    15         </xsd:attribute>
    16         <xsd:attribute name="name" type="xsd:string" use="required">
    17             <xsd:annotation>
    18                 <xsd:documentation><![CDATA[ The application name. ]]></xsd:documentation>
    19             </xsd:annotation>
    20         </xsd:attribute>
    21         <xsd:attribute name="version" type="xsd:string">
    22             <xsd:annotation>
    23                 <xsd:documentation><![CDATA[ The application version. ]]></xsd:documentation>
    24             </xsd:annotation>
    25         </xsd:attribute>
    26         <xsd:attribute name="owner" type="xsd:string">
    27             <xsd:annotation>
    28                 <xsd:documentation><![CDATA[ The application owner name (email prefix). ]]></xsd:documentation>
    29             </xsd:annotation>
    30         </xsd:attribute>
    31         <xsd:attribute name="organization" type="xsd:string">
    32             <xsd:annotation>
    33                 <xsd:documentation><![CDATA[ The organization name. ]]></xsd:documentation>
    34             </xsd:annotation>
    35         </xsd:attribute>
    36         <xsd:attribute name="architecture" type="xsd:string">
    37             <xsd:annotation>
    38                 <xsd:documentation><![CDATA[ The architecture. ]]></xsd:documentation>
    39             </xsd:annotation>
    40         </xsd:attribute>
    41         <xsd:attribute name="environment" type="xsd:string">
    42             <xsd:annotation>
    43                 <xsd:documentation><![CDATA[ The application environment, eg: dev/test/run ]]></xsd:documentation>
    44             </xsd:annotation>
    45         </xsd:attribute>
    46         <xsd:attribute name="compiler" type="xsd:string">
    47             <xsd:annotation>
    48                 <xsd:documentation><![CDATA[ The java code compiler. ]]></xsd:documentation>
    49             </xsd:annotation>
    50         </xsd:attribute>
    51         <xsd:attribute name="logger" type="xsd:string">
    52             <xsd:annotation>
    53                 <xsd:documentation><![CDATA[ The application logger. ]]></xsd:documentation>
    54             </xsd:annotation>
    55         </xsd:attribute>
    56         <xsd:attribute name="registry" type="xsd:string" use="optional">
    57             <xsd:annotation>
    58                 <xsd:documentation><![CDATA[ The application registry. ]]></xsd:documentation>
    59             </xsd:annotation>
    60         </xsd:attribute>
    61         <xsd:attribute name="monitor" type="xsd:string" use="optional">
    62             <xsd:annotation>
    63                 <xsd:documentation><![CDATA[ The application monitor. ]]></xsd:documentation>
    64             </xsd:annotation>
    65         </xsd:attribute>
    66         <xsd:attribute name="default" type="xsd:string" use="optional">
    67             <xsd:annotation>
    68                 <xsd:documentation><![CDATA[ Is default. ]]></xsd:documentation>
    69             </xsd:annotation>
    70         </xsd:attribute>
    71     </xsd:complexType>
    72 
    73     。。。
    74 
    75     <xsd:element name="application" type="applicationType">
    76         <xsd:annotation>
    77             <xsd:documentation><![CDATA[ The application config ]]></xsd:documentation>
    78         </xsd:annotation>
    79     </xsd:element>
    80 
    81     。。。
    82 
    83 </xsd:schema>

    与上一节完全相似。

    二 META_INF/spring.schemas

    1 http\://code.alibabatech.com/schema/dubbo/dubbo.xsd=META-INF/dubbo.xsd

    与上一节完全相似。

    三 DubboBeanDefinitionParser

    代码较长,不再贴出来了,与上一节完全相似。

    四 DubboNamespaceHandler

     1 package com.alibaba.dubbo.config.spring.schema;
     2 
     3 import com.alibaba.dubbo.common.Version;
     4 import com.alibaba.dubbo.config.ApplicationConfig;
     5 import com.alibaba.dubbo.config.ConsumerConfig;
     6 import com.alibaba.dubbo.config.ModuleConfig;
     7 import com.alibaba.dubbo.config.MonitorConfig;
     8 import com.alibaba.dubbo.config.ProtocolConfig;
     9 import com.alibaba.dubbo.config.ProviderConfig;
    10 import com.alibaba.dubbo.config.RegistryConfig;
    11 import com.alibaba.dubbo.config.spring.AnnotationBean;
    12 import com.alibaba.dubbo.config.spring.ReferenceBean;
    13 import com.alibaba.dubbo.config.spring.ServiceBean;
    14 
    15 import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
    16 
    17 public class DubboNamespaceHandler extends NamespaceHandlerSupport {
    18 
    19     static {
    20         Version.checkDuplicate(DubboNamespaceHandler.class);
    21     }
    22 
    23     public void init() {
    24         registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
    25         registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
    26         registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
    27         registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
    28         registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
    29         registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
    30         registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
    31         registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
    32         registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
    33         registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
    34     }
    35 }

    功能与上一节完全相似。这里可以看出,dubbo自定义了10个xml元素(也可以从xsd中看出)。从上边也可以看出,<dubbo:service>会被解析成ServiceBean,该bean极其重要。 

    五 META_INF/spring.handlers

    1 http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler

    与上一节完全相似。

    7.1 服务暴露前的准备-ServiceBean的装配

    dubbo的服务暴露以第一章 第一个dubbo项目中的dubbo-demo-provider来讲述。

    列出dubbo-demo-provider的xml配置:

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     3        xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
     4        xmlns="http://www.springframework.org/schema/beans"
     5        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
     6        http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
     7 
     8     <!-- 提供方应用信息,用于计算依赖关系 -->
     9     <dubbo:application name="demo-provider"/>
    10 
    11     <!-- 使用zookeeper注册中心,并使用curator客户端 -->
    12     <dubbo:registry protocol="zookeeper" address="10.211.55.5:2181" client="curator"/>
    13 
    14     <!-- 用dubbo协议在20880端口暴露服务 -->
    15     <dubbo:protocol name="dubbo" port="20880"/>
    16 
    17     <!-- 和本地bean一样实现服务 -->
    18     <bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl"/>
    19     <!--<bean id="demo2Service" class="com.alibaba.dubbo.demo.provider.Demo2ServiceImpl"/>-->
    20 
    21     <!-- 声明需要暴露的服务接口 -->
    22     <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService"/>
    23     <!--<dubbo:service interface="com.alibaba.dubbo.demo.Demo2Service" ref="demo2Service"/>-->
    24 </beans>

    服务暴露是由com.alibaba.dubbo.config.spring.ServiceBean这个类来实现的,这个类是spring通过解析<dubbo:service>节点创建的单例Bean,每一个<dubbo:service>都会创建一个ServiceBean。先看一下ServiceBean的继承类图:

    看一下ServiceBean的源码:

      1 package com.alibaba.dubbo.config.spring;
      2 
      3 import com.alibaba.dubbo.config.ApplicationConfig;
      4 import com.alibaba.dubbo.config.ModuleConfig;
      5 import com.alibaba.dubbo.config.MonitorConfig;
      6 import com.alibaba.dubbo.config.ProtocolConfig;
      7 import com.alibaba.dubbo.config.ProviderConfig;
      8 import com.alibaba.dubbo.config.RegistryConfig;
      9 import com.alibaba.dubbo.config.ServiceConfig;
     10 import com.alibaba.dubbo.config.annotation.Service;
     11 import com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory;
     12 
     13 import org.springframework.beans.factory.BeanFactoryUtils;
     14 import org.springframework.beans.factory.BeanNameAware;
     15 import org.springframework.beans.factory.DisposableBean;
     16 import org.springframework.beans.factory.InitializingBean;
     17 import org.springframework.context.ApplicationContext;
     18 import org.springframework.context.ApplicationContextAware;
     19 import org.springframework.context.ApplicationEvent;
     20 import org.springframework.context.ApplicationListener;
     21 import org.springframework.context.event.ContextRefreshedEvent;
     22 import org.springframework.context.support.AbstractApplicationContext;
     23 
     24 import java.lang.reflect.Method;
     25 import java.util.ArrayList;
     26 import java.util.List;
     27 import java.util.Map;
     28 
     29 /**
     30  * ServiceFactoryBean
     31  */
     32 public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener, BeanNameAware {
     33     private static final long serialVersionUID = 213195494150089726L;
     34 
     35     private static transient ApplicationContext SPRING_CONTEXT;
     36 
     37     private transient ApplicationContext applicationContext;
     38 
     39     private transient String beanName;
     40 
     41     private transient boolean supportedApplicationListener;
     42 
     43     public ServiceBean() {
     44         super();
     45     }
     46 
     47     public ServiceBean(Service service) {
     48         super(service);
     49     }
     50 
     51     public static ApplicationContext getSpringContext() {
     52         return SPRING_CONTEXT;
     53     }
     54 
     55     /**
     56      * ApplicationContextAware接口的方法
     57      * Set the ApplicationContext that this object runs in.
     58      * Invoked after population of normal bean properties but before an init callback such
     59      * as {@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}
     60      * or a custom init-method.
     61      *
     62      * 流程:
     63      * 1 将applicationContext设置到SpringExtensionFactory中,用于后续从SpringExtensionFactory中获取Bean
     64      * 2 获取方法addApplicationListener(ApplicationListener<?> listener),之后将当前类(因为当前类监听了ContextRefreshedEvent事件)加入spring的监听器列表
     65      */
     66     @Override
     67     public void setApplicationContext(ApplicationContext applicationContext) {
     68         this.applicationContext = applicationContext;
     69         SpringExtensionFactory.addApplicationContext(applicationContext);
     70         if (applicationContext != null) {
     71             SPRING_CONTEXT = applicationContext;
     72             try {
     73                 /**  */
     74                 Method method = applicationContext.getClass().getMethod("addApplicationListener", new Class<?>[]{ApplicationListener.class}); // 兼容Spring2.0.1
     75                 method.invoke(applicationContext, new Object[]{this});
     76                 supportedApplicationListener = true;
     77             } catch (Throwable t) {
     78                 if (applicationContext instanceof AbstractApplicationContext) {
     79                     try {
     80                         Method method = AbstractApplicationContext.class.getDeclaredMethod("addListener", new Class<?>[]{ApplicationListener.class}); // 兼容Spring2.0.1
     81                         if (!method.isAccessible()) {
     82                             method.setAccessible(true);
     83                         }
     84                         method.invoke(applicationContext, new Object[]{this});
     85                         supportedApplicationListener = true;
     86                     } catch (Throwable t2) {
     87                     }
     88                 }
     89             }
     90         }
     91     }
     92 
     93     /**
     94      * BeanNameAware接口的方法
     95      * Set the name of the bean in the bean factory that created this bean.
     96      * Invoked after population of normal bean properties but before an
     97      * init callback such as {@link InitializingBean#afterPropertiesSet()}
     98      * or a custom init-method.
     99      */
    100     @Override
    101     public void setBeanName(String name) {
    102         this.beanName = name;
    103     }
    104 
    105     /**
    106      * ApplicationListener接口的方法
    107      * delay没有设置或者是-1 && 服务没有暴露 && 服务没有反注册,则进行服务暴露
    108      */
    109     @Override
    110     public void onApplicationEvent(ApplicationEvent event) {
    111         if (ContextRefreshedEvent.class.getName().equals(event.getClass().getName())) {
    112             if (isDelay() && !isExported() && !isUnexported()) {
    113                 if (logger.isInfoEnabled()) {
    114                     logger.info("The service ready on spring started. service: " + getInterface());
    115                 }
    116                 export();
    117             }
    118         }
    119     }
    120 
    121     private boolean isDelay() {
    122         Integer delay = getDelay();
    123         ProviderConfig provider = getProvider();
    124         if (delay == null && provider != null) {
    125             delay = provider.getDelay();
    126         }
    127         return supportedApplicationListener && (delay == null || delay.intValue() == -1);
    128     }
    129 
    130     /**
    131      * InitializingBean接口的方法:
    132      * This method allows the bean instance to perform initialization only
    133      * possible when all bean properties have been set
    134      *
    135      * 流程:
    136      * 1 检查ServiceBean的ProviderConfig provider,如果为空,从applicationContext获取ProviderConfig类型的bean(这里查找的过程其实就是看有没有配置<dubbo:provider>),如果获取到了,进行设置
    137      * 2 后续会参照1分别进行
    138      *   -- ApplicationConfig application
    139      *   -- ModuleConfig module
    140      *   -- List<RegistryConfig> registries
    141      *   -- MonitorConfig monitor
    142      *   -- List<ProtocolConfig> protocols
    143      *   -- String path:服务名称
    144      * 3 判断延迟的事件是否大于0,如果是,执行export(),进行服务暴露,如果不是,结束(这种情况下服务暴露,会发生在发布上下文刷新事件的时候)
    145      */
    146     @Override
    147     @SuppressWarnings({"unchecked", "deprecation"})
    148     public void afterPropertiesSet() throws Exception {
    149         if (getProvider() == null) {
    150             Map<String, ProviderConfig> providerConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProviderConfig.class, false, false);
    151             if (providerConfigMap != null && providerConfigMap.size() > 0) {
    152                 Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class, false, false);
    153                 if ((protocolConfigMap == null || protocolConfigMap.size() == 0)
    154                         && providerConfigMap.size() > 1) { // 兼容旧版本
    155                     List<ProviderConfig> providerConfigs = new ArrayList<ProviderConfig>();
    156                     for (ProviderConfig config : providerConfigMap.values()) {
    157                         if (config.isDefault() != null && config.isDefault().booleanValue()) {
    158                             providerConfigs.add(config);
    159                         }
    160                     }
    161                     if (providerConfigs.size() > 0) {
    162                         setProviders(providerConfigs);
    163                     }
    164                 } else {
    165                     ProviderConfig providerConfig = null;
    166                     for (ProviderConfig config : providerConfigMap.values()) {
    167                         if (config.isDefault() == null || config.isDefault().booleanValue()) {
    168                             if (providerConfig != null) {
    169                                 throw new IllegalStateException("Duplicate provider configs: " + providerConfig + " and " + config);
    170                             }
    171                             providerConfig = config;
    172                         }
    173                     }
    174                     if (providerConfig != null) {
    175                         setProvider(providerConfig);
    176                     }
    177                 }
    178             }
    179         }
    180         if (getApplication() == null
    181                 && (getProvider() == null || getProvider().getApplication() == null)) {
    182             Map<String, ApplicationConfig> applicationConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class, false, false);
    183             if (applicationConfigMap != null && applicationConfigMap.size() > 0) {
    184                 ApplicationConfig applicationConfig = null;
    185                 for (ApplicationConfig config : applicationConfigMap.values()) {
    186                     if (config.isDefault() == null || config.isDefault().booleanValue()) {
    187                         if (applicationConfig != null) {
    188                             throw new IllegalStateException("Duplicate application configs: " + applicationConfig + " and " + config);
    189                         }
    190                         applicationConfig = config;
    191                     }
    192                 }
    193                 if (applicationConfig != null) {
    194                     setApplication(applicationConfig);
    195                 }
    196             }
    197         }
    198         if (getModule() == null
    199                 && (getProvider() == null || getProvider().getModule() == null)) {
    200             Map<String, ModuleConfig> moduleConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ModuleConfig.class, false, false);
    201             if (moduleConfigMap != null && moduleConfigMap.size() > 0) {
    202                 ModuleConfig moduleConfig = null;
    203                 for (ModuleConfig config : moduleConfigMap.values()) {
    204                     if (config.isDefault() == null || config.isDefault().booleanValue()) {
    205                         if (moduleConfig != null) {
    206                             throw new IllegalStateException("Duplicate module configs: " + moduleConfig + " and " + config);
    207                         }
    208                         moduleConfig = config;
    209                     }
    210                 }
    211                 if (moduleConfig != null) {
    212                     setModule(moduleConfig);
    213                 }
    214             }
    215         }
    216         if ((getRegistries() == null || getRegistries().size() == 0)
    217                 && (getProvider() == null || getProvider().getRegistries() == null || getProvider().getRegistries().size() == 0)
    218                 && (getApplication() == null || getApplication().getRegistries() == null || getApplication().getRegistries().size() == 0)) {
    219             Map<String, RegistryConfig> registryConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class, false, false);
    220             if (registryConfigMap != null && registryConfigMap.size() > 0) {
    221                 List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();
    222                 for (RegistryConfig config : registryConfigMap.values()) {
    223                     if (config.isDefault() == null || config.isDefault().booleanValue()) {
    224                         registryConfigs.add(config);
    225                     }
    226                 }
    227                 if (registryConfigs != null && registryConfigs.size() > 0) {
    228                     super.setRegistries(registryConfigs);
    229                 }
    230             }
    231         }
    232         if (getMonitor() == null
    233                 && (getProvider() == null || getProvider().getMonitor() == null)
    234                 && (getApplication() == null || getApplication().getMonitor() == null)) {
    235             Map<String, MonitorConfig> monitorConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class, false, false);
    236             if (monitorConfigMap != null && monitorConfigMap.size() > 0) {
    237                 MonitorConfig monitorConfig = null;
    238                 for (MonitorConfig config : monitorConfigMap.values()) {
    239                     if (config.isDefault() == null || config.isDefault().booleanValue()) {
    240                         if (monitorConfig != null) {
    241                             throw new IllegalStateException("Duplicate monitor configs: " + monitorConfig + " and " + config);
    242                         }
    243                         monitorConfig = config;
    244                     }
    245                 }
    246                 if (monitorConfig != null) {
    247                     setMonitor(monitorConfig);
    248                 }
    249             }
    250         }
    251         if ((getProtocols() == null || getProtocols().size() == 0)
    252                 && (getProvider() == null || getProvider().getProtocols() == null || getProvider().getProtocols().size() == 0)) {
    253             Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class, false, false);
    254             if (protocolConfigMap != null && protocolConfigMap.size() > 0) {
    255                 List<ProtocolConfig> protocolConfigs = new ArrayList<ProtocolConfig>();
    256                 for (ProtocolConfig config : protocolConfigMap.values()) {
    257                     if (config.isDefault() == null || config.isDefault().booleanValue()) {
    258                         protocolConfigs.add(config);
    259                     }
    260                 }
    261                 if (protocolConfigs != null && protocolConfigs.size() > 0) {
    262                     super.setProtocols(protocolConfigs);
    263                 }
    264             }
    265         }
    266         if (getPath() == null || getPath().length() == 0) {
    267             if (beanName != null && beanName.length() > 0
    268                     && getInterface() != null && getInterface().length() > 0
    269                     && beanName.startsWith(getInterface())) {
    270                 setPath(beanName);
    271             }
    272         }
    273         if (!isDelay()) {//设置的延迟时间大于0
    274             export();
    275         }
    276     }
    277 
    278     /**
    279      * DisposableBean接口的方法
    280      */
    281     @Override
    282     public void destroy() throws Exception {
    283         unexport();
    284     }
    285 }

    这里最重要的两个方法:afterPropertiesSet()和onApplicationEvent(ApplicationEvent event)。

    一 设置属性与服务暴露

    当所有的Bean的属性被设置好之后,执行afterPropertiesSet()。该方法的流程:

    1 设置属性

    检查ServiceBean的某个属性(这里的属性包含如下6个)是否为空,如果为空,从applicationContext获取相应类型的bean,如果获取到了,则进行相应的设置。

    • ProviderConfig provider:其实就是看有没有配置<dubbo:provider>
    • ApplicationConfig application:其实就是看有没有配置<dubbo:application>
    • ModuleConfig module:其实就是看有没有配置<dubbo:module>
    • List<RegistryConfig> registries:其实就是看有没有配置<dubbo:registry>
    • MonitorConfig monitor:其实就是看有没有配置<dubbo:monitor>
    • List<ProtocolConfig> protocols:其实就是看有没有配置<dubbo:protocol>
    • String path:服务名称

    2 是否暴露服务

    之后判断延迟的时间是否大于0,如果是,执行export(),进行服务暴露,如果不是,结束(这种情况下服务暴露会发生在容器发布上下文刷新事件的时候)。在这里,我们并没有指定delay,所以delay==null,服务暴露会发生在容器发布上下文刷新事件的时候。

    当afterPropertiesSet()结束之后,来看一下此时的ServiceBean实例,实例的私有属性如下:(没有值的暂时不说)

     1 id = com.alibaba.dubbo.demo.DemoService
     2 applicationContext = ClassPathXmlApplicationContext实例
     3 beanName = com.alibaba.dubbo.demo.DemoService
     4 interfaceName = com.alibaba.dubbo.demo.DemoService
     5 supportedApplicationListener = true
     6 ref = DemoServiceImpl实例
     7 path = com.alibaba.dubbo.demo.DemoService
     8 
     9 application:
    10 -- id = demo-provider
    11 -- name = demo-provider
    12 
    13 registries = [
    14     RegistryConfig:
    15     -- id = com.alibaba.dubbo.config.RegistryConfig
    16     -- protocol = zookeeper
    17     -- address = 10.211.55.5:2181
    18     -- client = curator
    19 ]
    20 
    21 protocols = [ 
    22     ProtocolConfig: 
    23     -- id = dubbo
    24     -- name = dubbo
    25     -- port = 20880    
    26 ]

    实际上在创建ServiceBean实例的时候,也会初始化其父类ServiceConfig的静态属性:

    1     private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
    2     private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

    其中protocol的实例是:Protocol$Adaptive实例,Protocol$Adaptive类的代码在5.2 dubbo-compiler源码解析已经列出。

    下边来看一下第二句代码的源码。首先,看一下ProxyFactory的定义:

     1 package com.alibaba.dubbo.rpc;
     2 
     3 import com.alibaba.dubbo.common.Constants;
     4 import com.alibaba.dubbo.common.URL;
     5 import com.alibaba.dubbo.common.extension.Adaptive;
     6 import com.alibaba.dubbo.common.extension.SPI;
     7 
     8 /**
     9  * ProxyFactory. (API/SPI, Singleton, ThreadSafe)
    10  */
    11 @SPI("javassist")
    12 public interface ProxyFactory {
    13     /**
    14      * create proxy.
    15      */
    16     @Adaptive({Constants.PROXY_KEY})
    17     <T> T getProxy(Invoker<T> invoker) throws RpcException;
    18 
    19     /**
    20      * create invoker.
    21      */
    22     @Adaptive({Constants.PROXY_KEY})
    23     <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
    24 }

    ExtensionLoader.getExtensionLoader(ProxyFactory.class)的实现结果还是:

    ExtensionLoader<com.alibaba.dubbo.rpc.ProxyFactory> loader,最终的loader包含如下属性:

    • Class<?> type = interface com.alibaba.dubbo.rpc.ProxyFactory
    • ExtensionFactory objectFactory = AdaptiveExtensionFactory(适配类)
      • factories = [SpringExtensionFactory实例, SpiExtensionFactory实例]

    之后,执行getAdaptiveExtension()。

    来看一下:META-INF/dubbo/internal/com.alibaba.dubbo.rpc.ProxyFactory的内容:

    1 stub=com.alibaba.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper
    2 jdk=com.alibaba.dubbo.rpc.proxy.jdk.JdkProxyFactory
    3 javassist=com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory

    从ProxyFactory的@SPI("javassist"),默认选用的实现是com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory。com.alibaba.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper是一个wrapper类,但是wrapper类只在getExtension("xxx")中会实现aop,而在getAdaptiveExtension()不会进行aop包裹。

    这里的三个实现类没有一个类上带有@Adaptive注解,所以会动态创建类。动态生成的类ProxyFactory$Adaptive代码如下:

     1 package com.alibaba.dubbo.rpc;
     2 import com.alibaba.dubbo.common.extension.ExtensionLoader;
     3 
     4 public class ProxyFactory$Adaptive implements com.alibaba.dubbo.rpc.ProxyFactory {
     5     public com.alibaba.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, com.alibaba.dubbo.common.URL arg2) throws com.alibaba.dubbo.rpc.RpcException {
     6         if (arg2 == null) 
     7             throw new IllegalArgumentException("url == null");
     8         com.alibaba.dubbo.common.URL url = arg2;
     9         String extName = url.getParameter("proxy", "javassist");
    10         if(extName == null) 
    11             throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
    12         com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
    13         return extension.getInvoker(arg0, arg1, arg2);
    14     }
    15     
    16     public java.lang.Object getProxy(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
    17         if (arg0 == null) 
    18             throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
    19         if (arg0.getUrl() == null) 
    20             throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
    21         String extName = url.getParameter("proxy", "javassist");
    22         if(extName == null) 
    23             throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
    24         com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
    25         return extension.getProxy(arg0);
    26     }
    27 }

    所以ServiceConfig中的静态属性proxyFactory为ProxyFactory$Adaptive实例。 

    至此,一个ServiceBean实例完成了。

    二 在上下文刷新时进行初始化

     1     /**
     2      * ApplicationListener接口的方法
     3      * delay没有设置或者是-1 && 服务没有暴露 && 服务没有反注册,则进行服务暴露
     4      */
     5     @Override
     6     public void onApplicationEvent(ApplicationEvent event) {
     7         if (ContextRefreshedEvent.class.getName().equals(event.getClass().getName())) {
     8             if (isDelay() && !isExported() && !isUnexported()) {
     9                 if (logger.isInfoEnabled()) {
    10                     logger.info("The service ready on spring started. service: " + getInterface());
    11                 }
    12                 export();
    13             }
    14         }
    15     }

    一切准备好之后,就在这里开始进行服务暴露!export()!!!

    7.2 服务本地暴露

    为了安全:服务启动的ip全部使用10.10.10.10

    服务暴露的流程其实就是下边这样(图片来自:http://www.iteye.com/topic/1123039

    简单看一下服务暴露的伪代码:

     1 /**
     2  * dubbo 服务暴露伪代码
     3  */
     4 public class DubboProviderSimpleCode {
     5     private final List<Exporter<?>> exporters = new ArrayList<Exporter<?>>();
     6     /**
     7      * 获取注册中心url列表
     8      * [ registry://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&client=curator&dubbo=2.0.0&pid=2956&registry=zookeeper&timestamp=1507004600231 ]
     9      */
    10     List<URL> registryURLs = loadRegistries();//获取注册中心url列表
    11     for (ProtocolConfig protocolConfig : protocols) {
    12         /**
    13          * 创建协议url
    14          * dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=2956&side=provider&timestamp=1507004625957
    15          */
    16         URL url = new URL(name, host, port, path, map);
    17         /**
    18          * 本地暴露
    19          */
    20         if (<dubbo:service scope!=remote>) {
    21             /**
    22              * 构造injvm协议的url
    23              * injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=2956&side=provider&timestamp=1507004625957
    24              */
    25             URL local = URL.valueOf(url.toFullString())
    26                     .setProtocol(Constants.LOCAL_PROTOCOL)
    27                     .setHost(NetUtils.LOCALHOST)
    28                     .setPort(0);
    29             Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, local);
    30             Exporter<?> exporter = protocol.export(invoker);
    31             exporters.add(exporter);
    32         }
    33         /**
    34          * 远程暴露
    35          */
    36         if (<dubbo:service scope!=local>) {
    37             for (URL registryURL : registryURLs) {
    38                 Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
    39                 Exporter<?> exporter = protocol.export(invoker);
    40                 exporters.add(exporter);
    41             }
    42         }
    43     }
    44 }

    本地暴露:

    • 通过JavassistProxyFactory(默认)将具体的实现类包装成AbstractProxyInvoker实例
    • InjvmProtocol将上述的AbstractProxyInvoker实例转换成Exporter

    远程暴露:

    • 通过JavassistProxyFactory(默认)将具体的实现类包装成AbstractProxyInvoker实例
    • DubboProtocol将上述的AbstractProxyInvoker实例转换成Exporter

     本节来看本地暴露。首先给出整个本地服务暴露的调用链。

     1 ServiceBean.onApplicationEvent(ApplicationEvent event)
     2 -->ServiceConfig.export()
     3    -->doExport()
     4       -->doExportUrls()
     5          -->loadRegistries(boolean provider) //
     6          -->doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs)
     7             -->Wrapper getWrapper(Class<?> c)
     8                -->makeWrapper(Class<?> c)
     9             -->new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map)
    10             
    11             <!--   本地暴露   -->
    12             -->exportLocal(url)
    13                -->构造injvm协议的url:injvmUrl
    14                <!--  1 将ref包装成Invoker   -->
    15                -->ProxyFactory$Adaptive.getInvoker(ref实例, interfaceClass, injvmUrl)
    16                   -->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension("javassist")
    17                   -->StubProxyFactoryWrapper.getInvoker(T proxy, Class<T> type, URL url)
    18                      -->JavassistProxyFactory.getInvoker(T proxy, Class<T> type, URL url)
    19                         -->Wrapper getWrapper(Class<?> c)
    20                            -->makeWrapper(Class<?> c)
    21                         -->new AbstractProxyInvoker<T>(ref实例, interfaceClass, injvmUrl)
    22                <!--  2 将Invoker暴露为Exporter   -->
    23                -->Protocol$Adaptive.export(Invoker<T> invoker)
    24                   -->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension("injvm")
    25                   -->ProtocolListenerWrapper.export(Invoker<T> invoker)
    26                      -->ProtocolFilterWrapper.export(Invoker<T> invoker)
    27                         -->buildInvokerChain(final Invoker<T> invoker, key:"service.filter", group:"provider")
    28                            -->ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(injvmUrl, "service.filter", "provider")
    29                         -->InjvmProtocol.export(Invoker<T> invoker)
    30                            -->new InjvmExporter(Invoker<T> invoker, key:"com.alibaba.dubbo.demo.DemoService", Map<String, Exporter<?>> exporterMap)
    31                               -->InjvmExporter.exporterMap({"com.alibaba.dubbo.demo.DemoService" -> "injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3207&side=provider&timestamp=1507009133930"})
    32                   -->new ListenerExporterWrapper(Exporter<T> exporter, List<ExporterListener> listeners) : 使用listener包装invoker
    33                <!--  3 将Invoker暴露为Exporter   -->
    34                -->ServiceConfig#exporters.add(exporter) 

    本地暴露的代码如下:

     1     /**
     2      * 本地暴露
     3      * 1 根据传进来的url(例如dubbo协议的url)构造injvm协议的url
     4      * injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=2999&side=provider&timestamp=1507005507343
     5      *
     6      * 2 将ref包装为AbstractProxyInvoker实例
     7      * 3 将AbstractProxyInvoker实例转换为InjvmExporter
     8      *
     9      * @param url dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=2999&side=provider&timestamp=1507005507343
    10      */
    11     @SuppressWarnings({"unchecked", "rawtypes"})
    12     private void exportLocal(URL url) {
    13         if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
    14             URL local = URL.valueOf(url.toFullString()).setProtocol(Constants.LOCAL_PROTOCOL).setHost(NetUtils.LOCALHOST).setPort(0);
    15             Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, local);
    16             Exporter<?> exporter = protocol.export(invoker);
    17             exporters.add(exporter);
    18             logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");
    19         }
    20     }

    为了清晰,这里对exportLocal(URL url)做了稍稍的改动。整体流程如下:

    1 首先将dubbo协议的url,改成了injvm协议的url:local;

    2 将具体服务类ref通过proxyFactory包装成AbstractProxyInvoker实例;

    3 将AbstractProxyInvoker实例转化为Exporter实例;

    4 最后将生成的Exporter实例存放在ServiceConfig的List<Exporter> exporters中。

    一 具体服务包装成AbstractProxyInvoker实例

    Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, local);

    具体服务:com.alibaba.dubbo.demo.provider.DemoServiceImpl。调用链如下:

    1                <!--  1 将ref包装成Invoker   -->
    2                -->ProxyFactory$Adaptive.getInvoker(ref实例, interfaceClass, injvmUrl)
    3                   -->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension("javassist")
    4                   -->StubProxyFactoryWrapper.getInvoker(T proxy, Class<T> type, URL url)
    5                      -->JavassistProxyFactory.getInvoker(T proxy, Class<T> type, URL url)
    6                         -->Wrapper getWrapper(Class<?> c)
    7                            -->makeWrapper(Class<?> c)
    8                         -->new AbstractProxyInvoker<T>(ref实例, interfaceClass, injvmUrl)

    1 ProxyFactory$Adaptive.getInvoker(java.lang.Object arg0, java.lang.Class arg1, com.alibaba.dubbo.common.URL arg2)

     1     public com.alibaba.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, com.alibaba.dubbo.common.URL arg2) throws com.alibaba.dubbo.rpc.RpcException {
     2         if (arg2 == null)
     3             throw new IllegalArgumentException("url == null");
     4         com.alibaba.dubbo.common.URL url = arg2;
     5         String extName = url.getParameter("proxy", "javassist");
     6         if(extName == null)
     7             throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
     8         com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
     9         return extension.getInvoker(arg0, arg1, arg2);
    10     }
    • arg0: com.alibaba.dubbo.demo.provider.DemoServiceImpl 实例
    • arg1: interface com.alibaba.dubbo.demo.DemoService Class对象
    • arg2: injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3275&side=provider&timestamp=1507010529605

    这里,首先是参数检查和赋值。之后获取key为javassist的ProxyFactory实现类:JavassistProxyFactory,该类会在spi进行aop的时候包裹在StubProxyFactoryWrapper中,最终调用链为:

    ProxyFactory$Adaptive -> StubProxyFactoryWrapper -> JavassistProxyFactory

    2 StubProxyFactoryWrapper.getInvoker(T proxy, Class<T> type, URL url)

    1     public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException {
    2         return proxyFactory.getInvoker(proxy, type, url);
    3     }

    这里的proxyFactory就是JavassistProxyFactory实例了。

    3 JavassistProxyFactory.getInvoker(T proxy, Class<T> type, URL url)

     1     public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
     2         // TODO Wrapper类不能正确处理带$的类名
     3         final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
     4         return new AbstractProxyInvoker<T>(proxy, type, url) {
     5             @Override
     6             protected Object doInvoke(T proxy, String methodName,
     7                                       Class<?>[] parameterTypes,
     8                                       Object[] arguments) throws Throwable {
     9                 return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
    10             }
    11         };
    12     }

    这里首先会创建一个class com.alibaba.dubbo.demo.provider.DemoServiceImpl的包装类Wrapper:Wrapper.getWrapper(Class<DemoServiceImpl>)。该类记录了DemoServiceImpl的属性名称,方法名称等信息。具体代码如下:

     1 package com.alibaba.dubbo.demo;
     2 
     3 import com.alibaba.dubbo.common.bytecode.Wrapper;
     4 import java.util.HashMap;
     5 
     6 public class Wrapper1 extends Wrapper {
     7 
     8     public static String[] pns;//property name array
     9     public static java.util.Map pts = new HashMap();//<property key, property value>
    10     public static String[] mns;//method names
    11     public static String[] dmns;//
    12     public static Class[] mts0;
    13 
    14     public Wrapper1() {
    15     }
    16 
    17     public String[] getPropertyNames() {
    18         return pns;
    19     }
    20 
    21     public boolean hasProperty(String n) {
    22         return pts.containsKey(n);
    23     }
    24 
    25     public Class getPropertyType(String n) {
    26         return (Class) pts.get(n);
    27     }
    28 
    29     public String[] getMethodNames() {
    30         return mns;
    31     }
    32 
    33     public String[] getDeclaredMethodNames() {
    34         return dmns;
    35     }
    36 
    37     public void setPropertyValue(Object o, String n, Object v) {
    38         com.alibaba.dubbo.demo.provider.DemoServiceImpl w;
    39         try {
    40             w = ((com.alibaba.dubbo.demo.provider.DemoServiceImpl) o);
    41         } catch (Throwable e) {
    42             throw new IllegalArgumentException(e);
    43         }
    44         throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + n + "\" filed or setter method in class com.alibaba.dubbo.demo.provider.DemoServiceImpl.");
    45     }
    46 
    47     public Object getPropertyValue(Object o, String n) {
    48         com.alibaba.dubbo.demo.provider.DemoServiceImpl w;
    49         try {
    50             w = ((com.alibaba.dubbo.demo.provider.DemoServiceImpl) o);
    51         } catch (Throwable e) {
    52             throw new IllegalArgumentException(e);
    53         }
    54         throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + n + "\" filed or setter method in class com.alibaba.dubbo.demo.provider.DemoServiceImpl.");
    55     }
    56 
    57     /**
    58      * @param o  实现类
    59      * @param n  方法名称
    60      * @param p  参数类型
    61      * @param v  参数名称
    62      * @return
    63      * @throws java.lang.reflect.InvocationTargetException
    64      */
    65     public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException {
    66         com.alibaba.dubbo.demo.provider.DemoServiceImpl w;
    67         try {
    68             w = ((com.alibaba.dubbo.demo.provider.DemoServiceImpl) o);
    69         } catch (Throwable e) {
    70             throw new IllegalArgumentException(e);
    71         }
    72         try {
    73             if ("sayHello".equals(n) && p.length == 1) {
    74                 return ($w) w.sayHello((java.lang.String) v[0]);
    75             }
    76         } catch (Throwable e) {
    77             throw new java.lang.reflect.InvocationTargetException(e);
    78         }
    79         throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException("Not found method \"" + n + "\" in class com.alibaba.dubbo.demo.provider.DemoServiceImpl.");
    80     }
    81 }

    之后创建一个AbstractProxyInvoker实例。

     1 package com.alibaba.dubbo.rpc.proxy;
     2 
     3 import com.alibaba.dubbo.common.URL;
     4 import com.alibaba.dubbo.rpc.Invocation;
     5 import com.alibaba.dubbo.rpc.Invoker;
     6 import com.alibaba.dubbo.rpc.Result;
     7 import com.alibaba.dubbo.rpc.RpcException;
     8 import com.alibaba.dubbo.rpc.RpcResult;
     9 
    10 import java.lang.reflect.InvocationTargetException;
    11 
    12 public abstract class AbstractProxyInvoker<T> implements Invoker<T> {
    13     private final T proxy;
    14     private final Class<T> type;
    15     private final URL url;
    16 
    17     public AbstractProxyInvoker(T proxy, Class<T> type, URL url) {
    18         if (proxy == null) {
    19             throw new IllegalArgumentException("proxy == null");
    20         }
    21         if (type == null) {
    22             throw new IllegalArgumentException("interface == null");
    23         }
    24         if (!type.isInstance(proxy)) {
    25             throw new IllegalArgumentException(proxy.getClass().getName() + " not implement interface " + type);
    26         }
    27         this.proxy = proxy;
    28         this.type = type;
    29         this.url = url;
    30     }
    31 
    32     public Class<T> getInterface() {
    33         return type;
    34     }
    35 
    36     public URL getUrl() {
    37         return url;
    38     }
    39 
    40     public boolean isAvailable() {
    41         return true;
    42     }
    43 
    44     public void destroy() {
    45     }
    46 
    47     public Result invoke(Invocation invocation) throws RpcException {
    48         try {
    49             return new RpcResult(doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments()));
    50         } catch (InvocationTargetException e) {
    51             return new RpcResult(e.getTargetException());
    52         } catch (Throwable e) {
    53             throw new RpcException("Failed to invoke remote proxy method " + invocation.getMethodName() + " to " + getUrl() + ", cause: " + e.getMessage(), e);
    54         }
    55     }
    56 
    57     /**
    58      * 由创建的实例来复写
    59      * @param proxy
    60      * @param methodName
    61      * @param parameterTypes
    62      * @param arguments
    63      * @return
    64      * @throws Throwable
    65      */
    66     protected abstract Object doInvoke(T proxy, String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Throwable;
    67 
    68     @Override
    69     public String toString() {
    70         return getInterface() + " -> " + getUrl() == null ? " " : getUrl().toString();
    71     }
    72 }

    其中:

    • proxy: com.alibaba.dubbo.demo.provider.DemoServiceImpl 实例
    • type: interface com.alibaba.dubbo.demo.DemoService Class对象
    • url: injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3275&side=provider&timestamp=1507010529605

    这样,具体服务就被包装成AbstractProxyInvoker实例了。

    二 AbstractProxyInvoker实例转换为Exporter

    Exporter<?> exporter = protocol.export(invoker);

    调用链如下:

     1                <!--  2 将Invoker暴露为Exporter   -->
     2                -->Protocol$Adaptive.export(Invoker<T> invoker)
     3                   -->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension("injvm")
     4                   -->ProtocolListenerWrapper.export(Invoker<T> invoker)
     5                      -->ProtocolFilterWrapper.export(Invoker<T> invoker)
     6                         -->buildInvokerChain(final Invoker<T> invoker, key:"service.filter", group:"provider")
     7                            -->ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(injvmUrl, "service.filter", "provider")
     8                         -->InjvmProtocol.export(Invoker<T> invoker)
     9                            -->new InjvmExporter(Invoker<T> invoker, key:"com.alibaba.dubbo.demo.DemoService", Map<String, Exporter<?>> exporterMap)
    10                               -->InjvmExporter.exporterMap({"com.alibaba.dubbo.demo.DemoService" -> "injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3207&side=provider&timestamp=1507009133930"})
    11                   -->new ListenerExporterWrapper(Exporter<T> exporter, List<ExporterListener> listeners) : 使用listener包装invoker

    1 Protocol$Adaptive.export(Invoker<T> invoker)

     1     public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
     2         if (arg0 == null)
     3             throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
     4         if (arg0.getUrl() == null)
     5             throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
     6         com.alibaba.dubbo.common.URL url = arg0.getUrl();
     7         String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
     8         if(extName == null)
     9             throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
    10         com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
    11         return extension.export(arg0);
    12     }
    • arg0:上边创建出来的AbstractProxyInvoker实例。

    这里,首先是参数检查和赋值。之后获取key为injvm的Protocol实现类:InjvmProtocol,该类会在spi进行aop的时候被ProtocolFilterWrapper和ProtocolListenerWrapper递归包裹,最终调用链为:

    ProxyFactory$Adaptive -> ProtocolListenerWrapper -> ProtocolFilterWrapper -> InjvmProtocol

    2 ProtocolListenerWrapper.export(Invoker<T> invoker)

    1     public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    2         if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
    3             return protocol.export(invoker);
    4         }
    5         return new ListenerExporterWrapper<T>(protocol.export(invoker),
    6                                               Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class).getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
    7     }

    这里先调用ProtocolFilterWrapper.export(Invoker<T> invoker),之后获取listener,最后进行递归包裹。(这里没有listener)

    3 ProtocolFilterWrapper.export(Invoker<T> invoker)

    1     public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    2         if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
    3             return protocol.export(invoker);
    4         }
    5         return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
    6     }

    这里首先使用filter对invoker进行了递归包裹,之后使用InjvmProtocol将包裹后的invoker转化为InjvmExporter。

    buildInvokerChain(final Invoker<T> invoker, String key, String group)

     1     /**
     2      * 1 根据key从url中获取相应的filter的values,再根据这个values和group去获取类上带有@Active注解的filter集合
     3      * 2 之后将这些filter对传入的invoker进行递归包装层invoker(就是一个链表)
     4      */
     5     private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
     6         Invoker<T> last = invoker;
     7         List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
     8         if (filters.size() > 0) {
     9             for (int i = filters.size() - 1; i >= 0; i--) {
    10                 final Filter filter = filters.get(i);
    11                 final Invoker<T> next = last;
    12                 last = new Invoker<T>() {
    13 
    14                     public Class<T> getInterface() {
    15                         return invoker.getInterface();
    16                     }
    17 
    18                     public URL getUrl() {
    19                         return invoker.getUrl();
    20                     }
    21 
    22                     public boolean isAvailable() {
    23                         return invoker.isAvailable();
    24                     }
    25 
    26                     public Result invoke(Invocation invocation) throws RpcException {
    27                         return filter.invoke(next, invocation);
    28                     }
    29 
    30                     public void destroy() {
    31                         invoker.destroy();
    32                     }
    33 
    34                     @Override
    35                     public String toString() {
    36                         return invoker.toString();
    37                     }
    38                 };
    39             }
    40         }
    41         return last;
    42     }

    这里:

    • invoker:之前创建出来的AbstractProxyInvoker实例;
    • key:service.filter
    • group:provider
    List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);

    这句代码,是:根据key从url中获取相应的filter的values,再根据这个values和group去获取类上带有@Active注解的filter集合。这一块儿具体的代码可以查看讲解spi中的loadFile方法。最终会获取到8个filter,关于filter,后续会说。

    4 InjvmProtocol.export(Invoker<T> invoker)

    1     public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    2         return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap);
    3     }
     1 class InjvmExporter<T> extends AbstractExporter<T> {
     2     private final String key;
     3     private final Map<String, Exporter<?>> exporterMap;
     4 
     5     InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {
     6         super(invoker);
     7         this.key = key;
     8         this.exporterMap = exporterMap;
     9         exporterMap.put(key, this);
    10     }
    11 
    12     public void unexport() {
    13         super.unexport();
    14         exporterMap.remove(key);
    15     }
    16 }

    最终的InjvmExporter实例:

    • key = "com.alibaba.dubbo.demo.DemoService"
    • exporterMap: { "com.alibaba.dubbo.demo.DemoService" -> 当前的InjvmExporter实例 }
    • Invoker<T> invoker = 被filter进行递归包裹后的Invoker

    最终的ServiceConfig的exporters列表:

    • List<Exporter<?>> exporters = [ 上边的injvmExporter实例 ]

    为什么要有本地暴露?

    同一个jvm中的服务,相互调用不需要通过远程注册中心,但是又想使用filter链,可以使用本地暴露。

    https://dubbo.gitbooks.io/dubbo-user-book/demos/local-call.html

    “本地调用使用了 injvm 协议,是一个伪协议,它不开启端口,不发起远程调用,只在 JVM 内直接关联,但执行 Dubbo 的 Filter 链。”

    7.3 netty3基本使用

    由于dubbo默认使用的是netty3进行通信的,这里简单的列出一个netty3通信的例子。

    一 server端

    1 Server

     1 package com.hulk.netty.server;
     2 
     3 import org.jboss.netty.bootstrap.ServerBootstrap;
     4 import org.jboss.netty.channel.*;
     5 import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
     6 import org.jboss.netty.handler.codec.string.StringDecoder;
     7 import org.jboss.netty.handler.codec.string.StringEncoder;
     8 
     9 import java.net.InetSocketAddress;
    10 import java.util.concurrent.Executors;
    11 
    12 public class Server {
    13     public void start(){
    14         ChannelFactory factory = new NioServerSocketChannelFactory(
    15                 Executors.newCachedThreadPool(),//boss线程池
    16                 Executors.newCachedThreadPool(),//worker线程池
    17                 8//worker线程数
    18         );
    19         ServerBootstrap bootstrap = new ServerBootstrap(factory);
    20         /**
    21          * 对于每一个连接channel, server都会调用PipelineFactory为该连接创建一个ChannelPipline
    22          */
    23         bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
    24             public ChannelPipeline getPipeline() throws Exception {
    25                 ChannelPipeline pipeline = Channels.pipeline();
    26                 pipeline.addLast("decoder", new StringDecoder());
    27                 pipeline.addLast("encoder", new StringEncoder());
    28                 pipeline.addLast("handler", new ServerLogicHandler());
    29                 return pipeline;
    30             }
    31         });
    32 
    33         Channel channel = bootstrap.bind(new InetSocketAddress("127.0.0.1", 8080));
    34         System.out.println("server start success!");
    35     }
    36 
    37     public static void main(String[] args) throws InterruptedException {
    38         Server server = new Server();
    39         server.start();
    40         Thread.sleep(Integer.MAX_VALUE);
    41     }
    42 }

    步骤:

    • 首先创建了NioServerSocketChannelFactory:创建boss线程池,创建worker线程池以及worker线程数。(boss线程数默认为1个)
    • 创建ServerBootstrap server端启动辅助类
    • 为ServerBootstrap设置ChannelPipelineFactory工厂,并为ChannelPipelineFactory将来创建出的ChannelPipeline设置编码器/解码器/事件处理器
    • 使用ServerBootstrap绑定监听地址和端口

    2 事件处理器ServerLogicHandler

     1 package com.hulk.netty.server;
     2 
     3 import org.jboss.netty.channel.Channel;
     4 import org.jboss.netty.channel.ChannelHandlerContext;
     5 import org.jboss.netty.channel.ChannelStateEvent;
     6 import org.jboss.netty.channel.ExceptionEvent;
     7 import org.jboss.netty.channel.MessageEvent;
     8 import org.jboss.netty.channel.SimpleChannelHandler;
     9 
    10 public class ServerLogicHandler extends SimpleChannelHandler {
    11     @Override
    12     public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
    13         System.out.println("连接成功, channel: " + e.getChannel().toString());
    14     }
    15 
    16     @Override
    17     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
    18         String msg = (String) e.getMessage();
    19         System.out.println("接收到了client的消息, msg: " + msg);
    20 
    21         Channel channel = e.getChannel();
    22         String str = "hi, client";
    23 
    24         channel.write(str);//写消息发给client端
    25         System.out.println("服务端发送数据: " + str + "完成");
    26     }
    27 
    28     @Override
    29     public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
    30         e.getCause().printStackTrace();
    31         e.getChannel().close();
    32     }
    33 }

    说明:

    • 监听与客户端连接成功事件
    • 监听接收到来自客户端的消息,之后写回给客户端消息
    • 捕捉异常事件

    二 client端

    1 Client

     1 package com.hulk.netty.client;
     2 
     3 import org.jboss.netty.bootstrap.ClientBootstrap;
     4 import org.jboss.netty.channel.ChannelFactory;
     5 import org.jboss.netty.channel.ChannelPipeline;
     6 import org.jboss.netty.channel.ChannelPipelineFactory;
     7 import org.jboss.netty.channel.Channels;
     8 import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
     9 import org.jboss.netty.handler.codec.string.StringDecoder;
    10 import org.jboss.netty.handler.codec.string.StringEncoder;
    11 
    12 import java.net.InetSocketAddress;
    13 import java.util.concurrent.Executors;
    14 
    15 public class Client {
    16     public static void main(String[] args) {
    17         ChannelFactory factory = new NioClientSocketChannelFactory(
    18                 Executors.newCachedThreadPool(),
    19                 Executors.newCachedThreadPool(),
    20                 8
    21         );
    22         ClientBootstrap bootstrap = new ClientBootstrap(factory);
    23         bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
    24             public ChannelPipeline getPipeline() throws Exception {
    25                 ChannelPipeline pipeline = Channels.pipeline();
    26                 pipeline.addLast("decoder", new StringDecoder());
    27                 pipeline.addLast("encoder", new StringEncoder());
    28                 pipeline.addLast("handler", new ClientLogicHandler());
    29                 return pipeline;
    30             }
    31         });
    32 
    33         bootstrap.connect(new InetSocketAddress("127.0.0.1", 8080));
    34         System.out.println("client start success!");
    35     }
    36 }

    步骤:(与Server几乎相同)

    • 首先创建了NioClientSocketChannelFactory:创建boss线程池,创建worker线程池以及worker线程数。(boss线程数默认为1个)
    • 创建ClientBootstrap client端启动辅助类
    • 为ClientBootstrap设置ChannelPipelineFactory工厂,并为ChannelPipelineFactory将来创建出的ChannelPipeline设置编码器/解码器/事件处理器
    • 使用ClientBootstrap连接Server端监听的地址和端口

    2 ClientLogicHandler

     1 package com.hulk.netty.client;
     2 
     3 import org.jboss.netty.channel.ChannelHandlerContext;
     4 import org.jboss.netty.channel.ChannelStateEvent;
     5 import org.jboss.netty.channel.ExceptionEvent;
     6 import org.jboss.netty.channel.MessageEvent;
     7 import org.jboss.netty.channel.SimpleChannelHandler;
     8 import org.jboss.netty.channel.WriteCompletionEvent;
     9 
    10 public class ClientLogicHandler extends SimpleChannelHandler {
    11     @Override
    12     public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
    13         System.out.println("客户端连接成功!");
    14         String str = "hi server!";
    15         e.getChannel().write(str);//异步
    16     }
    17 
    18     @Override
    19     public void writeComplete(ChannelHandlerContext ctx, WriteCompletionEvent e) throws Exception {
    20         System.out.println("客户端写消息完成");
    21     }
    22 
    23     @Override
    24     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
    25         String msg = (String) e.getMessage();
    26         System.out.println("客户端接收到消息, msg: " + msg);
    27     }
    28 
    29     @Override
    30     public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
    31         e.getCause().printStackTrace();
    32         e.getChannel().close();
    33     }
    34 }

    说明:

    • 监听与服务端连接成功事件,连接成功后,写消息给服务端
    • 监听向服务端写消息完成的事件
    • 监听接收到来自服务端的消息
    • 捕捉异常事件

    这就是一个简单的netty3通信的例子,关于netty,后续会读源码。

    7.4 服务远程暴露 - 创建Exporter与启动netty服务端

    为了安全:服务启动的ip全部使用10.10.10.10

    远程服务的暴露总体步骤:

    • 将ref封装为invoker
    • 将invoker转换为exporter
    • 启动netty
    • 注册服务到zookeeper
    • 订阅
    • 返回新的exporter实例

    服务远程暴露的代码:

     1             //如果配置不是local则暴露为远程服务.(配置为local,则表示只暴露本地服务)
     2             if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
     3                 if (logger.isInfoEnabled()) {
     4                     logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
     5                 }
     6                 if (registryURLs != null && registryURLs.size() > 0
     7                         && url.getParameter("register", true)) {
     8                     for (URL registryURL : registryURLs) {
     9                         url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));
    10                         URL monitorUrl = loadMonitor(registryURL);
    11                         if (monitorUrl != null) {
    12                             url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
    13                         }
    14                         if (logger.isInfoEnabled()) {
    15                             logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
    16                         }
    17                         Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
    18                         Exporter<?> exporter = protocol.export(invoker);
    19                         exporters.add(exporter);
    20                     }
    21                 } else {
    22                     Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
    23                     Exporter<?> exporter = protocol.export(invoker);
    24                     exporters.add(exporter);
    25                 }
    26             }

    首先将实现类ref封装为Invoker,之后将invoker转换为exporter,最后将exporter放入缓存List<Exporter> exporters中。

    一 将实现类ref封装为Invoker

    1 Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));

    1  为registryURL拼接export=providerUrl参数

    一开始的registryURL:

    registry://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&client=curator&dubbo=2.0.0&pid=887&registry=zookeeper&timestamp=1507096022072

    registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString())这句代码为registryURL添加了参数并编码:(这里给出没有编码的样子)

    1 export=dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=887&side=provider&timestamp=1507096024334

    2  ProxyFactory$Adaptive.getInvoker(DemoServiceImpl实例, Class<DemoService>, registryURL)

     1     public com.alibaba.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, com.alibaba.dubbo.common.URL arg2) throws com.alibaba.dubbo.rpc.RpcException {
     2         if (arg2 == null)
     3             throw new IllegalArgumentException("url == null");
     4         com.alibaba.dubbo.common.URL url = arg2;
     5         String extName = url.getParameter("proxy", "javassist");//结果是javassist
     6         if(extName == null)
     7             throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
     8         com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
     9         return extension.getInvoker(arg0, arg1, arg2);
    10     }

    这里,本来是调用JavassistProxyFactory的getInvoker方法,但是JavassistProxyFactory被StubProxyFactoryWrapper给aop了。

    3  StubProxyFactoryWrapper.getInvoker(DemoServiceImpl实例, Class<DemoService>, registryURL)

    1     public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException {
    2         return proxyFactory.getInvoker(proxy, type, url);
    3     }

    4  JavassistProxyFactory.getInvoker(DemoServiceImpl实例, Class<DemoService>, registryURL)

     1     public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
     2         // TODO Wrapper类不能正确处理带$的类名
     3         final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
     4         return new AbstractProxyInvoker<T>(proxy, type, url) {
     5             @Override
     6             protected Object doInvoke(T proxy, String methodName,
     7                                       Class<?>[] parameterTypes,
     8                                       Object[] arguments) throws Throwable {
     9                 return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
    10             }
    11         };
    12     }

    首先是创建Wrapper类:Wrapper.getWrapper(Class<DemoServiceImpl>)。该类记录了DemoServiceImpl的属性名称,方法名称等信息。关键代码如下:(完整代码见:7.2 服务本地暴露

     1 import com.alibaba.dubbo.common.bytecode.Wrapper;
     2 import java.util.HashMap;
     3 
     4 public class Wrapper1 extends Wrapper {
     5 
     6     public static String[] pns;//property name array
     7     public static java.util.Map pts = new HashMap();//<property key, property value>
     8     public static String[] mns;//method names
     9     public static String[] dmns;//
    10     public static Class[] mts0;
    55     /**
    56      * @param o  实现类
    57      * @param n  方法名称
    58      * @param p  参数类型
    59      * @param v  参数名称
    60      * @return
    61      * @throws java.lang.reflect.InvocationTargetException
    62      */
    63     public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException {
    64         com.alibaba.dubbo.demo.provider.DemoServiceImpl w;
    65         try {
    66             w = ((com.alibaba.dubbo.demo.provider.DemoServiceImpl) o);
    67         } catch (Throwable e) {
    68             throw new IllegalArgumentException(e);
    69         }
    70         try {
    71             if ("sayHello".equals(n) && p.length == 1) {
    72                 return ($w) w.sayHello((java.lang.String) v[0]);
    73             }
    74         } catch (Throwable e) {
    75             throw new java.lang.reflect.InvocationTargetException(e);
    76         }
    77         throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException("Not found method \"" + n + "\" in class com.alibaba.dubbo.demo.provider.DemoServiceImpl.");
    78     }
    79 }

    创建完DemoServiceImpl的Wrapper类之后(实际上该实例在本地暴露的时候已经存入缓存了,这里只是从缓存中拿出来而已),创建一个AbstractProxyInvoker实例。

     1     private final T proxy;
     2     private final Class<T> type;
     3     private final URL url;
     4 
     5     public AbstractProxyInvoker(T proxy, Class<T> type, URL url) {
     6         if (proxy == null) {
     7             throw new IllegalArgumentException("proxy == null");
     8         }
     9         if (type == null) {
    10             throw new IllegalArgumentException("interface == null");
    11         }
    12         if (!type.isInstance(proxy)) {
    13             throw new IllegalArgumentException(proxy.getClass().getName() + " not implement interface " + type);
    14         }
    15         this.proxy = proxy;
    16         this.type = type;
    17         this.url = url;
    18     }

    最后创建完成的AbstractProxyInvoker实例属性如下:

    • proxy:DemoServiceImpl实例
    • type:Class<com.alibaba.dubbo.demo.DemoService>
    • url:registry://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&client=curator&dubbo=2.0.0&export=dubbo%3A%2F%2F10.10.10.10%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26dubbo%3D2.0.0%26generic%3Dfalse%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D993%26side%3Dprovider%26timestamp%3D1507100322516&pid=993&registry=zookeeper&timestamp=1507100319830

    这样我们就将ref实现类转换成了Invoker,之后在调用该invoker.invoke(Invocation invocation)的时候,会调用invoker.doInvoke(T proxy, String methodName,Class<?>[] parameterTypes, Object[] arguments)的时候,就会调用相应的实现类proxy的wrapper类的invokeMethod(proxy, methodName, parameterTypes, arguments),该方法又会调用真实的实现类methodName方法。这里可以先给出AbstractProxyInvoker.invoke(Invocation invocation)源码:

    1     public Result invoke(Invocation invocation) throws RpcException {
    2         try {
    3             return new RpcResult(doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments()));
    4         } catch (InvocationTargetException e) {
    5             return new RpcResult(e.getTargetException());
    6         } catch (Throwable e) {
    7             throw new RpcException("Failed to invoke remote proxy method " + invocation.getMethodName() + " to " + getUrl() + ", cause: " + e.getMessage(), e);
    8         }
    9     }

    这里的proxy就是上边赋好值的proxy:DemoServiceImpl实例。而方法信息会封装在Invocation对象中,该对象在服务引用时介绍。

    二  将Invoker转换为Exporter

    1 Exporter<?> exporter = protocol.export(invoker)

    1  Protocol$Adaptive.export(com.alibaba.dubbo.rpc.Invoker AbstractProxyInvoker实例)

     1     public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
     2         if (arg0 == null)
     3             throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
     4         if (arg0.getUrl() == null)
     5             throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
     6         com.alibaba.dubbo.common.URL url = arg0.getUrl();
     7         String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());//registry
     8         if(extName == null)
     9             throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
    10         com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
    11         return extension.export(arg0);
    12     }

    这里,由于aop的原因,首先调用了ProtocolListenerWrapper的export(Invoker<T> invoker),如下:

    1     public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    2         if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
    3             return protocol.export(invoker);
    4         }
    5         return new ListenerExporterWrapper<T>(protocol.export(invoker),
    6                                               Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class).getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
    7     }

    由于协议是“registry”,所以不做任何处理,继续调用ProtocolFilterWrapper的export(Invoker<T> invoker),如下:

    1     public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    2         if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
    3             return protocol.export(invoker);
    4         }
    5         return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
    6     }

    同理,由于协议是“registry”,所以不做任何处理,继续调用RegistryProtocol.export(final Invoker<T> originInvoker),如下:

     1     public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
     2         //export invoker
     3         final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
     4         //registry provider
     5         final Registry registry = getRegistry(originInvoker);
     6         final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
     7         registry.register(registedProviderUrl);
     8         // 订阅override数据
     9         // FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,因为subscribed以服务名为缓存的key,导致订阅信息覆盖。
    10         final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);
    11         final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
    12         overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
    13         registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
    14         //保证每次export都返回一个新的exporter实例
    15         return new Exporter<T>() {
    16             public Invoker<T> getInvoker() {
    17                 return exporter.getInvoker();
    18             }
    19 
    20             public void unexport() {
    21                 try {
    22                     exporter.unexport();
    23                 } catch (Throwable t) {
    24                     logger.warn(t.getMessage(), t);
    25                 }
    26                 try {
    27                     registry.unregister(registedProviderUrl);
    28                 } catch (Throwable t) {
    29                     logger.warn(t.getMessage(), t);
    30                 }
    31                 try {
    32                     overrideListeners.remove(overrideSubscribeUrl);
    33                     registry.unsubscribe(overrideSubscribeUrl, overrideSubscribeListener);
    34                 } catch (Throwable t) {
    35                     logger.warn(t.getMessage(), t);
    36                 }
    37             }
    38         };
    39     }

    该方法完成了远程暴露的全部流程。

    • 将invoker转换为exporter
    • 启动netty
    • 注册服务到zookeeper
    • 订阅
    • 返回新的exporter实例

    2  将invoker转换为exporter并启动netty服务

    1 final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);

    doLocalExport(final Invoker<T> originInvoker)

     1     /**
     2      * 1 从invoker的URL中的Map<String, String> parameters中获取key为export的地址providerUrl,该地址将是服务注册在zk上的节点
     3      * 2 从 Map<String, ExporterChangeableWrapper<?>> bounds 缓存中获取key为上述providerUrl的exporter,如果有,直接返回,如果没有,创建并返回
     4      * @return
     5      */
     6     @SuppressWarnings("unchecked")
     7     private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker) {
     8         String key = getCacheKey(originInvoker);//根据originInvoker获取providerUrl
     9         ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
    10         if (exporter == null) {
    11             synchronized (bounds) {
    12                 exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
    13                 if (exporter == null) {
    14                     final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));//存储originInvoker和providerUrl
    15                     exporter = new ExporterChangeableWrapper<T>((Exporter<T>) protocol.export(invokerDelegete), originInvoker);
    16                     bounds.put(key, exporter);
    17                 }
    18             }
    19         }
    20         return exporter;
    21     }

    2.1 从originInvoker中获取providerUrl

    该方法直接首先调用getCacheKey(final Invoker<?> originInvoker)中获取providerUrl,这里的originInvoker就是上述创建出来的AbstractProxyInvoker实例,注意他的url是registry协议的,该url的export参数的value就是我们要获取的providerUrl。获取providerUrl的源码如下:

     1     private String getCacheKey(final Invoker<?> originInvoker) {
     2         URL providerUrl = getProviderUrl(originInvoker);
     3         String key = providerUrl.removeParameters("dynamic", "enabled").toFullString();
     4         return key;
     5     }
     6 
     7     private URL getProviderUrl(final Invoker<?> origininvoker) {
     8         String export = origininvoker.getUrl().getParameterAndDecoded(Constants.EXPORT_KEY);
     9         if (export == null || export.length() == 0) {
    10             throw new IllegalArgumentException("The registry export url is null! registry: " + origininvoker.getUrl());
    11         }
    12 
    13         URL providerUrl = URL.valueOf(export);
    14         return providerUrl;
    15     }

    之后一系列的操作,就是获取该providerUrl对应的exporter,之后放入缓存Map<String, ExporterChangeableWrapper<?>> bounds中,所以一个providerUrl只会对应一个exporter。

    2.2  创建InvokerDelegete

    1 final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));

    InvokerDelegete是RegistryProtocol的一个静态内部类,该类是一个originInvoker的委托类,该类存储了originInvoker,其父类InvokerWrapper还会存储providerUrl,InvokerWrapper会调用originInvoker的invoke方法,也会销毁invoker。可以管理invoker的生命周期。

     1     public static class InvokerDelegete<T> extends InvokerWrapper<T> {
     2         private final Invoker<T> invoker;
     3 
     4         /**
     5          * @param invoker
     6          * @param url     invoker.getUrl返回此值
     7          */
     8         public InvokerDelegete(Invoker<T> invoker, URL url) {
     9             super(invoker, url);
    10             this.invoker = invoker;
    11         }
    12 
    13         public Invoker<T> getInvoker() {
    14             if (invoker instanceof InvokerDelegete) {
    15                 return ((InvokerDelegete<T>) invoker).getInvoker();
    16             } else {
    17                 return invoker;
    18             }
    19         }
    20     }

    InvokerWrapper的核心代码:

     1 public class InvokerWrapper<T> implements Invoker<T> {
     2     private final Invoker<T> invoker;//originInvoker
     3     private final URL url;//providerUrl
     4 
     5     public InvokerWrapper(Invoker<T> invoker, URL url) {
     6         this.invoker = invoker;
     7         this.url = url;
     8     }
     9 
    10     public boolean isAvailable() {
    11         return invoker.isAvailable();
    12     }
    13 
    14     public Result invoke(Invocation invocation) throws RpcException {
    15         return invoker.invoke(invocation);
    16     }
    17 
    18     public void destroy() {
    19         invoker.destroy();
    20     }
    21 }

    这样一个InvokerDelegete对象就创建好了,属性如下:

    • invoker:originInvoker(AbstractProxyInvoker对象)
    • InvokerWrapper.invoker:originInvoker(AbstractProxyInvoker对象)
    • url:providerUrl(dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=1035&side=provider&timestamp=1507101286063)

    2.3  使用DubboProtocol将InvokerDelegete转换为Exporter

    1 exporter = new ExporterChangeableWrapper<T>((Exporter<T>) protocol.export(invokerDelegete), originInvoker)

    2.3.1  Protocol$Adaptive.export(com.alibaba.dubbo.rpc.Invoker InvokerDelegete对象)

     1     public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
     2         if (arg0 == null)
     3             throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
     4         if (arg0.getUrl() == null)
     5             throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
     6         com.alibaba.dubbo.common.URL url = arg0.getUrl();
     7         String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());//dubbo
     8         if(extName == null)
     9             throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
    10         com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
    11         return extension.export(arg0);
    12     }

    该代码再贴最后一遍了。之后调用ProtocolListenerWrapper的ProtocolListenerWrapper.export(Invoker<T> InvokerDelegete),之后调用ProtocolFilterWrapper.export(Invoker<T> InvokerDelegete):首先对InvokerDelegete对象进行8个filter的递归包装,之后使用DubboProtocol对包装后的InvokerDelegete对象进行export。

    层层包装的源码:

     1     /**
     2      * 1 根据key从url中获取相应的filter的values,再根据这个values和group去获取类上带有@Active注解的filter集合
     3      * 2 之后将这些filter对传入的invoker进行递归包装层invoker(就是一个链表)
     4      */
     5     private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
     6         Invoker<T> last = invoker;
     7         List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
     8         if (filters.size() > 0) {
     9             for (int i = filters.size() - 1; i >= 0; i--) {
    10                 final Filter filter = filters.get(i);
    11                 final Invoker<T> next = last;
    12                 last = new Invoker<T>() {
    13 
    14                     public Class<T> getInterface() {
    15                         return invoker.getInterface();
    16                     }
    17 
    18                     public URL getUrl() {
    19                         return invoker.getUrl();
    20                     }
    21 
    22                     public boolean isAvailable() {
    23                         return invoker.isAvailable();
    24                     }
    25 
    26                     public Result invoke(Invocation invocation) throws RpcException {
    27                         return filter.invoke(next, invocation);
    28                     }
    29 
    30                     public void destroy() {
    31                         invoker.destroy();
    32                     }
    33 
    34                     @Override
    35                     public String toString() {
    36                         return invoker.toString();
    37                     }
    38                 };
    39             }
    40         }
    41         return last;
    42     }

    上述方法中最重要的就是Invoker的Result invoke(Invocation invocation),在该方法中,是使用了filter.invoke(next, invocation),而这里的next又可能是另一个filter。这里我们打开一个filter来看一下源码:

    1 @Activate(group = Constants.PROVIDER, order = -110000)
    2 public class EchoFilter implements Filter {
    3     public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
    4         if (inv.getMethodName().equals(Constants.$ECHO) && inv.getArguments() != null && inv.getArguments().length == 1)
    5             return new RpcResult(inv.getArguments()[0]);
    6         return invoker.invoke(inv);
    7     }
    8 }

    可以看到,该filter会调用传入的next的invoke方法。

    这里给出被递归包装后的对象:(命名为InvokerDelegete的filter对象)

    1 EchoFilter
    2 -->ClassLoaderFilter
    3    -->GenericFilter
    4       -->ContextFilter
    5          -->TraceFilter
    6             -->TimeoutFilter
    7                -->MonitorFilter
    8                   -->ExceptionFilter
    9                      -->InvokerDelegete对象

    2.3.2  DubboProtocol.export(Invoker<T> InvokerDelegete的filter对象)

        /**
         * 1 从invoker的url中获取将要暴露的远程服务的key:com.alibaba.dubbo.demo.DemoService:20880(实际上是:serviceGroup/serviceName:serviceVersion:port)
         * 注意:本地暴露的key就是:com.alibaba.dubbo.demo.DemoService
         * 2 打开ExchangeServer
         */
        public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
            URL url = invoker.getUrl();
            // export service.
            String key = serviceKey(url);
            DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
            exporterMap.put(key, exporter);
    
            //export an stub service for dispaching 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);
                }
            }
    
            openServer(url);
    
            return exporter;
        }

    首先从“InvokerDelegete的filter对象”中的url获取key,这段代码很简单,就是获取serviceGroup/serviceName:serviceVersion:port这样形式的一个key,这里最后获取到的是com.alibaba.dubbo.demo.DemoService:20880。

    之后创建DubboExporter。

    2.3.2.1 DubboExporter<T>(InvokerDelegete的filter对象, "com.alibaba.dubbo.demo.DemoService:20880", exporterMap)

     1 public class DubboExporter<T> extends AbstractExporter<T> {
     2     //serviceGroup/serviceName:serviceVersion:port, 例如:com.alibaba.dubbo.demo.DemoService:20880
     3     private final String key;//
     4     //{ "com.alibaba.dubbo.demo.DemoService:20880" -> 当前的DubboExporter实例 }
     5     private final Map<String, Exporter<?>> exporterMap;
     6 
     7     public DubboExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {
     8         super(invoker);
     9         this.key = key;
    10         this.exporterMap = exporterMap;
    11     }
    12 
    13     @Override
    14     public void unexport() {
    15         super.unexport();
    16         exporterMap.remove(key);
    17     }
    18 }

    注意这里的exporterMap是引用传递。

    父类:

     1 public abstract class AbstractExporter<T> implements Exporter<T> {
     2     protected final Logger logger = LoggerFactory.getLogger(getClass());
     3     private final Invoker<T> invoker;
     4     private volatile boolean unexported = false;
     5 
     6     public AbstractExporter(Invoker<T> invoker) {
     7         if (invoker == null)
     8             throw new IllegalStateException("service invoker == null");
     9         if (invoker.getInterface() == null)
    10             throw new IllegalStateException("service type == null");
    11         if (invoker.getUrl() == null)
    12             throw new IllegalStateException("service url == null");
    13         this.invoker = invoker;
    14     }
    15 
    16     public Invoker<T> getInvoker() {
    17         return invoker;
    18     }
    19 
    20     public void unexport() {
    21         if (unexported) {
    22             return;
    23         }
    24         unexported = true;
    25         getInvoker().destroy();
    26     }
    27 
    28     public String toString() {
    29         return getInvoker().toString();
    30     }
    31 }

    这里,我们把一个“InvokerDelegete的filter对象”赋给了AbstractExporter的Invoker引用,也就是说从exporter中可以获取到invoker。最后在DubboProtocol.export(Invoker<T> invoker)中执行:exporterMap.put(key, exporter); 这样就将{ "com.alibaba.dubbo.demo.DemoService:20880" -> 当前的DubboExporter实例 }存储起来了。

    来看一下现在的DubboExporter实例:

    • key:com.alibaba.dubbo.demo.DemoService:20880
    • invoker:“InvokerDelegete的filter对象”
    • exporterMap:{ "com.alibaba.dubbo.demo.DemoService:20880" -> 当前的DubboExporter实例 }

    2.3.2.2 开启ExchangeServer

     1     /**
     2      * 从缓存Map<String, ExchangeServer> serverMap中根据"host:port"获取ExchangeServer,如果没有,创建ExchangeServer,之后放入缓存。
     3      * @param url
     4      */
     5     private void openServer(URL url) {
     6         // find server.
     7         String key = url.getAddress();//10.10.10.10:20880
     8         //client 也可以暴露一个只有server可以调用的服务。
     9         boolean isServer = url.getParameter(Constants.IS_SERVER_KEY, true);
    10         if (isServer) {
    11             ExchangeServer server = serverMap.get(key);
    12             if (server == null) {
    13                 serverMap.put(key, createServer(url));
    14             } else {
    15                 //server支持reset,配合override功能使用
    16                 server.reset(url);
    17             }
    18         }
    19     }

    首先从provderUrl中获取host:port作为key,之后从缓存serverMap中获取ExchangeServer,如果没有,创建ExchangeServer,最后以如下方式放入缓存:

    Map<String, ExchangeServer> serverMap:{ "10.10.10.10:20880"<->ExchangeServer实例 }。

    创建ExchangeServer:createServer(URL providerUrl)

     1     private ExchangeServer createServer(URL url) {
     2         //默认开启server关闭时发送readonly事件
     3         url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());
     4         //默认开启heartbeat
     5         url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
     6         String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);
     7 
     8         if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str))
     9             throw new RpcException("Unsupported server type: " + str + ", url: " + url);
    10 
    11         url = url.addParameter(Constants.CODEC_KEY, Version.isCompatibleVersion() ? COMPATIBLE_CODEC_NAME : DubboCodec.NAME);
    12         ExchangeServer server;
    13         try {
    14             server = Exchangers.bind(url, requestHandler);
    15         } catch (RemotingException e) {
    16             throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
    17         }
    18         str = url.getParameter(Constants.CLIENT_KEY);
    19         if (str != null && str.length() > 0) {
    20             Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
    21             if (!supportedTypes.contains(str)) {
    22                 throw new RpcException("Unsupported client type: " + str);
    23             }
    24         }
    25         return server;
    26     }

    首先是在原本providerUrl上添加参数:channel.readonly.sent=true&heartbeat=60000&codec=dubbo(其中的heartbeat参数会在HeaderExchangeServer启动心跳计时器时使用)

    之后使用Exchangers.bind("添加参数后的providerUrl", requestHandler)创建ExchangeServer。首先来看一下DubboProtocol#requestHandler。这个类极其重要,后续经过层层包装后,会成为最终netty的服务端逻辑处理器。

     1     private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
     2         public Object reply(ExchangeChannel channel, Object message) throws RemotingException {
     3             if (message instanceof Invocation) {
     4                 Invocation inv = (Invocation) message;
     5                 Invoker<?> invoker = getInvoker(channel, inv);
     6                 //如果是callback 需要处理高版本调用低版本的问题
     7                 if (Boolean.TRUE.toString().equals(inv.getAttachments().get(IS_CALLBACK_SERVICE_INVOKE))) {
     8                     String methodsStr = invoker.getUrl().getParameters().get("methods");
     9                     boolean hasMethod = false;
    10                     if (methodsStr == null || methodsStr.indexOf(",") == -1) {
    11                         hasMethod = inv.getMethodName().equals(methodsStr);
    12                     } else {
    13                         String[] methods = methodsStr.split(",");
    14                         for (String method : methods) {
    15                             if (inv.getMethodName().equals(method)) {
    16                                 hasMethod = true;
    17                                 break;
    18                             }
    19                         }
    20                     }
    21                     if (!hasMethod) {
    22                         logger.warn(new IllegalStateException("The methodName " + inv.getMethodName() + " not found in callback service interface ,invoke will be ignored. please update the api interface. url is:" + invoker.getUrl()) + " ,invocation is :" + inv);
    23                         return null;
    24                     }
    25                 }
    26                 RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
    27                 return invoker.invoke(inv);
    28             }
    29             throw new RemotingException(channel, "Unsupported request: " + message == null ? null : (message.getClass().getName() + ": " + message) + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress());
    30         }
    31 
    32         @Override
    33         public void received(Channel channel, Object message) throws RemotingException {
    34             if (message instanceof Invocation) {
    35                 reply((ExchangeChannel) channel, message);
    36             } else {
    37                 super.received(channel, message);
    38             }
    39         }
    40 
    41         @Override
    42         public void connected(Channel channel) throws RemotingException {
    43             invoke(channel, Constants.ON_CONNECT_KEY);
    44         }
    45 
    46         @Override
    47         public void disconnected(Channel channel) throws RemotingException {
    48             if (logger.isInfoEnabled()) {
    49                 logger.info("disconected from " + channel.getRemoteAddress() + ",url:" + channel.getUrl());
    50             }
    51             invoke(channel, Constants.ON_DISCONNECT_KEY);
    52         }
    53 
    54         private void invoke(Channel channel, String methodKey) {
    55             Invocation invocation = createInvocation(channel, channel.getUrl(), methodKey);
    56             if (invocation != null) {
    57                 try {
    58                     received(channel, invocation);
    59                 } catch (Throwable t) {
    60                     logger.warn("Failed to invoke event method " + invocation.getMethodName() + "(), cause: " + t.getMessage(), t);
    61                 }
    62             }
    63         }
    64 
    65         private Invocation createInvocation(Channel channel, URL url, String methodKey) {
    66             String method = url.getParameter(methodKey);
    67             if (method == null || method.length() == 0) {
    68                 return null;
    69             }
    70             RpcInvocation invocation = new RpcInvocation(method, new Class<?>[0], new Object[0]);
    71             invocation.setAttachment(Constants.PATH_KEY, url.getPath());
    72             invocation.setAttachment(Constants.GROUP_KEY, url.getParameter(Constants.GROUP_KEY));
    73             invocation.setAttachment(Constants.INTERFACE_KEY, url.getParameter(Constants.INTERFACE_KEY));
    74             invocation.setAttachment(Constants.VERSION_KEY, url.getParameter(Constants.VERSION_KEY));
    75             if (url.getParameter(Constants.STUB_EVENT_KEY, false)) {
    76                 invocation.setAttachment(Constants.STUB_EVENT_KEY, Boolean.TRUE.toString());
    77             }
    78             return invocation;
    79         }
    80     };

    从上可以看出在该handler中,定义了与客户端连接成功/断开连接/接受到客户端消息/相应消息,以及创造Invocation的方法。其中的getInvoker(Channel channel, Invocation inv)方法简码如下:

    1         String serviceKey = serviceKey(port, path, inv.getAttachments().get(Constants.VERSION_KEY), inv.getAttachments().get(Constants.GROUP_KEY));
    2         DubboExporter<?> exporter = (DubboExporter<?>) exporterMap.get(serviceKey);
    3         return exporter.getInvoker();

    这不就是我们刚刚放置到exporterMap中的DubboExporter,而其中的invoker不就是我们的“filter的invokerdelegete对象”。

    使用Exchangers.bind(providerUrl, ExchangeHandlerAdapter对象)创建ExchangeServer

     1     public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
     2         if (url == null) {
     3             throw new IllegalArgumentException("url == null");
     4         }
     5         if (handler == null) {
     6             throw new IllegalArgumentException("handler == null");
     7         }
     8         url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
     9         return getExchanger(url).bind(url, handler);
    10     }
    11 
    12     public static Exchanger getExchanger(URL url) {
    13         String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER);//header
    14         return getExchanger(type);
    15     }
    16 
    17     public static Exchanger getExchanger(String type) {
    18         return ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension(type);
    19     }

    getExchanger(URL url)返回一个HeaderExchanger实例。所以ExchangeServer的创建交由HeaderExchanger来实现。

    HeaderExchanger.bind(providerUrl, ExchangeHandlerAdapter对象) 

    1     /**
    2      * 1 对handler进行三次包装:首先将ExchangeHandlerAdapter赋给HeaderExchangeHandler中的ExchangeHandler handler属性;然后将创建出来的HeaderExchangeHandler赋给DecodeHandler的父类AbstractChannelHandlerDelegate的ChannelHandler handler属性
    3      */
    4     public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
    5         return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
    6     }

    说明:

    • 这里首先对传入的ExchangeHandlerAdapter进行了两次包装,最终得到DecodeHandler实例;
    • 之后,使用Transporters.bind(providerUrl, DecodeHandler对象)创建了一个NettyServer;
    • 最后使用HeaderExchangeServer包装了上边的NettyServer,并启动了心跳计数器。
      • HeaderExchangeServer实例也是最终返回的ExchangeServer实例,将最终被存储在Map<String, ExchangeServer> serverMap:{ "10.10.10.10:20880"<->HeaderExchangeServer实例 }

    包装ExchangeHandlerAdapter,获取DecodeHandler实例。代码比较简单,不列出来了。

    最终获取到的DecodeHandler实例的层级关系:

    1 DecodeHandler实例
    2 -->HeaderExchangeHandler实例
    3    -->ExchangeHandlerAdapter实例

    使用Transporters.bind(providerUrl, DecodeHandler对象)创建了一个NettyServer

    Transporters.bind(providerUrl, DecodeHandler对象)

     1     public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {
     2         if (url == null) {
     3             throw new IllegalArgumentException("url == null");
     4         }
     5         if (handlers == null || handlers.length == 0) {
     6             throw new IllegalArgumentException("handlers == null");
     7         }
     8         ChannelHandler handler;
     9         if (handlers.length == 1) {
    10             handler = handlers[0];
    11         } else {
    12             handler = new ChannelHandlerDispatcher(handlers);
    13         }
    14         return getTransporter().bind(url, handler);
    15     }
    16 
    17     public static Transporter getTransporter() {
    18         return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
    19     }

    Transporter$Adaptive.bind(providerUrl, DecodeHandler对象)

     1     public com.alibaba.dubbo.remoting.Server bind(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.remoting.RemotingException {
     2         if (arg0 == null)
     3             throw new IllegalArgumentException("url == null");
     4         com.alibaba.dubbo.common.URL url = arg0;
     5         String extName = url.getParameter("server", url.getParameter("transporter", "netty"));//netty
     6         if(extName == null)
     7             throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([server, transporter])");
     8         com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
     9         return extension.bind(arg0, arg1);
    10     }

    最后NettyServer的创建由NettyTransporter来创建。

    NettyTransporter.bind(providerUrl, DecodeHandler对象)

     1 public class NettyTransporter implements Transporter {
     2     public static final String NAME = "netty";
     3 
     4     public Server bind(URL url, ChannelHandler listener) throws RemotingException {
     5         return new NettyServer(url, listener);
     6     }
     7 
     8     public Client connect(URL url, ChannelHandler listener) throws RemotingException {
     9         return new NettyClient(url, listener);
    10     }
    11 }

    new NettyServer(providerUrl, DecodeHandler对象)

    1     public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
    2         super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
    3     }

    这里首先为providerUrl添加参数:threadname=DubboServerHandler-10.10.10.10:20880(ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME));

    之后,使用ChannelHandlers.wrap(DecodeHandler对象, providerUrl)对DecodeHandler对象进行了三层包装,最终得到MultiMessageHandler实例;

    最后调用父类的构造器初始化NettyServer的各个属性,最后启动netty。

    先看一下

    ChannelHandlers.wrap(DecodeHandler对象, providerUrl)

     1     /**
     2      * 这里又是层层包裹:
     3      * MultiMessageHandler
     4      * --HeartbeatHandler
     5      *   --AllChannelHandler
     6      *     --DecodeHandler
     7      *       --HeaderExchangeHandler
     8      *         --ExchangeHandlerAdapter
     9      * @param handler
    10      * @param url
    11      * @return
    12      */
    13     protected ChannelHandler wrapInternal(ChannelHandler handler, URL url) {
    14         return new MultiMessageHandler(new HeartbeatHandler(ExtensionLoader.getExtensionLoader(Dispatcher.class)
    15                 .getAdaptiveExtension().dispatch(handler, url)));
    16     }

    ExtensionLoader.getExtensionLoader(Dispatcher.class).getAdaptiveExtension()获取到一个Dispatcher$Adaptive适配类。

    Dispatcher$Adaptive.dispatch(DecodeHandler对象, providerUrl)

     1     public com.alibaba.dubbo.remoting.ChannelHandler dispatch(com.alibaba.dubbo.remoting.ChannelHandler arg0, com.alibaba.dubbo.common.URL arg1) {
     2         if (arg1 == null)
     3             throw new IllegalArgumentException("url == null");
     4         com.alibaba.dubbo.common.URL url = arg1;
     5         String extName = url.getParameter("dispatcher", url.getParameter("dispather", url.getParameter("channel.handler", "all")));//all
     6         if(extName == null)
     7             throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Dispatcher) name from url(" + url.toString() + ") use keys([dispatcher, dispather, channel.handler])");
     8         com.alibaba.dubbo.remoting.Dispatcher extension = (com.alibaba.dubbo.remoting.Dispatcher)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Dispatcher.class).getExtension(extName);
     9         return extension.dispatch(arg0, arg1);
    10     }

    这里获取到AllDispatcher,Dispatcher决定了dubbo的线程模型,指定了哪些做什么,哪些线程做什么。讲到dubbo通信的时候再写。

    AllDispatcher.dispatch(DecodeHandler对象, providerUrl)

    1     public ChannelHandler dispatch(ChannelHandler handler, URL url) {
    2         return new AllChannelHandler(handler, url);
    3     }

    new AllChannelHandler(DecodeHandler对象, providerUrl)

    1     public AllChannelHandler(ChannelHandler handler, URL url) {
    2         super(handler, url);
    3     }

    来看其父类的WrappedChannelHandler的构造器:

    WrappedChannelHandler(DecodeHandler对象, providerUrl)

     1     protected static final ExecutorService SHARED_EXECUTOR = Executors.newCachedThreadPool(new NamedThreadFactory("DubboSharedHandler", true));
     2     protected final ExecutorService executor;
     3     protected final ChannelHandler handler;
     4     protected final URL url;
     5 
     6     public WrappedChannelHandler(ChannelHandler handler, URL url) {
     7         this.handler = handler;
     8         this.url = url;
     9         executor = (ExecutorService) ExtensionLoader.getExtensionLoader(ThreadPool.class).getAdaptiveExtension().getExecutor(url);
    10 
    11         String componentKey = Constants.EXECUTOR_SERVICE_COMPONENT_KEY;
    12         if (Constants.CONSUMER_SIDE.equalsIgnoreCase(url.getParameter(Constants.SIDE_KEY))) {
    13             componentKey = Constants.CONSUMER_SIDE;
    14         }
    15         DataStore dataStore = ExtensionLoader.getExtensionLoader(DataStore.class).getDefaultExtension();
    16         dataStore.put(componentKey, Integer.toString(url.getPort()), executor);//{"java.util.concurrent.ExecutorService":{"20880":executor}}
    17     }

    首先创建了一个共享线程池:SHARED_EXECUTOR;

    之后为handler/url/executor赋值,其中executor是一个200个线程数的fixed线程池(队列为0,即同步队列);

     1     public Executor getExecutor(URL url) {
     2         String name = url.getParameter(Constants.THREAD_NAME_KEY, Constants.DEFAULT_THREAD_NAME);//默认为dubbo,但是我们这里是DubboServerHandler-10.10.10.10:20880(就是之前设置到url上的threadname)
     3         int threads = url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS);//200
     4         int queues = url.getParameter(Constants.QUEUES_KEY, Constants.DEFAULT_QUEUES);//0
     5         return new ThreadPoolExecutor(threads, threads, 0, TimeUnit.MILLISECONDS,
     6                 queues == 0 ? new SynchronousQueue<Runnable>() :
     7                         (queues < 0 ? new LinkedBlockingQueue<Runnable>()
     8                                 : new LinkedBlockingQueue<Runnable>(queues)),
     9                 new NamedThreadFactory(name, true), new AbortPolicyWithReport(name, url));
    10     }

    之后获取了一个数据存储器:SimpleDataStore;

    最后将{"java.util.concurrent.ExecutorService":{"20880": executor}}数据存储在SimpleDataStore的ConcurrentMap<String, ConcurrentMap<String, Object>> data数据结构中。也就是说:每一个端口,有一个线程池。

    注意:为什么SimpleDataSource可以做缓存来使用?

     1     public T getExtension(String name) {
     2         if (name == null || name.length() == 0)
     3             throw new IllegalArgumentException("Extension name == null");
     4         if ("true".equals(name)) {
     5             return getDefaultExtension();
     6         }
     7         Holder<Object> holder = cachedInstances.get(name);
     8         if (holder == null) {
     9             cachedInstances.putIfAbsent(name, new Holder<Object>());
    10             holder = cachedInstances.get(name);
    11         }
    12         Object instance = holder.get();
    13         if (instance == null) {
    14             synchronized (holder) {
    15                 instance = holder.get();
    16                 if (instance == null) {
    17                     instance = createExtension(name);
    18                     holder.set(instance);
    19                 }
    20             }
    21         }
    22         return (T) instance;
    23     }

    其实,就是这样SimpleDataStore实例会存储在cachedInstances缓存中,下一次不会再创建,而是直接获取该缓存。

    这样之后,一个AllChannelHandler实例就完成了,该实例属性如下:

    • WrappedChannelHandler.url:dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&channel.readonly.sent=true&codec=dubbo&dubbo=2.0.0&generic=false&heartbeat=60000&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=1287&side=provider&threadname=DubboServerHandler-10.10.10.10:20880&timestamp=1507116859919
    • WrappedChannelHandler.handler:DecodeHandler对象
    • WrappedChannelHandler.executor:FixedThreadPool实例

    当然还有一个类变量WrappedChannelHandler.SHARED_EXECUTOR=CachedThreadPool实例。

    之后AllChannelHandler实例会被HeartbeatHandler进行包裹,之后HeartbeatHandler实例又会被MultiMessageHandler所包裹,最后得到的MultiMessageHandler实例的层级结构如下:

    1 MultiMessageHandler
    2 -->handler: HeartbeatHandler
    3    -->handler: AllChannelHandler
    4          -->url: providerUrl
    5          -->executor: FixedExecutor
    6          -->handler: DecodeHandler
    7             -->handler: HeaderExchangeHandler
    8                -->handler: ExchangeHandlerAdapter

    MultiMessageHandler实例创建出来之后,NettyServer就开始调用其各个父类进行属性的初始化了。首先来看一下NettyServer的父类层级图:

    AbstractServer:

     1     protected static final String SERVER_THREAD_POOL_NAME = "DubboServerHandler";
     2     ExecutorService executor;
     3     private InetSocketAddress localAddress;
     4     private InetSocketAddress bindAddress;
     5     private int accepts;
     6     private int idleTimeout = 600;
     7 
     8     public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
     9         super(url, handler);
    10         localAddress = getUrl().toInetSocketAddress();
    11         String host = url.getParameter(Constants.ANYHOST_KEY, false)
    12                 || NetUtils.isInvalidLocalHost(getUrl().getHost())
    13                 ? NetUtils.ANYHOST : getUrl().getHost();
    14         bindAddress = new InetSocketAddress(host, getUrl().getPort());
    15         this.accepts = url.getParameter(Constants.ACCEPTS_KEY, Constants.DEFAULT_ACCEPTS);
    16         this.idleTimeout = url.getParameter(Constants.IDLE_TIMEOUT_KEY, Constants.DEFAULT_IDLE_TIMEOUT);
    17         try {
    18             doOpen();
    19             if (logger.isInfoEnabled()) {
    20                 logger.info("Start " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress());
    21             }
    22         } catch (Throwable t) {
    23             throw new RemotingException(url.toInetSocketAddress(), null, "Failed to bind " + getClass().getSimpleName()
    24                     + " on " + getLocalAddress() + ", cause: " + t.getMessage(), t);
    25         }
    26         //fixme replace this with better method
    27         DataStore dataStore = ExtensionLoader.getExtensionLoader(DataStore.class).getDefaultExtension();
    28         executor = (ExecutorService) dataStore.get(Constants.EXECUTOR_SERVICE_COMPONENT_KEY, Integer.toString(url.getPort()));
    29     }

    首先调用父类初始化属性,之后启动服务。

    AbstractEndpoint:

     1     private Codec2 codec;
     2     private int timeout;
     3     private int connectTimeout;
     4 
     5     public AbstractEndpoint(URL url, ChannelHandler handler) {
     6         super(url, handler);
     7         this.codec = getChannelCodec(url);
     8         this.timeout = url.getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);//1000
     9         this.connectTimeout = url.getPositiveParameter(Constants.CONNECT_TIMEOUT_KEY, Constants.DEFAULT_CONNECT_TIMEOUT);//3000
    10     }

    AbstractPeer:

     1     private final ChannelHandler handler;
     2     private volatile URL url;
     3 
     4     public AbstractPeer(URL url, ChannelHandler handler) {
     5         if (url == null) {
     6             throw new IllegalArgumentException("url == null");
     7         }
     8         if (handler == null) {
     9             throw new IllegalArgumentException("handler == null");
    10         }
    11         this.url = url;
    12         this.handler = handler;
    13     }

    来看一下最后初始化好的NettyServer实例:

    • url:providerUrl(dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&channel.readonly.sent=true&codec=dubbo&dubbo=2.0.0&generic=false&heartbeat=60000&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=1287&side=provider&timestamp=1507116859919)
    • handler:MultiMessageHandler实例
    • codec:DubboCountCodec实例
    • timeout:1000
    • connectTimeout:3000
    • idleTime:600*1000
    • localAddress:10.10.10.10:20880
    • bindAddress:0.0.0.0:20880
    • accepts:0
    • executor:null(此时的executor还没实例话,要等netty服务起来之后才会从缓存中获取之前存储在SimpleDataStore缓存中的那个200个线程数的FixedThreadPool实例)

    之后,就要启动netty服务了。

     1     /**
     2      * 启动netty服务,监听客户端连接
     3      */
     4     @Override
     5     protected void doOpen() throws Throwable {
     6         NettyHelper.setNettyLoggerFactory();
     7         ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true));
     8         ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true));
     9         ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS));
    10         bootstrap = new ServerBootstrap(channelFactory);
    11 
    12         final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
    13         channels = nettyHandler.getChannels();
    14         // https://issues.jboss.org/browse/NETTY-365
    15         // https://issues.jboss.org/browse/NETTY-379
    16         // final Timer timer = new HashedWheelTimer(new NamedThreadFactory("NettyIdleTimer", true));
    17         bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
    18             public ChannelPipeline getPipeline() {
    19                 NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
    20                 ChannelPipeline pipeline = Channels.pipeline();
    21                 /*int idleTimeout = getIdleTimeout();
    22                 if (idleTimeout > 10000) {
    23                     pipeline.addLast("timer", new IdleStateHandler(timer, idleTimeout / 1000, 0, 0));
    24                 }*/
    25                 pipeline.addLast("decoder", adapter.getDecoder());
    26                 pipeline.addLast("encoder", adapter.getEncoder());
    27                 pipeline.addLast("handler", nettyHandler);
    28                 return pipeline;
    29             }
    30         });
    31         // bind
    32         channel = bootstrap.bind(getBindAddress());
    33     }

    说明:

    • boss线程数默认只有一个;
    • worker线程数:Runtime.getRuntime().availableProcessors() + 1,为计算机核数+1;
    • 服务端逻辑处理器为NettyHandler:
    • 编码器为:InternalEncoder实例,内部使用NettyServer的DubboCountCodec实例来编码
    • 解码器为:InternalDecoder实例,内部使用NettyServer的DubboCountCodec实例来解码

     NettyHandler:

     1 @Sharable
     2 public class NettyHandler extends SimpleChannelHandler {
     3     private final Map<String, Channel> channels = new ConcurrentHashMap<String, Channel>(); // <ip:port, channel>
     4     private final URL url;
     5     private final ChannelHandler handler;
     6 
     7     public NettyHandler(URL url, ChannelHandler handler) {
     8         if (url == null) {
     9             throw new IllegalArgumentException("url == null");
    10         }
    11         if (handler == null) {
    12             throw new IllegalArgumentException("handler == null");
    13         }
    14         this.url = url;
    15         this.handler = handler;
    16     }
    17 
    18     public Map<String, Channel> getChannels() {
    19         return channels;
    20     }
    21 
    22     @Override
    23     public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
    24         NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
    25         try {
    26             if (channel != null) {
    27                 channels.put(NetUtils.toAddressString((InetSocketAddress) ctx.getChannel().getRemoteAddress()), channel);
    28             }
    29             handler.connected(channel);
    30         } finally {
    31             NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
    32         }
    33     }
    34 
    35     @Override
    36     public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
    37         NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
    38         try {
    39             channels.remove(NetUtils.toAddressString((InetSocketAddress) ctx.getChannel().getRemoteAddress()));
    40             handler.disconnected(channel);
    41         } finally {
    42             NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
    43         }
    44     }
    45 
    46     @Override
    47     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
    48         NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
    49         try {
    50             handler.received(channel, e.getMessage());
    51         } finally {
    52             NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
    53         }
    54     }
    55 
    56     @Override
    57     public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
    58         super.writeRequested(ctx, e);
    59         NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
    60         try {
    61             handler.sent(channel, e.getMessage());
    62         } finally {
    63             NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
    64         }
    65     }
    66 
    67     @Override
    68     public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
    69         NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
    70         try {
    71             handler.caught(channel, e.getCause());
    72         } finally {
    73             NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
    74         }
    75     }
    76 }

    说明:

    属性

    • handler:就是当前的NettyServer实例
    • url:providerUrl
    • channels:存放连接到来的channel

    监听连接完成/连接断开/接收到消息/发送完消息/异常捕捉事件,之后使用NettyServer实例进行相应的处理,NettyServer又会调用MultiMessageHandler实例(该handler属性位于NettyServer的父类AbstractPeer中)进行处理。

    在来看编码器和解码器:

    NettyCodecAdapter(DubboCountCodec实例, providerUrl, 当前的NettyServer实例)

     1 final class NettyCodecAdapter {
     2     private final ChannelHandler encoder = new InternalEncoder();
     3     private final ChannelHandler decoder = new InternalDecoder();
     4     private final Codec2 codec;
     5     private final URL url;
     6     private final int bufferSize;
     7     private final com.alibaba.dubbo.remoting.ChannelHandler handler;
     8 
     9     public NettyCodecAdapter(Codec2 codec, URL url, com.alibaba.dubbo.remoting.ChannelHandler handler) {
    10         this.codec = codec;
    11         this.url = url;
    12         this.handler = handler;
    13         int b = url.getPositiveParameter(Constants.BUFFER_KEY, Constants.DEFAULT_BUFFER_SIZE);//8*1024
    14         this.bufferSize = b >= Constants.MIN_BUFFER_SIZE && b <= Constants.MAX_BUFFER_SIZE ? b : Constants.DEFAULT_BUFFER_SIZE;//8*1024
    15     }
    16 
    17     public ChannelHandler getEncoder() {
    18         return encoder;
    19     }
    20 
    21     public ChannelHandler getDecoder() {
    22         return decoder;
    23     }
    24 
    25     @Sharable
    26     private class InternalEncoder extends OneToOneEncoder {
    27         @Override
    28         protected Object encode(ChannelHandlerContext ctx, Channel ch, Object msg) throws Exception {
    29             ...
    30             codec.encode(channel, buffer, msg);
    31             ...
    32         }
    33     }
    34 
    35     private class InternalDecoder extends SimpleChannelUpstreamHandler {
    36         @Override
    37         public void messageReceived(ChannelHandlerContext ctx, MessageEvent event) throws Exception {
    38            ...
    39             msg = codec.decode(channel, message);
    40            ...
    41         }
    42         ...
    43     }
    44 }

    只列出核心代码:可以看到,InternalEncoder实例和InternalDecoder实例内部还是使用NettyServer的DubboCountCodec实例来编解码的。dubbo的编解码做的非常好,后续会写。

    到此为止,NettyServer就创建成功了。 之后,终于执行到了:

    new HeaderExchangeServer(Server NettyServer)

     1     private final ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(1, new NamedThreadFactory("dubbo-remoting-server-heartbeat",true));
     2     private final Server server;
     3     // 心跳定时器
     4     private ScheduledFuture<?> heatbeatTimer;
     5     // 心跳超时,毫秒。缺省0,不会执行心跳。
     6     private int heartbeat;
     7     private int heartbeatTimeout;
     8     private AtomicBoolean closed = new AtomicBoolean(false);
     9 
    10     public HeaderExchangeServer(Server server) {
    11         if (server == null) {
    12             throw new IllegalArgumentException("server == null");
    13         }
    14         this.server = server;
    15         this.heartbeat = server.getUrl().getParameter(Constants.HEARTBEAT_KEY, 0);//60000 在createServer(URL providerUrl)中拼接了heartbeat参数
    16         this.heartbeatTimeout = server.getUrl().getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3);//3*60000
    17         if (heartbeatTimeout < heartbeat * 2) {
    18             throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");
    19         }
    20         startHeatbeatTimer();
    21     }

    说明:

    • 属性
      • scheduled:是一个有1个名字为dubbo-remoting-server-heartbeat的后台线程的定时线程池;
      • server:之前创建出来的NettyServer实例;
      • heartbeatTimer:心跳计时器
      • heartbeat:心跳时间,该参数会在HeaderExchangeServer的构造器中进行赋值,60000
      • heartbeatTimeout:心跳超时时间,超过该时间,会进行channel重连,180000
    • 启动心跳计时器

    startHeatbeatTimer()

     1     private void startHeatbeatTimer() {
     2         stopHeartbeatTimer();
     3         if (heartbeat > 0) {
     4             heatbeatTimer = scheduled.scheduleWithFixedDelay(
     5                     new HeartBeatTask(new HeartBeatTask.ChannelProvider() {
     6                         public Collection<Channel> getChannels() {
     7                             return Collections.unmodifiableCollection(HeaderExchangeServer.this.getChannels());
     8                         }
     9                     }, heartbeat, heartbeatTimeout),
    10                     heartbeat,
    11                     heartbeat,
    12                     TimeUnit.MILLISECONDS);
    13         }
    14     }
    15 
    16     private void stopHeartbeatTimer() {
    17         try {
    18             ScheduledFuture<?> timer = heatbeatTimer;
    19             if (timer != null && !timer.isCancelled()) {
    20                 timer.cancel(true);
    21             }
    22         } catch (Throwable t) {
    23             logger.warn(t.getMessage(), t);
    24         } finally {
    25             heatbeatTimer = null;
    26         }
    27     }

    首先停掉之前的计时器,之后在线程创建开始heartbeat毫秒(60s)后执行第一次HeartBeatTask任务,之后每隔heartbeat毫秒(60s)执行一次HeartBeatTask任务。来看一下HeartBeatTask:

    HeartBeatTask

     1 final class HeartBeatTask implements Runnable {
     2     private ChannelProvider channelProvider;
     3     private int heartbeat;//60s
     4     private int heartbeatTimeout;//180s
     5 
     6     HeartBeatTask(ChannelProvider provider, int heartbeat, int heartbeatTimeout) {
     7         this.channelProvider = provider;
     8         this.heartbeat = heartbeat;
     9         this.heartbeatTimeout = heartbeatTimeout;
    10     }
    11 
    12     public void run() {
    13         try {
    14             long now = System.currentTimeMillis();
    15             for (Channel channel : channelProvider.getChannels()) {
    16                 if (channel.isClosed()) {
    17                     continue;
    18                 }
    19                 try {
    20                     Long lastRead = (Long) channel.getAttribute(HeaderExchangeHandler.KEY_READ_TIMESTAMP);//"READ_TIMESTAMP"
    21                     Long lastWrite = (Long) channel.getAttribute(HeaderExchangeHandler.KEY_WRITE_TIMESTAMP);//"WRITE_TIMESTAMP"
    22                     //如果最后一次读和写在heartbeat时间(60s)内,则最后一次的读和写本身可以看作心跳;否则,需要程序发送心跳
    23                     if ((lastRead != null && now - lastRead > heartbeat)
    24                             || (lastWrite != null && now - lastWrite > heartbeat)) {
    25                         Request req = new Request();
    26                         req.setVersion("2.0.0");
    27                         req.setTwoWay(true);
    28                         req.setEvent(Request.HEARTBEAT_EVENT);
    29                         channel.send(req);
    30                         if (logger.isDebugEnabled()) {
    31                             logger.debug("Send heartbeat to remote channel " + channel.getRemoteAddress()
    32                                     + ", cause: The channel has no data-transmission exceeds a heartbeat period: " + heartbeat + "ms");
    33                         }
    34                     }
    35                     //如果最后一次读的时间距离现在已经超过heartbeatTimeout了,我们认为channel已经断了(因为在这个过程中,发送了三次心跳都没反应),此时channel进行重连
    36                     if (lastRead != null && now - lastRead > heartbeatTimeout) {
    37                         logger.warn("Close channel " + channel
    38                                 + ", because heartbeat read idle time out: " + heartbeatTimeout + "ms");
    39                         if (channel instanceof Client) {
    40                             try {
    41                                 ((Client) channel).reconnect();
    42                             } catch (Exception e) {
    43                                 //do nothing
    44                             }
    45                         } else {
    46                             channel.close();
    47                         }
    48                     }
    49                 } catch (Throwable t) {
    50                     logger.warn("Exception when heartbeat to remote channel " + channel.getRemoteAddress(), t);
    51                 }
    52             }
    53         } catch (Throwable t) {
    54             logger.warn("Unhandled exception when heartbeat, cause: " + t.getMessage(), t);
    55         }
    56     }
    57 
    58     interface ChannelProvider {
    59         Collection<Channel> getChannels();
    60     }
    61 }

    说明:

    • 属性
      • channelProvider在startHeatbeatTimer()中创建,并且获取了当前的HeaderExchangeServer的所有channels
      • heartbeat:60s
      • heartbeatTimeout:180s
    • run()
      • 如果最后一次读和写的时间距离现在在heartbeat时间(60s)内,则最后一次的读和写本身可以看作心跳;否则,发送心跳
      • 如果最后一次读的时间距离现在已经超过heartbeatTimeout了,认为channel已经断了(因为在这个过程中,发送了三次心跳都没反应),此时channel进行重连

    到现在一个完整的ExchangeServer就OK了。之后我们将创建出来的ExchangeServer实例存放在DubboProtocol的Map<String, ExchangeServer> serverMap属性中:

    { "10.10.10.10:20880" : ExchangeServer实例 }

    最后,DubboProtocol.export(Invoker<T> invoker)将之前创建的DubboExporter实例返回。

    2.4  创建RegistryProtocol.ExporterChangeableWrapper来封装Exporter和originInvoker

    1 exporter = new ExporterChangeableWrapper<T>((Exporter<T>) protocol.export(invokerDelegete), originInvoker)
     1     private class ExporterChangeableWrapper<T> implements Exporter<T> {
     2         private final Invoker<T> originInvoker;
     3         private Exporter<T> exporter;
     4 
     5         public ExporterChangeableWrapper(Exporter<T> exporter, Invoker<T> originInvoker) {
     6             this.exporter = exporter;
     7             this.originInvoker = originInvoker;
     8         }
     9 
    10         public Invoker<T> getOriginInvoker() {
    11             return originInvoker;
    12         }
    13 
    14         public Invoker<T> getInvoker() {
    15             return exporter.getInvoker();
    16         }
    17 
    18         public void setExporter(Exporter<T> exporter) {
    19             this.exporter = exporter;
    20         }
    21 
    22         public void unexport() {
    23             String key = getCacheKey(this.originInvoker);
    24             bounds.remove(key);
    25             exporter.unexport();
    26         }
    27     }

    ExporterChangeableWrapper类是RegistryProtocol的私有内部类

    最后,将<providerUrl, ExporterChangeableWrapper实例>放入RegistryProtocol的属性Map<String, ExporterChangeableWrapper<?>> bounds中。

    • key:dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=744&side=provider&timestamp=1507176748026
    • value:RegistryProtocol$ExporterChangeableWrapper实例
      • originInvoker:即AbstractProxyInvoker实例属性如下:
        • proxy:DemoServiceImpl实例
        • type:Class<com.alibaba.dubbo.demo.DemoService>
        • url:registry://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&client=curator&dubbo=2.0.0&export=dubbo%3A%2F%2F10.10.10.10%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26dubbo%3D2.0.0%26generic%3Dfalse%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D993%26side%3Dprovider%26timestamp%3D1507100322516&pid=993&registry=zookeeper&timestamp=1507100319830
      • DubboExporter实例
        • key:com.alibaba.dubbo.demo.DemoService:20880
        • invoker:"InvokerDelegete的filter对象"
        • exporterMap:{ "com.alibaba.dubbo.demo.DemoService:20880" -> 当前的DubboExporter实例 }

    到此为止,RegistryProtocol.export(final Invoker<T> originInvoker)的第一行代码就完成了。

     1     public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
     2         //export invoker
     3         final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
     4         //registry provider
     5         final Registry registry = getRegistry(originInvoker);
     6         final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
     7         registry.register(registedProviderUrl);
     8         // 订阅override数据
     9         // FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,因为subscribed以服务名为缓存的key,导致订阅信息覆盖。
    10         final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);
    11         final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
    12         overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
    13         registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
    14         //保证每次export都返回一个新的exporter实例
    15         return new Exporter<T>() {
    16             public Invoker<T> getInvoker() {
    17                 return exporter.getInvoker();
    18             }
    19 
    20             public void unexport() {
    21                 try {
    22                     exporter.unexport();
    23                 } catch (Throwable t) {
    24                     logger.warn(t.getMessage(), t);
    25                 }
    26                 try {
    27                     registry.unregister(registedProviderUrl);
    28                 } catch (Throwable t) {
    29                     logger.warn(t.getMessage(), t);
    30                 }
    31                 try {
    32                     overrideListeners.remove(overrideSubscribeUrl);
    33                     registry.unsubscribe(overrideSubscribeUrl, overrideSubscribeListener);
    34                 } catch (Throwable t) {
    35                     logger.warn(t.getMessage(), t);
    36                 }
    37             }
    38         };
    39     }

    7.5 zookeeper客户端curator的基本使用 + zkui

    使用zookeeper原生API实现一些复杂的东西比较麻烦。所以,出现了两款比较好的开源客户端,对zookeeper的原生API进行了包装:zkClient和curator。后者是Netflix出版的,必属精品,也是最好用的zk的开源客户端。

    一  curator基本API使用

    引入依赖:

    1         <dependency>
    2             <groupId>org.apache.curator</groupId>
    3             <artifactId>curator-framework</artifactId>
    4             <version>2.12.0</version>
    5         </dependency>

    该依赖引入后,默认引入的zookeeper版本是3.4.8。

    注意:不要引入>=3.0.0的curator-framework,默认引入的zookeeper版本是3.5.x(该版本还不稳定),目前测试起来还是有点问题的。

    完整代码:

     1 package com.hulk.curator;
     2 
     3 import org.apache.curator.framework.CuratorFramework;
     4 import org.apache.curator.framework.CuratorFrameworkFactory;
     5 import org.apache.curator.framework.api.BackgroundCallback;
     6 import org.apache.curator.framework.api.CuratorEvent;
     7 import org.apache.curator.retry.ExponentialBackoffRetry;
     8 import org.apache.zookeeper.CreateMode;
     9 import org.apache.zookeeper.data.Stat;
    10 
    11 import java.util.concurrent.Executors;
    12 
    13 public class CuratorTest {
    14     private static CuratorFramework client = CuratorFrameworkFactory.builder()
    15             .connectString("10.211.55.4:2181")
    16             .sessionTimeoutMs(50000)
    17             .connectionTimeoutMs(30000)
    18             .retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
    19 
    20     public static void main(String[] args) throws Exception {
    21         /**
    22          * 创建会话
    23          */
    24         client.start();
    25 
    26         /**
    27          * 创建节点
    28          * 注意:
    29          * 1 除非指明创建节点的类型,默认是持久节点
    30          * 2 ZooKeeper规定:所有非叶子节点都是持久节点,所以递归创建出来的节点,只有最后的数据节点才是指定类型的节点,其父节点是持久节点
    31          */
    32         client.create().forPath("/China");//创建一个初始内容为空的节点
    33         client.create().forPath("/America", "zhangsan".getBytes());
    34         client.create().withMode(CreateMode.EPHEMERAL).forPath("/France");//创建一个初始内容为空的临时节点
    35         client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/Russia/car", "haha".getBytes());//递归创建,/Russia是持久节点
    36 
    37         /**
    38          * 异步创建节点
    39          * 注意:如果自己指定了线程池,那么相应的操作就会在线程池中执行,如果没有指定,那么就会使用Zookeeper的EventThread线程对事件进行串行处理
    40          */
    41         client.create().withMode(CreateMode.EPHEMERAL).inBackground(new BackgroundCallback() {
    42             @Override
    43             public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
    44                 System.out.println("当前线程:" + Thread.currentThread().getName() + ",code:" + event.getResultCode()
    45                                    + ",type:" + event.getType());
    46             }
    47         }, Executors.newFixedThreadPool(10)).forPath("/async-curator-my");
    48 
    49         client.create().withMode(CreateMode.EPHEMERAL).inBackground(new BackgroundCallback() {
    50             @Override
    51             public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
    52                 System.out.println("当前线程:" + Thread.currentThread().getName() + ",code:" + event.getResultCode()
    53                                    + ",type:" + event.getType());
    54             }
    55         }).forPath("/async-curator-zookeeper");
    56 
    57         /**
    58          * 获取节点内容
    59          */
    60         byte[] data = client.getData().forPath("/America");
    61         System.out.println(new String(data));
    62         byte[] data2 = client.getData().storingStatIn(new Stat()).forPath("/America"); //传入一个旧的stat变量,来存储服务端返回的最新的节点状态信息
    63         System.out.println(new String(data2));
    64         /**
    65          * 更新数据
    66          */
    67         Stat stat = client.setData().forPath("/America");
    68         client.setData().withVersion(4).forPath("/America", "lisi".getBytes());
    69 
    70         /**
    71          * 删除节点
    72          */
    73         client.delete().forPath("/China");//只能删除叶子节点
    74         client.delete().deletingChildrenIfNeeded().forPath("/Russia");//删除一个节点,并递归删除其所有子节点
    75         client.delete().withVersion(5).forPath("/America");//强制指定版本进行删除
    76         client.delete().guaranteed().forPath("/America");//注意:由于一些网络原因,上述的删除操作有可能失败,使用guaranteed(),如果删除失败,会记录下来,只要会话有效,就会不断的重试,直到删除成功为止
    77 
    78         Thread.sleep(Integer.MAX_VALUE);
    79     }
    80 }

    1  创建会话

    curator创建会话有两种方式,推荐流式API。

    1 CuratorFramework client = CuratorFrameworkFactory.builder()
    2             .connectString("10.211.55.4:2181")
    3             .sessionTimeoutMs(50000)
    4             .connectionTimeoutMs(30000)
    5             .retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();

    参数:

    • connectString:zk的server地址,多个server之间使用英文逗号分隔开
    • connectionTimeoutMs:连接超时时间,如上是30s,默认是15s
    • sessionTimeoutMs:会话超时时间,如上是50s,默认是60s
    • retryPolicy:失败重试策略
      • ExponentialBackoffRetry:构造器含有三个参数 ExponentialBackoffRetry(int baseSleepTimeMs, int maxRetries, int maxSleepMs)
        • baseSleepTimeMs:初始的sleep时间,用于计算之后的每次重试的sleep时间,
          • 计算公式:当前sleep时间=baseSleepTimeMs*Math.max(1, random.nextInt(1<<(retryCount+1)))
        • maxRetries:最大重试次数
        • maxSleepMs:最大sleep时间,如果上述的当前sleep计算出来比这个大,那么sleep用这个时间
      • 其他,查看org.apache.curator.RetryPolicy接口的实现类

    此时会话还没创建,使用如下代码创建会话:

    1 client.start();

    start()会阻塞到会话创建成功为止。

    2  创建节点

    2.1  同步创建

    1         client.create().forPath("/China");//创建一个初始内容为空的节点
    2         client.create().forPath("/America", "zhangsan".getBytes());
    3         client.create().withMode(CreateMode.EPHEMERAL).forPath("/France");//创建一个初始内容为空的临时节点
    4         client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/Russia/car", "haha".getBytes());//递归创建,/Russia是持久节点

    注意:

    • 除非指明创建节点的类型,默认是持久节点
    • ZooKeeper规定:所有非叶子节点都是持久节点,所以递归创建出来的节点,只有最后的数据节点才是指定类型的节点,其父节点是持久节点
    • creatingParentsIfNeeded():可以实现递归创建

    2.2  异步创建

     1         client.create().withMode(CreateMode.EPHEMERAL).inBackground(new BackgroundCallback() {
     2             @Override
     3             public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
     4                 System.out.println("当前线程:" + Thread.currentThread().getName() + ",code:" + event.getResultCode()
     5                                    + ",type:" + event.getType());
     6             }
     7         }, Executors.newFixedThreadPool(10)).forPath("/async-curator-my");
     8 
     9         client.create().withMode(CreateMode.EPHEMERAL).inBackground(new BackgroundCallback() {
    10             @Override
    11             public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
    12                 System.out.println("当前线程:" + Thread.currentThread().getName() + ",code:" + event.getResultCode()
    13                                    + ",type:" + event.getType());
    14             }
    15         }).forPath("/async-curator-zookeeper");

    注意:

    • 在curator中所有异步操作,都使用org.apache.curator.framework.api.BackgroundCallback接口的实现类完成
    • 如果在BackgroundCallback中自己指定了线程池,那么相应的操作就会在线程池中执行,如果没有指定,那么就会使用Zookeeper的EventThread线程对事件进行串行处理,所以上述的两个输出分别如下:
      当前线程:pool-3-thread-1,code:0,type:CREATE
      当前线程:main-EventThread,code:0,type:CREATE

    3  获取节点内容

    1         byte[] data = client.getData().forPath("/America");
    2         System.out.println(new String(data));
    3         byte[] data2 = client.getData().storingStatIn(new Stat()).forPath("/America"); //传入一个旧的stat变量,来存储服务端返回的最新的节点状态信息
    4         System.out.println(new String(data2));

    4  获取节点子节点列表

    1 List<String> children = client.getChildren().forPath("/Russia");

    5  更新数据

    1         Stat stat = client.setData().forPath("/America");
    2         client.setData().withVersion(4).forPath("/America", "lisi".getBytes());

    注意:

    • version版本号还是为了实现CAS并发处理,也会强制某个线程必须更新相应的版本的数据

    6  删除节点

    1         client.delete().forPath("/China");//只能删除叶子节点
    2         client.delete().deletingChildrenIfNeeded().forPath("/Russia");//删除一个节点,并递归删除其所有子节点
    3         client.delete().withVersion(5).forPath("/America");//强制指定版本进行删除
    4         client.delete().guaranteed().forPath("/America");

    注意:

    • deletingChildrenIfNeeded()实现级联删除
    • guaranteed()由于一些网络原因,上述的删除操作有可能失败,使用guaranteed(),如果删除失败,会记录下来,只要会话有效,就会不断的重试,直到删除成功为止

    二  curator实现事件监听

    引入两个依赖:

     1         <dependency>
     2             <groupId>org.apache.curator</groupId>
     3             <artifactId>curator-framework</artifactId>
     4             <version>2.12.0</version>
     5         </dependency>
     6         <dependency>
     7             <groupId>org.apache.curator</groupId>
     8             <artifactId>curator-recipes</artifactId>
     9             <version>2.12.0</version>
    10         </dependency>

    给出全部代码:

     1 package com.hulk.curator;
     2 
     3 import org.apache.curator.framework.CuratorFramework;
     4 import org.apache.curator.framework.CuratorFrameworkFactory;
     5 import org.apache.curator.framework.recipes.cache.NodeCache;
     6 import org.apache.curator.framework.recipes.cache.NodeCacheListener;
     7 import org.apache.curator.framework.recipes.cache.PathChildrenCache;
     8 import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
     9 import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
    10 import org.apache.curator.retry.ExponentialBackoffRetry;
    11 
    12 /**
    13  * 事件监听器
    14  */
    15 public class CuratorWatcherTest {
    16     private static CuratorFramework client = CuratorFrameworkFactory.builder()
    17             .connectString("10.211.55.4:2181")
    18             .sessionTimeoutMs(50000)
    19             .connectionTimeoutMs(30000)
    20             .retryPolicy(new ExponentialBackoffRetry(1000, 3))
    21             .build();
    22 
    23     public static void main(String[] args) throws Exception {
    24         /**
    25          * 创建会话
    26          */
    27         client.start();
    28         client.create().creatingParentsIfNeeded().forPath("/book/computer","java".getBytes());
    29         /**
    30          * 监听指定节点本身的变化,包括节点本身的创建和节点本身数据的变化
    31          */
    32         NodeCache nodeCache = new NodeCache(client,"/book/computer");
    33         nodeCache.getListenable().addListener(new NodeCacheListener() {
    34             @Override
    35             public void nodeChanged() throws Exception {
    36                 System.out.println("新的节点数据:" + new String(nodeCache.getCurrentData().getData()));
    37             }
    38         });
    39         nodeCache.start(true);
    40 
    41         client.setData().forPath("/book/computer","c++".getBytes());
    42         /**
    43          * 监听子节点变化情况
    44          * 1 新增子节点
    45          * 2 删除子节点
    46          * 3 子节点数据变更
    47          */
    48         PathChildrenCache pathChildrenCache = new PathChildrenCache(client,"/book13",true);
    49         pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
    50             @Override
    51             public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
    52                 switch (event.getType()){
    53                     case CHILD_ADDED:
    54                         System.out.println("新增子节点:" + event.getData().getPath());
    55                         break;
    56                     case CHILD_UPDATED:
    57                         System.out.println("子节点数据变化:" + event.getData().getPath());
    58                         break;
    59                     case CHILD_REMOVED:
    60                         System.out.println("删除子节点:" + event.getData().getPath());
    61                         break;
    62                     default:
    63                         break;
    64                 }
    65             }
    66         });
    67         pathChildrenCache.start();
    68 
    69         client.create().forPath("/book13");
    70 
    71         client.create().forPath("/book13/car", "bmw".getBytes());
    72 
    73         client.setData().forPath("/book13/car", "audi".getBytes());
    74 
    75         client.delete().forPath("/book13/car");
    76     }
    77 }

    curator的事件监听分为:

    • NodeCache:对节点本身的监听
      • 监听节点本身的创建
      • 监听节点本身的数据的变化
    • PathChildrenCache:对节点的子节点的监听
      • 监听新增子节点
      • 监听删除子节点
      • 监听子节点数据变化

    注意

    • PathChildrenCache只会监听指定节点的一级子节点,不会监听节点本身(例如:“/book13”),也不会监听子节点的子节点(例如,“/book13/car/color”)

    三  zkui

    zk的操作我们一般可以登上zk所在的机器,然后执行“sh zkCli.sh”,之后执行一些命令,但是由于这样始终效率低下,这里推荐一款比较好用的zk的ui界面:zkui。

    假设我们要在10.211.55.5机器上安装该程序。

    1  下载打包

    1 git clone https://github.com/DeemOpen/zkui.git
    2 cd zkui/
    3 mvn clean install

    通过上述的操作,在zkui/target目录下我们会生成一个fatjar:zkui-2.0-SNAPSHOT-jar-with-dependencies.jar,在启动这个jar之前先要进行相关配置。

    2  配置zkui

    1 cp config.cfg target/
    2 vi config.cfg
    3 修改内容如下,其他不变:
    4 zkServer=10.211.55.5:2181

    注意:需要将配置文件config.cfg与fatjar放在同一个目录下。

    3  启动zkui

    之后进入target/目录下,执行:

    1 nohup java -jar zkui-2.0-SNAPSHOT-jar-with-dependencies.jar &

    4  浏览器访问

    浏览器访问“http://10.211.55.5:9090”,之后在登录页面输入用户名密码:admin/manager进行登录。(可以去config.cfg进行配置)

     
  • 相关阅读:
    如何测试复杂的逻辑
    Docker 安装nginx和tomcat
    提高英语
    2020年终总结
    在互联网上班是什么感觉?
    已经过去2周了,你感觉怎么样?
    如何使用玩弄 macOS 的「聚焦搜索」
    如何使用玩弄 macOS 的「聚焦搜索」
    [sdoi2015]排序(搜索+剪枝优化)
    [sdoi 2010][bzoj 1925]地精部落(神仙dp)
  • 原文地址:https://www.cnblogs.com/hanease/p/16295172.html
Copyright © 2020-2023  润新知