下面进入正文
- config 配置层:对外配置接口,以 ServiceConfig, ReferenceConfig 为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类
- proxy 服务代理层:服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以 ServiceProxy 为中心,扩展接口为 ProxyFactory
- registry 注册中心层:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 RegistryFactory, Registry, RegistryService
- cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心,扩展接口为 Cluster, Directory, Router, LoadBalance
- monitor 监控层:RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为 MonitorFactory, Monitor, MonitorService
- protocol 远程调用层:封装 RPC 调用,以 Invocation, Result 为中心,扩展接口为 Protocol, Invoker, Exporter
- exchange 信息交换层:封装请求响应模式,同步转异步,以 Request, Response 为中心,扩展接口为 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer
- transport 网络传输层:抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel, Transporter, Client, Server, Codec
- serialize 数据序列化层:可复用的一些工具,扩展接口为 Serialization, ObjectInput, ObjectOutput, ThreadPool
但是在实际使用的时候,我们只需要使用一种实现,那dubbo是怎么选择的呢。
private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
复制代码
public class DubboNamespaceHandler extends NamespaceHandlerSupport {
static {
Version.checkDuplicate(DubboNamespaceHandler.class);
}
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 DubboBeanDefinitionParser(AnnotationBean.class, true));
}
}复制代码
public class DubboBeanDefinitionParser implements BeanDefinitionParser {
private static final Logger logger = LoggerFactory.getLogger(DubboBeanDefinitionParser.class);
private static final Pattern GROUP_AND_VERION = Pattern.compile("^[\\-.0-9_a-zA-Z]+(\\:[\\-.0-9_a-zA-Z]+)?$");
private final Class> beanClass;
private final boolean required;
public DubboBeanDefinitionParser(Class> beanClass, boolean required) {
this.beanClass = beanClass;
this.required = required;
}
@SuppressWarnings("unchecked")
private static BeanDefinition parse(Element element, ParserContext parserContext, Class> beanClass, boolean required) {
}
private static boolean isPrimitive(Class> cls) {
}
@SuppressWarnings("unchecked")
private static void parseMultiRef(String property, String value, RootBeanDefinition beanDefinition,
ParserContext parserContext) {
}
private static void parseNested(Element element, ParserContext parserContext, Class> beanClass, boolean required, String tag, String property, String ref, BeanDefinition beanDefinition) {
}
private static void parseProperties(NodeList nodeList, RootBeanDefinition beanDefinition) {
}
@SuppressWarnings("unchecked")
private static ManagedMap parseParameters(NodeList nodeList, RootBeanDefinition beanDefinition) {
}
@SuppressWarnings("unchecked")
private static void parseMethods(String id, NodeList nodeList, RootBeanDefinition beanDefinition,
ParserContext parserContext) {
}
@SuppressWarnings("unchecked")
private static void parseArguments(String id, NodeList nodeList, RootBeanDefinition beanDefinition,
ParserContext parserContext) {
}
public BeanDefinition parse(Element element, ParserContext parserContext) {
}
复制代码
public class ServiceBean extends ServiceConfig implements InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener, BeanNameAware {复制代码
public void afterPropertiesSet() throws Exception {
if (getProvider() == null) {
}
if (getApplication() == null{
}
if (getModule() == null
&& (getProvider() == null || getProvider().getModule() == null)) {
}
if ((getRegistries() == null || getRegistries().size() == 0)
&& (getProvider() == null || getProvider().getRegistries() == null || getProvider().getRegistries().size() == 0)
&& (getApplication() == null || getApplication().getRegistries() == null || getApplication().getRegistries().size() == 0)) {
Map registryConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class, false, false);
if (registryConfigMap != null && registryConfigMap.size() > 0)
}
if (getMonitor() == null
&& (getProvider() == null || getProvider().getMonitor() == null)
&& (getApplication() == null || getApplication().getMonitor() == null)) {
Map monitorConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class, false, false);
if (monitorConfigMap != null && monitorConfigMap.size() > 0)
}
if ((getProtocols() == null || getProtocols().size() == 0)
&& (getProvider() == null || getProvider().getProtocols() == null || getProvider().getProtocols().size() == 0)) {
Map protocolConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class, false, false);
if (protocolConfigMap != null && protocolConfigMap.size() > 0)
}
if (getPath() == null || getPath().length() == 0) {
if (beanName != null && beanName.length() > 0
&& getInterface() != null && getInterface().length() > 0
&& beanName.startsWith(getInterface())) {
setPath(beanName);
}
}
if (!isDelay()) {
export();
}
}复制代码
3、API和SPI的区别
相信大家对API都是比较了解的,这是软件开发的一个基本概念。下面是API和SPI的区别:
API:实现方来制定接口并完成对接口的不同实现,调用方仅仅依赖却无权选择不同实现。
SPI:调用方来制定接口,由实现方来实现,调用方来选择自己的实现。
这里有一个图可以帮助理解:
API就是,定义一个接口,由服务方实现,然后客户进行调用,如果客户需要修改服务实现,需要让服务方进行修改。
而SPI是,定义一个接口,客户去实现这个接口,然后客户去调用服务方的接口时,可以让服务方调用客户的这种实现。
这里可能比较难懂,API和SPI的面向用户和目的是不同的,API直接被应用开发人员使用,SPI被框架扩展人员使用。
4、Java-SPI机制的介绍
JDBC大家应该都了解,就是实现通过Java连接到不同数据库的。
这里我们需要了解服务方和客户分别是什么
调用方是JDK,JDK定义了一个接口,java.sql.Driver,但是没有实现这个接口。
package java.sql;
import java.util.logging.Logger;
public interface Driver {
Connection connect(String url, java.util.Properties info)
throws SQLException;
boolean acceptsURL(String url) throws SQLException;
DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info)
throws SQLException;
int getMajorVersion();
int getMinorVersion();
boolean jdbcCompliant();
public Logger getParentLogger() throws SQLFeatureNotSupportedException;
}
复制代码
实现方是数据库提供商,每一个提供商,例如mysql,oracle等等不同的数据库,对上面的接口进行不同的连接实现。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.mysql.fabric.jdbc;
import com.mysql.jdbc.ExceptionInterceptor;
import com.mysql.jdbc.NonRegisteringDriver;
import com.mysql.jdbc.Util;
import java.lang.reflect.Constructor;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import java.util.logging.Logger;
public class FabricMySQLDriver extends NonRegisteringDriver implements Driver {
public static final String FABRIC_URL_PREFIX = "jdbc:mysql:fabric://";
public static final String FABRIC_SHARD_KEY_PROPERTY_KEY = "fabricShardKey";
public static final String FABRIC_SHARD_TABLE_PROPERTY_KEY = "fabricShardTable";
public static final String FABRIC_SERVER_GROUP_PROPERTY_KEY = "fabricServerGroup";
public static final String FABRIC_PROTOCOL_PROPERTY_KEY = "fabricProtocol";
public static final String FABRIC_USERNAME_PROPERTY_KEY = "fabricUsername";
public static final String FABRIC_PASSWORD_PROPERTY_KEY = "fabricPassword";
public static final String FABRIC_REPORT_ERRORS_PROPERTY_KEY = "fabricReportErrors";
public FabricMySQLDriver() throws SQLException {
}
public Connection connect(String url, Properties info) throws SQLException {
Properties parsedProps = this.parseFabricURL(url, info);
if (parsedProps == null) {
return null;
} else {
parsedProps.setProperty("fabricProtocol", "http");
if (Util.isJdbc4()) {
try {
Constructor> jdbc4proxy = Class.forName("com.mysql.fabric.jdbc.JDBC4FabricMySQLConnectionProxy").getConstructor(Properties.class);
return (Connection)Util.handleNewInstance(jdbc4proxy, new Object[]{parsedProps}, (ExceptionInterceptor)null);
} catch (Exception var5) {
throw (SQLException)(new SQLException(var5.getMessage())).initCause(var5);
}
} else {
return new FabricMySQLConnectionProxy(parsedProps);
}
}
}
public boolean acceptsURL(String url) throws SQLException {
return this.parseFabricURL(url, (Properties)null) != null;
}
Properties parseFabricURL(String url, Properties defaults) throws SQLException {
return !url.startsWith("jdbc:mysql:fabric://") ? null : super.parseURL(url.replaceAll("fabric:", ""), defaults);
}
public Logger getParentLogger() throws SQLException {
throw new SQLException("no logging");
}
static {
try {
DriverManager.registerDriver(new FabricMySQLDriver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver", var1);
}
}
}
复制代码
那么我们是怎么是调用到不同厂商的实现方式的呢,这里就是用了Java-SPI机制。
实现Java-SPI有以下的步骤:
到这里,大家需要了解API和SPI的区别,已经SPI的应用场景。
5、dubbo-SPI机制的介绍
Dubbo 改进了 JDK 标准的 SPI 的以下问题:
(1)JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
(2)如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过 getName()
获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持 ruby,而不是真正失败的原因。
(3)增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。
我们以一个例子来了解,dubbo-SPI机制的实现和原理。
private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();复制代码
我们先看一下Protocol这个类
@SPI("dubbo")
public interface Protocol {
int getDefaultPort();
@Adaptive
Exporter export(Invoker invoker) throws RpcException;
@Adaptive
Invoker refer(Class type, URL url) throws RpcException;
void destroy();
}复制代码
首先,我们需要了解三个主要的注解。
(1)@SPI注解,使用在接口上,标志这个接口是一个可以进行SPI扩展的接口;注解的value值指定这个扩展的默认实现方式。
例如上面的Protocol,注解为@SPI("dubbo"),标志这个接口支持SPI扩展,也就是说可以指定下面实现类的某个实现来完成这个接口的功能
注解的value值为dubbo,表明如果没有指定protocol的实现方式,默认使用DubboProtocol作为这个接口的实现类。
(2)@Adaptive注解,用在方法上和实现类上;用在方法上时,标志这个方法是支持动态扩展的,可以在调用的时候指定特定的扩展类来实现这个方法;用在类上时,指定这个扩展的默认实现方式,这个默认实现的优先级比@SPI注解要高。
注解用在方法上:
我们可以看一下上面的Protocol的接口定义,这个接口有两个方法使用了@Adaptive注解,也就是说这两个方法在调用的时候,可以动态选择不同的扩展实现来调用,这到底是怎么实现的呢。
上面的代码,就是dubbo对protocol进行扩展后生成的类。我们可以看到,没有使用@Adaptive注解的方法,实现类上只是抛出了一个异常,并没有做特定的实现。而使用@Adaptive注解的两个方法,都是通过URL对象来进行扩展实现的选择的。
(3)@Activate注解,用在实现类上,用于标志这个类的配置信息。
@Activate注解主要使用在集合扩展类上,就是那些需要一次性扩展比较多实现的类。
例如上面的三个类,@Activate注解的group = Constants.PROVIDER,表明这些类的扩展实现都是在URL带用Constants.PROVIDER的时候生效的,他们会生成一个链式的过滤器,对请求进行特定的行为处理。