从本章开始基于Dubbo3.1.8分析Dubbo3源码。
由于Dubbo3中到处都会出现各种xxxModel,所以必须先搞清楚这些东西的作用。
本章主要分析:
1)DubboSPI的变更;
2)多实例特性及其作用;
3)Deployer新角色及其作用;
在3.x之前,ExtensionLoader针对于一个扩展点接口只有一个实例,针对一个扩展点实现Class也只有一个扩展点实现实例。
2.xExtensionLoader全局缓存:
在第一章的时候提到过,单例往往会针对一个scope来说,而在Dubbo3.0引入了Scope概念。
ExtensionScope列举了目前支持的四种scope:FRAMEWORK-dubbo框架级别、APPLICATION-应用级别、MODULE-模块级别、SELF-prototype原型;
public enum ExtensionScope {
FRAMEWORK,
APPLICATION,
MODULE,
SELF
}
复制代码
不同scope有不同ScopeModel负责管理。
ScopeModel实现ExtensionAccessor,提供获取SPI扩展点的能力。
dubbo中所有用到扩展点的地方,都会通过ScopeModel来获取对应scope下的扩展点。
而原来ExtensionLoader#getExtensionLoader(type)方法已经废弃,委派给全局单例ModuleModel获取扩展点。
每个ScopeModel实例对应一个ExtensionDirector,所有获取扩展点逻辑都委派给这个ExtensionDirector。
private volatile ExtensionDirector extensionDirector;
protected void initialize() {
synchronized (instLock) {
this.extensionDirector = new ExtensionDirector(parent != null ?
parent.getExtensionDirector() :
null, scope, this);
// ...
}
}
复制代码
ExtensionDirector存储一个scope范围中扩展接口class对应ExtensionLoader,从而实现了不同scope管理自己的ExtensionLoader实例。
ExtensionDirector#getExtensionLoader:获取或创建ExtensionLoader。
1)如果scope=SELF,每次创建一个ExtensionLoader;
2)优先走parent获取ExtensionLoader;
3)parent找不到,自己判断scope是否和扩展点SPI注解scope一致,一致才创建ExtensionLoader;
双亲关系:Framework最大,接着是Application,最后是Module。
如果scope=Framework,通过ModuleModel/ApplicationModel也能获取到,且module、application、framework具备父子关系的三个ScopeModel都会拿到同一个扩展点实现。
例如Codec2编解码器,一种Codec2编解码器在一个FrameworkModel中只有一个实例。
如果scope=Module,只能通过ModuleModel获取,ApplicationModel/FrameworkModel获取不到。
例如Filter过滤器,一种Filter过滤器在一个ModuleModel中只有一个实例。
2.x仅支持setter注入。
3.x增加了构造/aware注入,主要是为了方便接入ScopeModel。
ExtensionLoader#createExtension:
普通扩展点支持构造/setter/aware注入。
InstantiationStrategy#instantiate:选择构造方法实例化扩展点。
目前支持两种构造方法,一种无参构造,另一种是参数列表只有ScopeModel的构造方法。
优先会取ScopeModel注入构造方法,默认采用无参构造。
可以注入不同ScopeModel。
和2.x相同,通过一个自适应扩展AdaptiveExtensionInjector,循环所有ExtensionInjector,获取setter方法的type+name对应实现实例。
和2.x相同,通过setter方法可以注入自适应扩展实现。
区别在于需要通过关联ScopeModel获取对应scope下的扩展实例。
3.x新支持从一个scope的BeanFactory中获取bean注入,这个BeanFactory后续再聊。
3.x对于spring ioc容器注入更合理了,一个SpringExtensionInjector只能对应一个ioc容器。
ApplicationContext是有父子关系的,2.x通过多个ApplicationContext循环获取bean,不太合理。
ExtensionLoader#postProcessAfterInitialization:
ExtensionAccessorAware接口实现,通过setExtensionAccessor注入scope对应ExtensionDirector 。
ScopeModelAwareExtensionProcessor#postProcessAfterInitialization:
ScopeModelAware接口实现,注入当前scope持有的Model。
ScopeModelAwareExtensionProcessor#initialize:
不同scope能注入的Model不同。
2.7.8版本做了Wrapper增强(github.com/apache/dubb…)。
注:因为之前读的是2.7.6,说到Wrapper无序,这里澄清一下。
1)支持直接获取未包装扩展点实现
ExtensionLoader#getExtension支持传入wrap,决定是否获取被包装的扩展点实现。
ExtensionLoader会缓存被包装和未被包装的扩展点实现。
2)支持包装排序
3)支持针对部分扩展点实现做包装
注意:包装类仅支持Setter/Aware注入,不支持构造注入。
ScopeModel#initialize:每个ScopeModel都持有一个ScopeBeanFactory。
这个BeanFactory是干嘛用的,和Extension扩展点有什么关系,有什么区别?
ScopeBeanFactory用于统一管理ScopeModel范围内的非扩展点bean。
ScopeModelInitializer扩展点负责在ScopeModel构造时,执行一些初始化任务。
如FrameworkModel:
如ApplicationModel:
这些ScopeModelInitializer会向ScopeModel的beanFactory中注入一些自己需要用的bean。
比如dubbo-cluster模块需要用到bean都会在ClusterScopeModelInitializer中注入,bean可以选择在某个scope下单例。
无论是基于Class注册还是基于instance直接注册,最终都会执行初始化Aware注入,放入beanFactory缓存。
获取bean,先走当前scopeBeanFactory获取,如果获取不到,走父scopeBeanFactory获取。
注意:这里和SPI扩展点Scope双亲委派是相反的,但是和SpringIOC是一样的,子容器能感知到父容器,父容器对子容器无感知。
底层优先name匹配,其次type匹配(匹配多个抛出异常),如果都匹配不到返回null。
基于ScopeModel非单例,3.0提供了多实例的新特性。
FrameworkModel,支持同一个jvm内部dubbo框架级别隔离。
ApplicationModel,支持应用级别隔离。
ModuleModel,支持模块级别隔离。
通过DubboBootstrap#newInstance这个新api,可以构造多个DubboBootstrap实例。
ApplicationConfig applicationConfig = new ApplicationConfig("app1");
applicationConfig.setRegisterMode("instance"); // 应用实例级别注册
ServiceConfig service = new ServiceConfig<>();
service.setInterface(DemoService.class);
service.setRef(new DemoServiceImpl());
DubboBootstrap.newInstance().application(applicationConfig)
.registry(new RegistryConfig("zookeeper://127.0.0.1:2181"))
.protocol(new ProtocolConfig(CommonConstants.DUBBO, 20880))
.service(service)
.start();
ApplicationConfig applicationConfig2 = new ApplicationConfig("app2");
applicationConfig2.setRegisterMode("instance"); // 应用实例级别注册
ServiceConfig service2 = new ServiceConfig<>();
service2.setInterface(DemoService.class);
service2.setRef(new DemoServiceImpl());
DubboBootstrap.newInstance().application(applicationConfig2)
.registry(new RegistryConfig("zookeeper://127.0.0.1:2181"))
.protocol(new ProtocolConfig(CommonConstants.DUBBO, 20880))
.service(service2)
.start();
复制代码
注意:两个应用使用了同样的dubbo端口20880,但是并不会触发端口冲突。
原因是Protocol是framework级别的。
Protocol成员变量serverMap在framework级别只有一份。
DubboProtocol#openServer:发现已经存在20880端口上的ProtocolServer,跳过。
DubboBootstrap底层从2.7.5全局单例变为ApplicationModel纬度下单例。
DubboBootstrap#newInstance:
1)FrameworkModel#defaultModel#newApplication:利用全局默认FrameworkModel创建一个新的ApplicationModel
2)DubboBootstrap#getInstance(ApplicationModel):创建DubboBootstrap放入全局缓存instanceMap
对于2.7的DubboBootstrap#getInstance api,用全局默认ApplicationModel创建DubboBootstrap。
2.xConfigManager管理所有AbstractConfig,到3.x这里只管理应用级别AbstractConfig。
比如应用配置ApplicationConfig、配置中心配置ConfigCenterConfig、注册中心配置RegistryConfig等等。
其中大部分配置对应实际实例都同样是应用级别对象。
比如元数据中心。
比如基于rpc服务级别的注册中心。
比如基于应用级别的注册中心。
比如配置中心。
应用级别kv配置Environment。
但是Protocol协议是框架级别。
服务端处理rpc请求的线程池(DubboServerHandler线程)是独立的,默认200固定线程池。
注意,线程池创建策略ThreadPool是框架级别的,但是线程池实例ExecutorService是应用级别的。
DubboBootstrap多实例支持后,可以在一个jvm进程中部署多个dubbo应用。
举个例子,下面这个issue因为多ioc容器的情况下,DubboBootstrap只能启动一次,导致部分服务没有暴露。
github.com/apache/dubb…
在配置ReferenceConfig时,可以指定所属模块,不同模块中的ReferenceConfig都是相互独立的。
注:该案例即使不建立多个module,也没有问题,只是说明api的使用。
System.setProperty("dubbo.application.service-discovery.migration", "FORCE_APPLICATION");
ApplicationConfig consumerApp = new ApplicationConfig("multi-module-app-consumer");
ReferenceConfig directReference = new ReferenceConfig<>();
directReference.setInterface(DemoService.class);
directReference.setScope("remote"); // 不走injvm调用
ReferenceConfig cacheReference = new ReferenceConfig<>();
cacheReference.setInterface(DemoService.class);
cacheReference.setScope("remote"); // 不走injvm调用
cacheReference.setCache("lfu"); // 走dubbo自带本地缓存
ApplicationModel applicationModel = FrameworkModel.defaultModel().newApplication();
DubboBootstrap bootstrap = DubboBootstrap.getInstance(applicationModel).application(consumerApp)
.registry(new RegistryConfig("zookeeper://127.0.0.1:2181"))
.protocol(new ProtocolConfig(CommonConstants.DUBBO, -1))
.reference(directReference, applicationModel.newModule()) // focus
.reference(cacheReference, applicationModel.newModule()) // focus
.start();
复制代码
每个ReferenceConfig会关联一个ModuleModel。
每个ModuleModel都有一个模块级别的ModuleConfigManager(类比ConfigManager),管理部分AbstractConfig,比如ReferenceConfig。
ModuleConfigManger管理部分AbstractConfig,包括ServiceConfig、ReferenceConfig等等。
这些配置都是从原来ConfigManager中拆分出来的,现在是模块级别配置。
Module级别也有自己的ModuleEnvironment。
Filter是模块级别的。
即便如此,很多Filter内部依赖的还是应用级别甚至全局变量,比如CacheFilter管理本地缓存。
CacheFactory走应用级别,所以本地缓存实际上是应用级别的。
ClusterFilter是3.0引入的,用于代替ClusterInterceptor(废弃)的扩展点,也是模块级别的。
Filter的javadoc,说明3.0在负载均衡前会经过ClusterFilter。
2.x,dubbo在很多地方都用到了serviceKey,即rpc服务唯一标识interface+group+version。
很多地方存储了serviceKey对应的全局配置(map结构),而dubbo并没有强制约束只能有一个serviceKey,导致部分配置相互覆盖。
举个例子,下面这个issue在异步调用场景下,无法正确获取到Reference上配置的回调方法。
github.com/apache/dubb…。
2.x因为一个serviceKey只能有一个ConsumerModel,所以无法获取到正确的AsyncMethodInfo。
而在3.0,ModuleServiceRepository存储模块级别下的ConsumerModel,从根本上避免了类似问题。
同一个serviceKey在不同module下可以存在多个ConsumerModel。
引用自dubbo官网:cn.dubbo.apache.org/zh-cn/overv…
当服务集群规模进一步扩大,带动IT治理结构进一步升级,需要实现动态部署,进行流动计算,现有分布式服务架构不会带来阻力。下图是未来可能的一种架构:
dubbo3.0中提到一个新角色Deployer,自动部署服务的本地代理。
目前Deployer只是一个接口,存在于应用jvm进程中,也并非是一个独立的角色。
下面是一个案例,通过ModuleDeployer单独启动和停止一个DemoService服务。
ApplicationConfig providerApp = new ApplicationConfig("app1");
providerApp.setRegisterMode("instance");
DubboBootstrap provider = DubboBootstrap.newInstance().application(providerApp)
.registry(new RegistryConfig("zookeeper://127.0.0.1:2181"))
.protocol(new ProtocolConfig(CommonConstants.DUBBO, 22880))
// 启动GreetingService
.service(t -> t.interfaceClass(GreetingService.class).ref((GreetingService) () -> "null")).start();
// 使用 ModuleDeployer 单独启动 DemoService
ModuleModel module1 = provider.getApplicationModel().newModule();
ServiceConfig demoService= new ServiceConfig<>();
demoService.setInterface(DemoService.class);
demoService.setRef(new DemoServiceImpl());
provider.service(demoService, module1);
module1.getDeployer().start();
Assert.assertTrue(module1.getDeployer().isStarted(), "deployer start fail");
// 使用 ModuleDeployer 单独停止 DemoService
module1.getDeployer().stop();
Assert.assertTrue(module1.getDeployer().isStopped(), "deployer stop fail");
复制代码
目前根据ScopeModel划分,支持两个纬度的Deployer,一个是应用,一个是模块。
ConfigScopeModelInitializer#initializeApplicationModel
在ApplicationModel构造期间,创建DefaultApplicationDeployer与当前ApplicationModel绑定。
DubboBootstrap#start:委派给底层ApplicationDeployer#start(2.7的启动流程都下放到Deployer)。
ApplicationDeployer#start:我们只考虑首次start的情况,分为两步
1)DefaultApplicationDeployer#initialize:应用Deployer自己初始化
2)DefaultApplicationDeployer#startModules:启动ApplicationModel下所有模块的ModuleDeployer
DefaultApplicationDeployer#initialize:分为以下几步
1)注册ShutdownHook
2)连接配置中心,创建动态配置
3)利用Environment中的kv配置,注入ConfigManager管理的AbstractConfig
4)初始化模块Deployer
5)连接元数据中心
DefaultApplicationDeployer#startModules:所有模块级别的Deployer启动
DubboBootstrap#destroy:委派给对应ApplicationModel#destroy
ApplicationModel#onDestroy:销毁应用model所有依赖
其中包含ApplicationDeployer和ModuleDeployer。
DefaultApplicationDeployer#preDestroy:从注册中心注销应用实例。
DefaultApplicationDeployer#postDestroy:当所有Module销毁后执行
1)销毁注册中心,取消订阅
2)销毁所有元数据中心
3)执行ShutdownHook
4)销毁线程池
DefaultModuleDeployer#start:可以由上层ApplicationDeployer#start触发,也可以主动启动
1)模块级别AbstractConfig填充
2)暴露服务
3)引用服务
同样,模块Deployer可以由上层ApplicationModel销毁触发,也可以手动触发。
DefaultModuleDeployer#stop:走Deployer销毁,实际底层是销毁ModuleModel
ModuleModel#onDestroy:销毁ModuleModel
需要注意的是ModuleModel#onDestroy每次执行完成,都会执行ApplicationModel#tryDestroy。
ApplicationModel#tryDestroy:判断如果没有ModuleModel存在,会销毁自身,所以ModuleModel销毁可能会触发ApplicationModel销毁。
DefaultModuleDeployer#preDestroy:状态变更
DefaultModuleDeployer#postDestroy:
1)取消暴露
2)取消引用
3)销毁ModuleServiceRepository
支持scope,每个ScopeModel管理自己scope下的扩展点。
通过ScopeModel查找扩展点,走双亲委派模式,优先从父Scope找,父Scope找不到才从自身Scope找。
每个ScopeModel持有一个BeanFactory,管理非扩展点bean。
通过BeanFactory查找bean,走ioc父子容器模式,子Scope先从自身找,自身找不到从父Scope找。
为了方便获取ScopeModel,ExtensionLoader支持构造注入、Aware注入ScopeModel。
此外,Wrapper包装扩展点在2.7.8版本做了增强,支持排序,支持条件包装。
3.0支持DubboBootstrap存在多个,本质上是一个ApplicationModel对应一个DubboBoostrap。
此外,同一个DubboBoostrap中还支持模块级别隔离,本质上是多个ModuleModel。
Deployer是3.0提出的新角色。
官网的解释是自动部署服务的本地代理。
目前在代码中是以Deployer接口的形式出现。
原来2.7中DubboBootstrap的启动和停止逻辑都下沉到Deployer中。
ApplicationDeployer,借助ApplicationModel,实现应用粒度的自动部署服务,负责应用级别的启动和停止。
比如连接配置中心、元数据中心。
ModuleDeployer,借助ModuleModel,实现模块粒度的自动部署服务,负责模块级别的启动和停止。
比如暴露服务、引用服务。
更新时间:2024-08-18
本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828
© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号