注意:本文所述均为dubbox,因dubbo 好长时间没有维护了,dubbox的gitHub传送门
dubbox介绍
以下引用自官方介绍
Dubbox当前的主要功能
支持REST风格远程调用(HTTP + JSON/XML):基于非常成熟的JBoss RestEasy框架,在dubbo中实现了REST风格(HTTP + JSON/XML)的远程调用,以显著简化企业内部的跨语言交互,同时显著简化企业对外的Open API、无线API甚至AJAX服务端等等的开发。事实上,这个REST调用也使得Dubbo可以对当今特别流行的“微服务”架构提供基础性支持。 另外,REST调用也达到了比较高的性能,在基准测试下,HTTP + JSON与Dubbo 2.x默认的RPC协议(即TCP + Hessian2二进制序列化)之间只有1.5倍左右的差距,详见文档中的基准测试报告。
支持基于Kryo和FST的Java高效序列化实现:基于当今比较知名的Kryo和FST高性能序列化库,为Dubbo默认的RPC协议添加新的序列化实现,并优化调整了其序列化体系,比较显著的提高了Dubbo RPC的性能,详见文档中的基准测试报告。
支持基于Jackson的JSON序列化:基于业界应用最广泛的Jackson序列化库,为Dubbo默认的RPC协议添加新的JSON序列化实现。
支持基于嵌入式Tomcat的HTTP remoting体系:基于嵌入式tomcat实现dubbo的HTTP remoting体系(即dubbo-remoting-http),用以逐步取代Dubbo中旧版本的嵌入式Jetty,可以显著的提高REST等的远程调用性能,并将Servlet API的支持从2.5升级到3.1。(注:除了REST,dubbo中的WebServices、Hessian、HTTP Invoker等协议都基于这个HTTP remoting体系)。
升级Spring:将dubbo中Spring由2.x升级到目前最常用的3.x版本,减少版本冲突带来的麻烦。
升级ZooKeeper客户端:将dubbo中的zookeeper客户端升级到最新的版本,以修正老版本中包含的bug。
支持完全基于Java代码的Dubbo配置:基于Spring的Java Config,实现完全无XML的纯Java代码方式来配置dubbo
调整Demo应用:暂时将dubbo的demo应用调整并改写以主要演示REST功能、Dubbo协议的新序列化方式、基于Java代码的Spring配置等等。
修正了dubbo的bug 包括配置、序列化、管理界面等等的bug。
注:dubbox和dubbo 2.x是兼容的,没有改变dubbo的任何已有的功能和配置方式(除了升级了spring之类的版本)
常用的dubbo配置
一个常见的dubbo配置,参照我们项目中的配置
<!-- 提供方应用信息,用于计算依赖关系-->
<dubbo:application name="XXXXX" organization="YYYY" owner="ZZZZ" logger="log4j"/>
<dubbo:registry address="${dubbo.registry}" file="${dubbo.cache.file}" />
<!--uncomment this if you want to test dubbo's monitor-->
<!--
<dubbo:monitor protocol="registry"/>
-->
<!-- 定义dubbo协议-->
<dubbo:protocol name="dubbo" port="${dubbo.port}" serialization="kryo"/>
<!-- 定义rest协议 -->
<dubbo:protocol name="rest" server="servlet" port="${dubbo.rest.port}" contextpath="${dubbo.rest.contextpath}"/>
<!-- consumer的默认配置,不检查provider是否存在 -->
<dubbo:consumer check="false" timeout="${dubbo.timeout}"/>
<dubbo:provider timeout="${dubbo.timeout}" />
<!-- 对外暴露的接口 -->
<import resource="classpath:/config/server/spring-dubbo-provider.xml"/>
对外暴露的服务:
<dubbo:service
interface="服务的接口类"
ref="adDirectionalPackageServer" protocol="dubbo" />
服务消费者配置:
<dubbo:reference id="dubboExampleService"
interface="服务的接口类"
protocol="dubbo" />
以上是常见的dubbo配置方式,其配置与Spring结合堪称完美,几乎没有代码侵入,像使用本地代码一样简单,那么这种配置是如何读取的呢??
上文书《Spring源码阅读之-自定义配置的解析》说道,dubbo也是通过spring.handlers 配置文件声明的命名空间解析类,他负责的命名空间是谁呢?
http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
可见其处理的是http://code.alibabatech.com/schema/dubbo 命名空间,并使用com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler类对其解析,其内部也是通过
this.registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
this.registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
this.registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
this.registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
this.registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
this.registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
this.registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
this.registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
this.registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
this.registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
这种方式对不同的元素,注册不同的解析器(其实是一个类),通过这段代码,发现上面配置中定义的元素,基本都有自己的解析器(DubboBeanDefinitionParser,这里dubbo把节点都通过该类来解析了)
这里先摘一个重点出来。
this.registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
其对应构造器(dubbo源码上没有注释,不过看接下来的parse方法很好理解)
public DubboBeanDefinitionParser(Class<?> beanClass, boolean required) {
this.beanClass = beanClass;
this.required = required;
}
之前已经有文章说明了,其实针对自定义命名空间的bean解析会调用BeanDefinitionParser中的parse方法。DubboBeanDefinitionParser类中的parse方法会调用如下的parse方法。这里就很好理解了,因为创建beanDefinition对象,其设置的beanDefinition对象的beanClass为构造器第一个参数。结合起来即:dubbo将对应的节点内容,解析给对应的Class维护,下面可以总结配置节点和类实例的对应关系。
@SuppressWarnings("unchecked")
private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClass(beanClass);
beanDefinition.setLazyInit(false);
String id = element.getAttribute("id");
if ((id == null || id.length() == 0) && required) {
//***省略,生成Bean Id的过程**//
}
if (id != null && id.length() > 0) {
if (parserContext.getRegistry().containsBeanDefinition(id)) {
throw new IllegalStateException("Duplicate spring bean id " + id);
}
// 看这里,对beanDefinition的注册操作。
parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
beanDefinition.getPropertyValues().addPropertyValue("id", id);
}
if (ProtocolConfig.class.equals(beanClass)) {
//对应节点的处理,不关注
} else if (ServiceBean.class.equals(beanClass)) {
//对应节点的处理,不关注
} else if (ProviderConfig.class.equals(beanClass)) {
//该方法内部会调用parse方法,即每个service方法会重新调用这个parse
parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition);
} else if (ConsumerConfig.class.equals(beanClass)) {
parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition);
}
Set<String> props = new HashSet<String>();
ManagedMap parameters = null;
for (Method setter : beanClass.getMethods()) {
String name = setter.getName();
if (name.length() > 3 && name.startsWith("set")
&& Modifier.isPublic(setter.getModifiers())
&& setter.getParameterTypes().length == 1) {
Class<?> type = setter.getParameterTypes()[0];
String property = StringUtils.camelToSplitName(name.substring(3, 4).toLowerCase() + name.substring(4), "-");
props.add(property);
Method getter = null;
try {
getter = beanClass.getMethod("get" + name.substring(3), new Class<?>[0]);
} catch (NoSuchMethodException e) {
try {
getter = beanClass.getMethod("is" + name.substring(3), new Class<?>[0]);
} catch (NoSuchMethodException e2) {
}
}
if (getter == null
|| ! Modifier.isPublic(getter.getModifiers())
|| ! type.equals(getter.getReturnType())) {
continue;
}
//***调用相关class的getter,setter方法,设置bean的属性值****//
beanDefinition.getPropertyValues().addPropertyValue(property, reference);
}
}
NamedNodeMap attributes = element.getAttributes();
int len = attributes.getLength();
for (int i = 0; i < len; i++) {
Node node = attributes.item(i);
String name = node.getLocalName();
if (! props.contains(name)) {
if (parameters == null) {
parameters = new ManagedMap();
}
String value = node.getNodeValue();
parameters.put(name, new TypedStringValue(value, String.class));
}
}
if (parameters != null) {
beanDefinition.getPropertyValues().addPropertyValue("parameters", parameters);
}
return beanDefinition;
}
对应配置节点与类对象的配置关系。
application->com.alibaba.dubbo.configcom.alibaba.dubbo.config.ApplicationConfig
module->com.alibaba.dubbo.config.ModuleConfig
registry->com.alibaba.dubbo.config.RegistryConfig
monitor->com.alibaba.dubbo.config.MonitorConfig
provider->com.alibaba.dubbo.config.ProviderConfig
consumer->com.alibaba.dubbo.config.ConsumerConfig
protocol->com.alibaba.dubbo.config.ProtocolConfig
service->com.alibaba.dubbo.config.ServiceBean
reference->com.alibaba.dubbo.config.ReferenceBean
annotation->com.alibaba.dubbo.config.AnnotationBean
类对象具体的属性值含义和节点配置方法,可参看一篇博文,