Dubbo架构简介
- Registry:注册中心。负责服务地址的注册与查找,服务的Provider与Consumer在启动时与注册中心交互。注册中心是通过长连接感知Provider的存在,
在Provider出现宕机的时候,注册中心会立即推送相关事件通知Consumer。- Provider:服务提供者。在启动时,会向Registry进行注册操作。将自己服务的地址和相关配置信息封装成URL添加到Zookeeper中。
- Consumer:向Registry进行订阅操作,从Zookeeper中获取到相应的Provider,然后根据负载均衡算法从多个Provider中选择一个Provider建立连接。
- Monitor:用于统计服务的调用次数和调用时间。
URL解析
protocol://username:password@host:port/path?key=value&key=value
- protocol:URL协议
- username/password:用户名/密码,HTTP Basic Authentication中多会使用在URL的协议之后直接携带用户名和密码的方式。
- host/port:主机/端口。
- path:请求的路径。
- parameters:键值对。
SPI
JDK SPI
在Classpath下的META-INF/services目录里创建一个以服务接口明明的文件,文件内容为服务接口的具体实现类。
public interface Log {
void log(String info);
}
public class Logback implements Log {
@Override
public void log(String info) {
System.out.println("Logback " + info);
}
}
public class Log4j implements Log {
@Override
public void log(String info) {
System.out.println("Log4j " + info);
}
}
public class SPIClass {
public static void main(String[] args) {
ServiceLoader<Log> serviceLoader = ServiceLoader.load(Log.class);
Iterator<Log> iterator = serviceLoader.iterator();
while (iterator.hasNext()){
Log log = iterator.next();
log.log("JDK SPI");
}
}
}
JDK SPI的流程
ServiceLoader.load()方法中,首先会尝试获取当前使用的ClassLoader,如果查找失败后使用SystemClassLoader,然后调用reload()方法。
在reload()方法中,首先会清理providers缓存,该缓存用来记录ServiceLoader创建的实现对象,其中Key为实现类的完整类名,Value为实现类的对象。
创建LazyIterator迭代器,用于读取SPI配置文件并实现实例化类对象。LazyIterator.hasNextService方法负责查找META-INF/services/目录下的
SPI配置文件,并进行遍历。
JDK SPI在JDBC中应用
DriverManager是JDK提供的数据库驱动管理器。
在调用getConnection的时候,DriverManager类会被Java虚拟机加载、解析并出发static代码块的执行;在loadInitialDrivers()方法中通过JDK SPI
扫描Classpath下java.sql.Driver接口实现类并实例化。
Dubbo SPI不仅解决了资源浪费的问题,还对SPI配置文件扩展和修改。Dubbo SPI配置文件的分类:
- META-INF/services/目录:用来兼容JDK SPI。
- META-INF/dubbo/目录:用来存放用户自定义的SPI配置文件。
- META-INF/dubbo/internal/目录:存放Dubbo内部使用的SPI配置文件。
@SPI注解
Dubbo中某个接口被@SPI注解修饰时,就表示该接口时扩展接口,@SPI注解的value值指定了默认的扩展名称。
ExtensionLoader是如何处理SPI注解:
- strategies:LoadingStrategy接口有三个实现,默认优先级为:DubboInternalLoadingStrategy > DubboLoadingStrategy > ServiceLoadingStrategy
- EXTENSION_LOADERS:该集合缓存了全部ExtensionLoader实例,其中的Key为扩展接口,Value为加载其扩展实现的ExtensionLoader实例。
- EXTENSION_INSTANCES:缓存了扩展实现类与实例对象的映射关系。
- 实例字段type:当前ExtensionLoader实例负责加载扩展接口。
- cachedDefaultName:记录了type这个扩展接口上@SPI注解的value值,也就是默认扩展名。
- cachedNames:缓存了该ExtensionLoader加载的扩展实现类与扩展名之间的映射关系。
- cachedClass:缓存了该ExtensionLoader加载的扩展名与扩展实现类之间的映射关系。
- cacheInstances:缓存了该ExtensionLoader加载的扩展名与扩展实现对象之间的映射关系。
@Adaptive
@Adaptive注解还可以驾到接口方法之上,Dubbo会动态生成适配器类。适配器什么实际工作都不用做,就是根据参数和状态选择其他实现来完成工作。
SpiExtensionFactory:根据扩展接口获取相应的适配器,没有得到属性名称;
SpringExtensionFactory:将属性名称作为SpringBean的名称,从Spring容器中获取Bean。
@Activate
- group属性:修饰的实现类是在Provider端被激活还是在Consumer端被激活;
- value属性:修饰的实现类只在URL参数中出现指定的key时才会被激活;
- order属性:用来确定扩展实现类的排序。
默认激活的扩展实现类有几个条件:
- 在cacheActivates集合中存在;
- @Activate注解指定的group属性与当前group匹配;
- 扩展名没有出现在values中;
- URL中出现了@Activate注解中指定的key。
网友评论