一、架构设计图
1、service 业务逻辑层:接口和接口实现,dubbo的服务提供端暴露哪个接口和接口的实现,dubbo的服务消费端订阅的服务接口,用户仅需要关注这一层
2、config 配置层:对外配置接口,以 ServiceConfig、ReferenceConfig 为中心,可以直接初始化配置类(@Bean的方式),也可以通过 Spring 解析配置生成配置类
3、proxy 服务代理层:服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton,以 ServiceProxy 为中心,扩展接口为 ProxyFactory
4、registry 注册中心层:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 RegistryFactory,Registry,RegistryService
5、cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心,扩展接口为 Cluster、Directory、Router、LoadBalance
6、monitor 监控层:RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为 MonitorFactory、Monitor、MonitorService
7、protocol 远程调用层:封装 RPC 调用,以 Invocation、Result 为中心,扩展接口为 Protocol、Invoke、Exporter
8、exchange 信息交换层:封装请求响应模式,同步转异步,以 Request、Response 为中心,扩展接口为 Exchanger、ExchangeChannel、ExchangeClient、ExchangeServer
9、transport 网络传输层:抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel、Transporter、Client、Server、Codec
10、serialize 数据序列化层:可复用的一些工具,扩展接口为 Serialization、ObjectInput、ObjectOutput、ThreadPool
二、标签解析
Spring解析配置文件中的每一个标签都会有相应的类来完成,这些类都实现了同一个接口:BeanDefinitionParser
public abstract interface BeanDefinitionParser {
public abstract BeanDefinition parse(Element paramElement, ParserContext paramParserContext);
}
该接口的继承树如下:其中就有DubboBeanDefinitionParser用来解析配置文件中的dubbo标签
DubboBeanDefinitionParser的parse方法如下:可以看出来其实就是解析每一个标签的配置,并给相应的对象属性赋值
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)) {
String generatedBeanName = element.getAttribute("name");
if ((generatedBeanName == null) || (generatedBeanName.length() == 0)) {
if (ProtocolConfig.class.equals(beanClass))
generatedBeanName = "dubbo";
else {
generatedBeanName = element.getAttribute("interface");
}
}
if ((generatedBeanName == null) || (generatedBeanName.length() == 0)) {
generatedBeanName = beanClass.getName();
}
id = generatedBeanName;
int counter = 2;
while (parserContext.getRegistry().containsBeanDefinition(id)) {
id = generatedBeanName + (counter++);
}
}
if ((id != null) && (id.length() > 0)) {
if (parserContext.getRegistry().containsBeanDefinition(id)) {
throw new IllegalStateException("Duplicate spring bean id " + id);
}
parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
beanDefinition.getPropertyValues().addPropertyValue("id", id);
}
if (ProtocolConfig.class.equals(beanClass)) {
for (String name : parserContext.getRegistry().getBeanDefinitionNames()) {
BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name);
PropertyValue property = definition.getPropertyValues().getPropertyValue("protocol");
if (property != null) {
Object value = property.getValue();
if ((value instanceof ProtocolConfig) && (id.equals(((ProtocolConfig) value).getName())))
definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id));
}
}
} else if (ServiceBean.class.equals(beanClass)) {
String className = element.getAttribute("class");
if ((className != null) && (className.length() > 0)) {
RootBeanDefinition classDefinition = new RootBeanDefinition();
classDefinition.setBeanClass(ReflectUtils.forName(className));
classDefinition.setLazyInit(false);
parseProperties(element.getChildNodes(), classDefinition);
beanDefinition.getPropertyValues().addPropertyValue("ref",
new BeanDefinitionHolder(classDefinition, id + "Impl"));
}
} else if (ProviderConfig.class.equals(beanClass)) {
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 props = new HashSet();
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)) {
continue;
}
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 localNoSuchMethodException1) {
}
}
if ((getter == null) || (!(Modifier.isPublic(getter.getModifiers()))))
continue;
if (!(type.equals(getter.getReturnType()))) {
continue;
}
if ("parameters".equals(property)) {
parameters = parseParameters(element.getChildNodes(), beanDefinition);
} else if ("methods".equals(property)) {
parseMethods(id, element.getChildNodes(), beanDefinition, parserContext);
} else if ("arguments".equals(property)) {
parseArguments(id, element.getChildNodes(), beanDefinition, parserContext);
} else {
String value = element.getAttribute(property);
if (value != null) {
value = value.trim();
if (value.length() > 0) {
if (("registry".equals(property)) && ("N/A".equalsIgnoreCase(value))) {
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("N/A");
beanDefinition.getPropertyValues().addPropertyValue(property, registryConfig);
} else if (("registry".equals(property)) && (value.indexOf(44) != -1)) {
parseMultiRef("registries", value, beanDefinition, parserContext);
} else if (("provider".equals(property)) && (value.indexOf(44) != -1)) {
parseMultiRef("providers", value, beanDefinition, parserContext);
} else if (("protocol".equals(property)) && (value.indexOf(44) != -1)) {
parseMultiRef("protocols", value, beanDefinition, parserContext);
} else {
Object reference;
Object reference;
if (isPrimitive(type)) {
if ((("async".equals(property)) && ("false".equals(value)))
|| (("timeout".equals(property)) && ("0".equals(value)))
|| (("delay".equals(property)) && ("0".equals(value)))
|| (("version".equals(property)) && ("0.0.0".equals(value)))
|| (("stat".equals(property)) && ("-1".equals(value)))
|| (("reliable".equals(property)) && ("false".equals(value)))) {
value = null;
}
reference = value;
} else {
Object reference;
if (("protocol".equals(property))
&& (ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(value))
&& (((!(parserContext.getRegistry().containsBeanDefinition(value)))
|| (!(ProtocolConfig.class.getName().equals(parserContext.getRegistry()
.getBeanDefinition(value).getBeanClassName())))))) {
if ("dubbo:provider".equals(element.getTagName())) {
logger.warn("Recommended replace <dubbo:provider protocol=\"" + value
+ "\" ... /> to <dubbo:protocol name=\"" + value + "\" ... />");
}
ProtocolConfig protocol = new ProtocolConfig();
protocol.setName(value);
reference = protocol;
} else if ("onreturn".equals(property)) {
int index = value.lastIndexOf(".");
String returnRef = value.substring(0, index);
String returnMethod = value.substring(index + 1);
Object reference = new RuntimeBeanReference(returnRef);
beanDefinition.getPropertyValues().addPropertyValue("onreturnMethod", returnMethod);
} else if ("onthrow".equals(property)) {
int index = value.lastIndexOf(".");
String throwRef = value.substring(0, index);
String throwMethod = value.substring(index + 1);
Object reference = new RuntimeBeanReference(throwRef);
beanDefinition.getPropertyValues().addPropertyValue("onthrowMethod", throwMethod);
} else if ("oninvoke".equals(property)) {
int index = value.lastIndexOf(".");
String invokeRef = value.substring(0, index);
String invokeRefMethod = value.substring(index + 1);
Object reference = new RuntimeBeanReference(invokeRef);
beanDefinition.getPropertyValues().addPropertyValue("oninvokeMethod",
invokeRefMethod);
} else {
if (("ref".equals(property))
&& (parserContext.getRegistry().containsBeanDefinition(value))) {
BeanDefinition refBean = parserContext.getRegistry().getBeanDefinition(value);
if (!(refBean.isSingleton())) {
throw new IllegalStateException("The exported service ref " + value
+ " must be singleton! Please set the " + value
+ " bean scope to singleton, eg: <bean id=\"" + value
+ "\" scope=\"singleton\" ...>");
}
}
reference = new RuntimeBeanReference(value);
}
}
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;
}
在标签的解析进入DubboBeanDefinitionParser的parse方法之前会先通过DubboNamespaceHandler的init初始化方法:在该方法中会先将所有的dubbo标签类型放在一个HashMap中(key为标签名,value为对应的标签解析对象),会将标签封装成对应的组件,这样在解析到这个标签时也就知道这个标签对应的是哪个类了。Spring的标签解析是逐个解析的,并不是在将配置文件一次性读完之后,一次性解析,而是读一个标签解析一个标签,因此dubbo的配置也可以参杂在Spring的bean配置文件中,效果是一样的,只不过单拉出来一个配置文件显得更规整
public class DubboNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
}
static {
Version.checkDuplicate(DubboNamespaceHandler.class);
}
}
可以看到在解析到service和reference时并不是对应一个配置类(XxxConfig),而是一个Bean:
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
三、服务暴露流程
在将标签封装为组件的时候,如果标签是service则会将该标签封装为ServiceBean,这个ServiceBean的源码如下:实现了InitializingBean和ApplicationListener两个接口,这两个接口的实现方法是afterPropertiesSet和onApplicationEvent
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,
ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, BeanNameAware {
// ...
public void onApplicationEvent(ContextRefreshedEvent event) {
if ((isDelay()) && (!(isExported())) && (!(isUnexported()))) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
export();
}
}
//...
public void afterPropertiesSet() throws Exception {
if (getProvider() == null) {
Map providerConfigMap = (this.applicationContext == null) ? null
: BeanFactoryUtils.beansOfTypeIncludingAncestors(this.applicationContext, ProviderConfig.class,
false, false);
if ((providerConfigMap != null) && (providerConfigMap.size() > 0)) {
Map protocolConfigMap = (this.applicationContext == null) ? null
: BeanFactoryUtils.beansOfTypeIncludingAncestors(this.applicationContext, ProtocolConfig.class,
false, false);
if ((((protocolConfigMap == null) || (protocolConfigMap.size() == 0)))
&& (providerConfigMap.size() > 1)) {
List providerConfigs = new ArrayList();
for (ProviderConfig config : providerConfigMap.values()) {
if ((config.isDefault() != null) && (config.isDefault().booleanValue())) {
providerConfigs.add(config);
}
}
if (!(providerConfigs.isEmpty()))
setProviders(providerConfigs);
} else {
ProviderConfig providerConfig = null;
for (ProviderConfig config : providerConfigMap.values()) {
if ((config.isDefault() == null) || (config.isDefault().booleanValue())) {
if (providerConfig != null) {
throw new IllegalStateException(
"Duplicate provider configs: " + providerConfig + " and " + config);
}
providerConfig = config;
}
}
if (providerConfig != null) {
setProvider(providerConfig);
}
}
}
}
if ((getApplication() == null) && (((getProvider() == null) || (getProvider().getApplication() == null)))) {
Map applicationConfigMap = (this.applicationContext == null) ? null
: BeanFactoryUtils.beansOfTypeIncludingAncestors(this.applicationContext, ApplicationConfig.class,
false, false);
if ((applicationConfigMap != null) && (applicationConfigMap.size() > 0)) {
ApplicationConfig applicationConfig = null;
for (ApplicationConfig config : applicationConfigMap.values()) {
if ((config.isDefault() == null) || (config.isDefault().booleanValue())) {
if (applicationConfig != null) {
throw new IllegalStateException(
"Duplicate application configs: " + applicationConfig + " and " + config);
}
applicationConfig = config;
}
}
if (applicationConfig != null) {
setApplication(applicationConfig);
}
}
}
if ((getModule() == null) && (((getProvider() == null) || (getProvider().getModule() == null)))) {
Map moduleConfigMap = (this.applicationContext == null) ? null
: BeanFactoryUtils.beansOfTypeIncludingAncestors(this.applicationContext, ModuleConfig.class, false,
false);
if ((moduleConfigMap != null) && (moduleConfigMap.size() > 0)) {
ModuleConfig moduleConfig = null;
for (ModuleConfig config : moduleConfigMap.values()) {
if ((config.isDefault() == null) || (config.isDefault().booleanValue())) {
if (moduleConfig != null) {
throw new IllegalStateException(
"Duplicate module configs: " + moduleConfig + " and " + config);
}
moduleConfig = config;
}
}
if (moduleConfig != null) {
setModule(moduleConfig);
}
}
}
if ((((getRegistries() == null) || (getRegistries().isEmpty())))
&& (((getProvider() == null) || (getProvider().getRegistries() == null)
|| (getProvider().getRegistries().isEmpty())))
&& (((getApplication() == null) || (getApplication().getRegistries() == null)
|| (getApplication().getRegistries().isEmpty())))) {
Map registryConfigMap = (this.applicationContext == null) ? null
: BeanFactoryUtils.beansOfTypeIncludingAncestors(this.applicationContext, RegistryConfig.class,
false, false);
if ((registryConfigMap != null) && (registryConfigMap.size() > 0)) {
List registryConfigs = new ArrayList();
for (RegistryConfig config : registryConfigMap.values()) {
if ((config.isDefault() == null) || (config.isDefault().booleanValue())) {
registryConfigs.add(config);
}
}
if ((registryConfigs != null) && (!(registryConfigs.isEmpty()))) {
super.setRegistries(registryConfigs);
}
}
}
if ((getMonitor() == null) && (((getProvider() == null) || (getProvider().getMonitor() == null)))
&& (((getApplication() == null) || (getApplication().getMonitor() == null)))) {
Map monitorConfigMap = (this.applicationContext == null) ? null
: BeanFactoryUtils.beansOfTypeIncludingAncestors(this.applicationContext, MonitorConfig.class,
false, false);
if ((monitorConfigMap != null) && (monitorConfigMap.size() > 0)) {
MonitorConfig monitorConfig = null;
for (MonitorConfig config : monitorConfigMap.values()) {
if ((config.isDefault() == null) || (config.isDefault().booleanValue())) {
if (monitorConfig != null) {
throw new IllegalStateException(
"Duplicate monitor configs: " + monitorConfig + " and " + config);
}
monitorConfig = config;
}
}
if (monitorConfig != null) {
setMonitor(monitorConfig);
}
}
}
if ((((getProtocols() == null) || (getProtocols().isEmpty()))) && (((getProvider() == null)
|| (getProvider().getProtocols() == null) || (getProvider().getProtocols().isEmpty())))) {
Map protocolConfigMap = (this.applicationContext == null) ? null
: BeanFactoryUtils.beansOfTypeIncludingAncestors(this.applicationContext, ProtocolConfig.class,
false, false);
if ((protocolConfigMap != null) && (protocolConfigMap.size() > 0)) {
List protocolConfigs = new ArrayList();
for (ProtocolConfig config : protocolConfigMap.values()) {
if ((config.isDefault() == null) || (config.isDefault().booleanValue())) {
protocolConfigs.add(config);
}
}
if ((protocolConfigs != null) && (!(protocolConfigs.isEmpty()))) {
super.setProtocols(protocolConfigs);
}
}
}
if ((((getPath() == null) || (getPath().length() == 0))) && (this.beanName != null)
&& (this.beanName.length() > 0) && (getInterface() != null) && (getInterface().length() > 0)
&& (this.beanName.startsWith(getInterface()))) {
setPath(this.beanName);
}
if (!(isDelay()))
export();
}
//...
}
在afterPropertiesSet()中会将标签中的配置信息保存起来,在onApplicationEvent()中调用export()暴露服务,具体的服务暴露流程参看下图:
四、服务引用流程
此处说的服务引用是说在消费者端的Controller或者Service组件中通过@Autowired注入Dubbo的组件,我们知道在服务的消费者端是没有服务接口的实现的,那服务对象时怎么注入成功的呢,其实注入的是一个代理对象,原理如下:
在将标签封装为组件的时候,如果标签是reference则会将该标签封装为ReferenceBean,这个ReferenceBean的源码如下:实现了FactoryBean接口,是一个工厂Bean
public class ReferenceBean<T> extends ReferenceConfig<T>
implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean {
private static final long serialVersionUID = 213195494150089726L;
private transient ApplicationContext applicationContext;
//...
public Object getObject() throws Exception {
return get();
}
public Class<?> getObjectType() {
return getInterfaceClass();
}
@Parameter(excluded = true)
public boolean isSingleton() {
return true;
}
//...
}
由于ReferenceBean实现了FactoryBean接口,因此在获取该Bean的实例时会调用getObject(),在该方法中会调用get(),之后会调用createProxy()创建代理,具体的服务引用流程如下图:
五、服务调用流程
通过代理对象,在调用的过程中有容错、负载均衡、超时、失败重试等机制