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进行配置)

 

第二章 dubbo源码解析目录的更多相关文章

  1. 第零章 dubbo源码解析目录

    第一章 第一个dubbo项目 第二章  dubbo内核之spi源码解析 2.1  jdk-spi的实现原理 2.2 dubbo-spi源码解析 第三章 dubbo内核之ioc源码解析 第四章 dubb ...

  2. 第二章 ConcurrentHashMap源码解析

    注:在看这篇文章之前,如果对HashMap的层不清楚的话,建议先去看看HashMap源码解析. http://www.cnblogs.com/java-zhao/p/5106189.html 1.对于 ...

  3. 第二章 ArrayList源码解析

    一.对于ArrayList需要掌握的七点内容 ArrayList的创建:即构造器 往ArrayList中添加对象:即add(E)方法 获取ArrayList中的单个对象:即get(int index) ...

  4. dubbo源码解析五 --- 集群容错架构设计与原理分析

    欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 博客园 Dubbo 入门之二 --- 项目结构解析 博客园 Dubbo 源码分析系列之 ...

  5. Dubbo 源码解析四 —— 负载均衡LoadBalance

    欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 Dubbo 入门之二 --- 项目结构解析 Dubbo 源码分析系列之三 -- 架构原 ...

  6. dubbo源码解析-spi(一)

    前言 虽然标题是dubbo源码解析,但是本篇并不会出现dubbo的源码,本篇和之前的dubbo源码解析-简单原理.与spring融合一样,为dubbo源码解析专题的知识预热篇. 插播面试题 你是否了解 ...

  7. 第六章 ReentrantLock源码解析2--释放锁unlock()

    最常用的方式: int a = 12; //注意:通常情况下,这个会设置成一个类变量,比如说Segement中的段锁与copyOnWriteArrayList中的全局锁 final Reentrant ...

  8. 第十四章 Executors源码解析

    前边两章介绍了基础线程池ThreadPoolExecutor的使用方式.工作机理.参数详细介绍以及核心源码解析. 具体的介绍请参照: 第十二章 ThreadPoolExecutor使用与工作机理 第十 ...

  9. dubbo源码解析-zookeeper创建节点

    前言 在之前dubbo源码解析-本地暴露中的前言部分提到了两道高频的面试题,其中一道dubbo中zookeeper做注册中心,如果注册中心集群都挂掉,那发布者和订阅者还能通信吗?在上周的dubbo源码 ...

  10. dubbo源码解析-spi(4)

    前言 本篇是spi的第四篇,本篇讲解的是spi中增加的AOP,还是和上一篇一样,我们先从大家熟悉的spring引出AOP. AOP是老生常谈的话题了,思想都不会是一蹴而就的.比如架构设计从All in ...

随机推荐

  1. 一文彻底弄懂Spring IOC 依赖注入

    Spring IOC(Inversion of Control,控制反转)依赖注入是 Spring 框架的核心特性之一,旨在实现对象之间的松耦合,提升代码的可维护性.可测试性和可扩展性.下面我们将从以 ...

  2. CAD Plus 使用帮助

    English help 移动端使用帮助 文档更新日期: 2023-07-28; 这篇文章将介绍如何使用CAD Plus app; 如果您有疑问或需要帮助请发送邮件至 3167292926@qq.co ...

  3. 一款绘制3D架构图的在线神器:iCraft Editor

    在软件开发的世界里,架构图是系统设计的蓝图,它们不仅帮助团队理解系统的整体结构,还能提升沟通效率,确保项目的顺利推进.然而,绘制一张清晰.直观的架构图,往往需要大量时间和专业工具.面对繁琐的操作和复杂 ...

  4. PHP之常见问题

    汇总在PHP开发中遇到的一些问题 1.post提交参数缺失 场景: 在前端页面发起一个post提交的时候,查看payload中的数据是正常的, 但是在接收的时候,发现只有部分数据,算了一下,包含的数据 ...

  5. fastadmin-自定义

    1.样式重置 渲染完毕后再展示 [v-cloak]{ display: none; } <div id="app" v-cloak></div> 去除内置的 ...

  6. LeetCode题集-5 - 最长回文子串(一)

    题目:给你一个字符串 s,找到 s 中最长的回文子串. 这一题作为中等难度,常规解法对于大多数人应该都没有难度.但是其中也有超难的解决办法,下面我们就一起由易到难,循序渐进地来解这道题. 01.暴力破 ...

  7. 攻防世界:web习题之disabled_button

    攻防世界:web习题之disabled_button 题目内容 https://adworld.xctf.org.cn/challenges/list 打开网页会发现有一个无法点击的按钮 思路 查看该 ...

  8. VTK 显示3D模型的网格线(线框/wireframe)

    在VTK9.1.0在Windows10+VS2019+Qt 5.15.2环境下编译安装的Qt例子中,想显示球体表面的网格线(线框/wireframe),设置actor的EdgeVisibilityOn ...

  9. 一款基于 .NET MVC 框架开发、功能全面的MES系统

    前言 今天大姚给大家分享一款基于 .NET MVC 框架开发.功能全面的离散型零部件制造管理系统(MES):EasyMES. 项目介绍 EasyMES一款基于 .NET 6 MVC 框架开发.开源免费 ...

  10. 【Java】【Spring Boot】CP01:创建一个SpringBoot项目(Spring Initializr)

    设置(可跳过这一步) 点击Apply 然后点击OK 创建项目 目前不需要勾选什么,以后根据需要勾选 创建一个名为controller的文件夹(控制层),并在文件夹中创建一个HelloControlle ...