Dubbo 3.x源码(18)—Dubbo服务引用源码(1)

基于Dubbo 3.1,详细介绍了Dubbo服务的发布与引用的源码。

此前我们学习了Dubbo的服务导出的源码,在DubboBootstrapApplicationListener#startSync方法中,在调用了exportServices方法进行服务导出之后,立即调用了referServices方法进行服务引用,所以说Dubbo3.1中,服务导出和服务引用的入口是相同的,都是DubboDeployApplicationListener这个监听器。

在spring容器启动的最后一个步也就是refresh方法内部最后的finishRefresh方法中,将会向所有监听器发布一个ContextRefreshedEvent事件,表示容器刷新完毕。DubboDeployApplicationListener会监听该事件,进而触发服务的导出和引用。

Dubbo 3.x服务引用源码:

  1. Dubbo 3.x源码(11)—Dubbo服务的发布与引用的入口
  2. Dubbo 3.x源码(18)—Dubbo服务引用源码(1)

Dubbo 3.x服务发布源码:

  1. Dubbo 3.x源码(11)—Dubbo服务的发布与引用的入口
  2. Dubbo 3.x源码(12)—Dubbo服务发布导出源码(1)
  3. Dubbo 3.x源码(13)—Dubbo服务发布导出源码(2)
  4. Dubbo 3.x源码(14)—Dubbo服务发布导出源码(3)
  5. Dubbo 3.x源码(15)—Dubbo服务发布导出源码(4)
  6. Dubbo 3.x源码(16)—Dubbo服务发布导出源码(5)
  7. Dubbo 3.x源码(17)—Dubbo服务发布导出源码(6)

文章目录

  • 1 referServices引用服务入口
  • 2 SimpleReferenceCache#get获取缓存
  • 3 ReferenceConfig#get服务引用
  • 4 ReferenceConfig#init初始化服务引用
    • 4.1 appendConfig获取服务引用参数
  • 5 总结

1 referServices引用服务入口

该方法获取全部ReferenceConfigBase实例并遍历,判断是否应该初始化,判断init属性,默认true,继续判断是否异步引用,默认同步。最后通过引用缓存对象来进行服务引用,即referenceCache.get(rc)方法实现。

这个init属性在Dubbo2.x版本引入,该值用于判断是否在afterPropertiesSet()时饥饿初始化引用,否则等到有人注入或引用该实例时再初始化,默认false,即懒加载。但是在Dubbo3.1版本中,init仅被用于在shouldInit方法中,而且默认返回true,不再是默认懒加载,只有手动设置为false,才不会引入服务。因为此时dubbo服务的引入已不在ReferenceBean的 afterPropertiesSet方法中。

/*** DefaultModuleDeployer的方法* <p>* 服务引用*/
private void referServices() {//获取全部ReferenceConfigBase实例并遍历configManager.getReferences().forEach(rc -> {try {ReferenceConfig<?> referenceConfig = (ReferenceConfig<?>) rc;//如果还没刷新该引用配置if (!referenceConfig.isRefreshed()) {//刷新配置,即Dubbo配置的重写(基于优先级的覆盖)//设个方法我们在此前Dubbo配置的加载部分已经讲过了referenceConfig.refresh();}//是否应该初始化,判断init属性,默认trueif (rc.shouldInit()) {//如果模块消费端开启异步调用,或者消费者开启异步调用,默认都是false,即同步调用if (referAsync || rc.shouldReferAsync()) {ExecutorService executor = executorRepository.getServiceReferExecutor();CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {try {referenceCache.get(rc);} catch (Throwable t) {logger.error("5-9", "", "", "Failed to async export service config: " + getIdentifier() + " , catch error : " + t.getMessage(), t);}}, executor);asyncReferringFutures.add(future);}//通常的逻辑,通过引用缓存对象来进行服务引用else {referenceCache.get(rc);}}} catch (Throwable t) {logger.error("5-15", "", "", "Model reference failed: " + getIdentifier() + " , catch error : " + t.getMessage(), t);referenceCache.destroy(rc);throw t;}});
}

2 SimpleReferenceCache#get获取缓存

SimpleReferenceCache是一个对于单例的ReferenceConfigBase的缓存对象。该方法中的大概逻辑为:

  1. 首先构建缓存key,规则为 {group}/{InterfaceName}:{version} ,随后获取引用的服务接口Class。
  2. 判断是否是单例服务,如果是那么首先尝试从缓存中获取已创建的服务代理实例对象proxy。
  3. 如果没有缓存,那么将ReferenceConfigBase存入referenceTypeMap和referenceKeyMap这两个缓存map中,随后调用ReferenceConfigBase自身的get方法进行服务引用,获取服务代理实例对象。

可以看到,虽然我们是调用的SimpleReferenceCache#get方法,但是其内部会判断如果没有进行服务引用,那么会引用服务,间接的达成了我们服务引用的目的,同时还构建了缓存。而服务引用的关键方法就是ReferenceConfigBase#get方法。

/*** SimpleReferenceCache的方法** @param rc 服务消费者引用服务配置* @return 服务代理实例对象*/
@Override
@SuppressWarnings("unchecked")
public <T> T get(ReferenceConfigBase<T> rc) {//获取缓存key,规则为 {group}/{InterfaceName}:{version} String key = generator.generateKey(rc);//获取引用的服务接口ClassClass<?> type = rc.getInterfaceClass();//是否是单例,默认trueboolean singleton = rc.getSingleton() == null || rc.getSingleton();T proxy = null;// Check existing proxy of the same 'key' and 'type' first.//如果是单例if (singleton) {//那么首先尝试从缓存中获取已创建的服务代理实例对象proxy = get(key, (Class<T>) type);} else {logger.warn("Using non-singleton ReferenceConfig and ReferenceCache at the same time may cause memory leak. " +"Call ReferenceConfig#get() directly for non-singleton ReferenceConfig instead of using ReferenceCache#get(ReferenceConfig)");}//如果缓存没有服务代理实例对象if (proxy == null) {//引用服务类型到服务消费者引用服务配置的缓存referenceTypeMapList<ReferenceConfigBase<?>> referencesOfType = referenceTypeMap.computeIfAbsent(type, _t -> Collections.synchronizedList(new ArrayList<>()));referencesOfType.add(rc);//引用缓存key到服务消费者引用服务配置的缓存referenceKeyMapList<ReferenceConfigBase<?>> referenceConfigList = referenceKeyMap.computeIfAbsent(key, _k -> Collections.synchronizedList(new ArrayList<>()));referenceConfigList.add(rc);/** 调用ReferenceConfigBase自身的get方法进行服务引用,获取服务代理实例对象*/proxy = rc.get();}//返回服务代理实例对象,不需要特意存入某个换尺寸,因为每个ReferenceConfigBase自身会存储它的取服务代理实例对象return proxy;
}

3 ReferenceConfig#get服务引用

该方法获取接口代理引用对象实例,这里面使用双重检测锁来判断ref是否为null,如果为null,说明该服务还没有进行服务引用,那么调用init方法初始化引用服务。

/*** ReferenceConfig的方法** @return 代理引用服务实例*/
@Override
public T get() {//销毁检查if (destroyed) {throw new IllegalStateException("The invoker of ReferenceConfig(" + url + ") has already destroyed!");}//如果接口代理引用为nullif (ref == null) {// ensure start module, compatible with old api usage//确保启动模块,与旧的API使用兼容getScopeModel().getDeployer().start();//双重校验锁synchronized (this) {//如果接口代理引用为nullif (ref == null) {//初始化引用服务init();}}}return ref;
}

4 ReferenceConfig#init初始化服务引用

该方法用于初始化某个服务引用,大概逻辑为:

  1. 服务引用前初始化serviceMetadata服务元数据。
  2. 注册serviceDescriptor服务描述符到本地内存的服务仓库ModuleServiceRepositoryservices缓存中,即注册service
  3. 构建此服务对应的服务消费者模型consumerModel,并且注册到本地内存的服务仓库ModuleServiceRepositoryconsumers缓存中,即注册consumer
  4. 调用createProxy方法,根据服务引用参数map创建服务接口代理引用对象,并赋值给res。这是核心逻辑。
  5. 设置服务元数据的服务接口代理引用对象。
/*** ReferenceConfig的方法* <p>* 初始化引用服务*/
protected synchronized void init() {//如果已被初始化,那么直接返回if (initialized && ref != null) {return;}try {//如果还没刷新该引用配置if (!this.isRefreshed()) {//刷新配置,即Dubbo配置的重写(基于优先级的覆盖)//设个方法我们在此前Dubbo配置的加载部分已经讲过了this.refresh();}/** 1 服务引用前初始化serviceMetadata服务元数据*/// init serviceMetadata//根据consumer初始化服务元数据initServiceMetadata(consumer);//设置服务类型serviceMetadata.setServiceType(getServiceInterfaceClass());// TODO, uncomment this line once service key is unified//服务key: group/服务接口:版本号serviceMetadata.generateServiceKey();//附加服务引用所需的所有配置Map<String, String> referenceParameters = appendConfig();// init service-application mapping 从本地存储和url参数初始化服务应用程序映射//也就是MetadataServiceNameMappinginitServiceAppsMapping(referenceParameters);//获取Module级别的服务存储仓库,其内部保存着服务提供者和服务消费者的缓存ModuleServiceRepository repository = getScopeModel().getServiceRepository();/** 2 注册serviceDescriptor服务描述符到本地内存的服务仓库ModuleServiceRepository的services缓存中,即注册service*///服务描述符,通过它可以获取服务描述信息,例如服务提供的方法,服务接口名,服务接口Class等ServiceDescriptor serviceDescriptor;if (CommonConstants.NATIVE_STUB.equals(getProxy())) {serviceDescriptor = StubSuppliers.getServiceDescriptor(interfaceName);repository.registerService(serviceDescriptor);} else {//注册服务描述符到服务仓库内部的services集合中serviceDescriptor = repository.registerService(interfaceClass);}/** 3 构建此服务对应的服务消费者模型consumerModel,并且注册到本地内存的服务仓库ModuleServiceRepository的consumers缓存中,即注册consumer*/consumerModel = new ConsumerModel(serviceMetadata.getServiceKey(),//生成动态代理的策略,可以选择两种策略:jdk和javassistproxy,//服务描述符serviceDescriptor,//域模型getScopeModel(),//服务元数据serviceMetadata,//转换和聚合异步方法信息createAsyncMethodInfo(),//服务接口类加载器interfaceClassLoader);// Compatible with dependencies on ServiceModel#getReferenceConfig() , and will be removed in a future version.//兼容ServiceModel#getReferenceConfig()上的依赖项,并将在未来的版本中删除。consumerModel.setConfig(this);//注册服务消费者模型到服务仓库内部的consumers集合中repository.registerConsumer(consumerModel);//将引用参数存入服务元数据的附加数据中serviceMetadata.getAttachments().putAll(referenceParameters);/** 4 根据服务引用参数创建服务接口代理引用对象*/ref = createProxy(referenceParameters);//设置服务元数据的服务接口代理引用对象serviceMetadata.setTarget(ref);serviceMetadata.addAttribute(PROXY_CLASS_REF, ref);consumerModel.setDestroyRunner(getDestroyRunner());consumerModel.setProxyObject(ref);consumerModel.initMethodModels();//检查调用程序可用checkInvokerAvailable();} catch (Throwable t) {try {//销毁invokerif (invoker != null) {invoker.destroy();}} catch (Throwable destroy) {logger.warn("5-3", "", "", "Unexpected error occurred when destroy invoker of ReferenceConfig(" + url + ").", t);}//取消注册consumerModelif (consumerModel != null) {ModuleServiceRepository repository = getScopeModel().getServiceRepository();repository.unregisterConsumer(consumerModel);}//还原属性initialized = false;invoker = null;ref = null;consumerModel = null;serviceMetadata.setTarget(null);serviceMetadata.getAttributeMap().remove(PROXY_CLASS_REF);// Thrown by checkInvokerAvailable().if (t.getClass() == IllegalStateException.class &&t.getMessage().contains("No provider available for the service")) {// 2-2 - No provider available.logger.error("2-2", "server crashed", "", "No provider available.", t);}throw t;}initialized = true;
}

4.1 appendConfig获取服务引用参数

该方法用于获取当前的服务引用参数,用于后续的createProxy方法创建服务代理引用对象。

/*** ReferenceConfig的方法* <p>* 附加服务引用所需的所有配置到一个map中** @return 引用参数*/
private Map<String, String> appendConfig() {Map<String, String> map = new HashMap<>(16);//interface -> 服务接口全路径名map.put(INTERFACE_KEY, interfaceName);//side -> consumer  表示消费端map.put(SIDE_KEY, CONSUMER_SIDE);//添加运行时参数//dubbo -> Dubbo RPC协议版本,默认2.0.2//release -> Dubbo的实现版本,通常是jar版本//timestamp -> 当前时间戳毫秒值//pid -> 当前服务进程pidReferenceConfigBase.appendRuntimeParameters(map);//非泛化接口if (!ProtocolUtils.isGeneric(generic)) {String revision = Version.getVersion(interfaceClass, version);if (StringUtils.isNotEmpty(revision)) {map.put(REVISION_KEY, revision);}String[] methods = methods(interfaceClass);if (methods.length == 0) {logger.warn("5-4", "", "", "No method found in service interface: " + interfaceClass.getName());map.put(METHODS_KEY, ANY_VALUE);} else {map.put(METHODS_KEY, StringUtils.join(new HashSet<>(Arrays.asList(methods)), COMMA_SEPARATOR));}}//Application配置AbstractConfig.appendParameters(map, getApplication());//Module配置AbstractConfig.appendParameters(map, getModule());//consumer配置AbstractConfig.appendParameters(map, consumer);//reference配置AbstractConfig.appendParameters(map, this);appendMetricsCompatible(map);String hostToRegistry = ConfigUtils.getSystemProperty(DUBBO_IP_TO_REGISTRY);if (StringUtils.isEmpty(hostToRegistry)) {hostToRegistry = NetUtils.getLocalHost();} else if (isInvalidLocalHost(hostToRegistry)) {throw new IllegalArgumentException("Specified invalid registry ip from property:" + DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);}map.put(REGISTER_IP_KEY, hostToRegistry);if (CollectionUtils.isNotEmpty(getMethods())) {for (MethodConfig methodConfig : getMethods()) {AbstractConfig.appendParameters(map, methodConfig, methodConfig.getName());String retryKey = methodConfig.getName() + ".retry";if (map.containsKey(retryKey)) {String retryValue = map.remove(retryKey);if ("false".equals(retryValue)) {map.put(methodConfig.getName() + ".retries", "0");}}}}return map;
}

5 总结

本次我们学习了Dubbo 3.x服务引用的入口源码。我们知道ReferenceConfig#init用于初始化服务引用,其中调用createProxy方法,**根据服务引用参数map创建服务接口代理引用对象,并赋值给res,**这是核心逻辑,我们下文再学习!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/786476.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【ZZULIOJ】1025: 最大字符(Java)

目录 题目描述 输入 输出 样例输入 Copy 样例输出 Copy code 题目描述 给你三个ASCII字符(不含空白字符:包括空格、制表符\t、回车换行符\n)&#xff0c;找出其中最大的那个 输入 输入包含三个字符&#xff0c;之间有一个空格隔开。 输出 输出ASCII码最大的那个字符…

神经网络汇聚层

文章目录 最大汇聚层平均汇聚层自适应平均池化层 最大汇聚层 汇聚窗口从输入张量的左上角开始&#xff0c;从左往右、从上往下的在输入张量内滑动。在汇聚窗口到达的每个位置&#xff0c;它计算该窗口中输入子张量的最大值或平均值。计算最大值或平均值是取决于使用了最大汇聚…

RISC-V/ARM mcu OpenOCD 调试架构解析

Risc-v/ARM mcu OpenOCD 调试架构解析 最近有使用到risc-v的单片机&#xff0c;所以了解了下risc-v单片机的编译与调试环境的搭建&#xff0c;面试时问到risc-v的调试可参看以下内容。 risc-v根据官方的推荐&#xff0c;调试器服务是选择OpenOCD&#xff0c;DopenOCD(开放片上…

【ROS笔记3】节点 和 命名空间 (通俗理解运用)

1. 前言 在ROS中,节点、话题、服务、参数等都可以有自己的命名空间(namespace)。命名空间是ROS用来组织和隔离不同资源的一种方式,确保了系统中的名字是唯一的,并允许同样的结构在不同的上下文中被重用。这就像在真实世界中的邮政系统,同一个城市里可以有多条同名的“梅花…

Python反爬案例——验证码的识别

验证码的识别 使用打码平台识别验证码 利用打码平台可以轻松识别各种各样的验证码&#xff0c;图形验证码、滑动验证码、点选验证码和逻辑推理验证码。打码平台提供了一系列API&#xff0c;只需要向API上传验证码图片&#xff0c;它便会返回对应的识别结果。 使用超级鹰平台…

深入理解指针1:指针变量、指针运算、野指针、const修饰指针

生活中我们把门牌号也叫地址&#xff0c;在计算机中我们把内存单元的编号也称为地址。C语⾔中给地址起 了新的名字叫&#xff1a;指针。 所以我们可以理解为&#xff1a;内存单元的编号地址指针 1、指针变量 我们知道的是&#xff1a;数组名是数组首元素的地址。也就是说&…

构建高可用性数据库架构:深入探索Oracle Active Data Guard(ADG)

随着企业数据规模的不断增长和业务的复杂化&#xff0c;数据库的高可用性和可靠性变得尤为重要。Oracle Active Data Guard&#xff08;ADG&#xff09;作为Oracle数据库提供的一种高可用性解决方案&#xff0c;在实时备份和灾难恢复方面发挥着重要作用。本文将深入探讨ADG的原…

中断服务程序模板

通常定时器初始化过程如下: ①对 TMOD赋值,以确定TO和T1的工作方式。 ②计算初值,并将初值写入THO、TLO或TH1、TL1。 ③中断方式时&#xff0c;则对IE赋值&#xff0c;开放中断。 ④使TRO或TR1置位&#xff0c;启动定时器/计数器定时或计数。 代码 利用定时器0工作方式1&…

轻松设置Facebook自动隐藏评论和删除评论功能

Facebook作为海外营销的最大流量平台之一&#xff0c;是很多跨境卖家争夺的市场&#xff0c;希望可以通过Facebook这个全球性的平台来推广自己的产品或服务。身处这个竞争激烈的市场&#xff0c;任何一条负面评论或不当言论出现在你的品牌页面上都可能影响到品牌形象&#xff0…

臻奶惠无人售货机:新零售时代的便捷消费革命

臻奶惠无人售货机&#xff1a;新零售时代的便捷消费革命 在新零售的浪潮中&#xff0c;智能无人售货机作为一个创新的消费模式&#xff0c;已经成为距离消费者最近的便捷购物点之一。这种模式不仅能够满足居民对消费升级的需求&#xff0c;还能通过建立多样化和多层次的消费体…

k8s练习-创建一个Deployment

创建Deployment 创建一个nginx deployment [rootk8s-master home]# cat nginx-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata:name: nginx-deployment spec:selector:matchLabels:app: nginx # 配置pod的labelsreplicas: 2 # 声明2个副本template:metada…

spring boot自动配置原理-怎样回答这个问题

首先我们说一下自动配置的概念。 自动配置&#xff1a;遵循约定大约配置的原则&#xff0c;在boot程序启动后&#xff0c;起步依赖中的一些bean对象会自动注入到ioc容器 例子 程序引入spring-boot-starter-web 起步依赖&#xff0c;启动后&#xff0c;会自动往ioc容器中注入…

记一次 pdfplumber 内存泄漏导致的服务器宕机

有一个项目需求&#xff0c;要在每天凌晨5点的时候执行一个任务&#xff0c;获取一系列的PDF文件并解析。 后端是Django框架&#xff0c;定时任务用Celery来实现的。 本地跑没什么问题&#xff0c;但是一放到服务器上跑就会宕机&#xff0c;而且是毫无征兆的宕机&#xff0c;…

黑马HTMLCSS基础

黑马的笔记和资料都是提供好了的&#xff0c;这个文档非常适合回顾复习。我在黑马提供的笔记上做了一些微不足道的补充&#xff0c;以便自己复习查阅。该笔记比较重要的部分是 表单&#xff0c;http请求 第一章. HTML 与 CSS HTML 是什么&#xff1a;即 HyperText Markup lan…

WPF —— 动画

wpf动画类型 1<类型>Animation这些动画称为from/to/by动画或者叫基本动画&#xff0c;他们会在起始值或者结束值进行动画处理&#xff0c;常用的例如 <DoubleAnimation> 2 <类型>AnimationUsingKeyFrames: 关键帧动画&#xff0c;功能要比from/to这些动画功…

正则表达式 (regex) 简介和基本用法

正则表达式 (regex) 是用于模式匹配和文本操作的强大工具。 它们广泛应用于编程、文本处理、数据验证等领域。 以下是正则表达式的一些常见用例&#xff1a; 模式匹配&#xff1a;正则表达式可用于搜索文本中的特定模式。 例如&#xff0c;在文档中查找电子邮件地址、URL、电话…

使用虚拟引擎为AR体验提供动力

Powering AR Experiences with Unreal Engine ​​​​​​​ 目录 1. 虚拟引擎概述 2. 虚拟引擎如何为AR体验提供动力 3. 虚拟引擎中AR体验的组成部分是什么&#xff1f; 4. 使用虚拟引擎创建AR体验 5. 虚拟引擎中AR的优化提示 6. 将互动性融入AR与虚拟引擎 7. 在AR中…

AutoGluon

官网 amazon (base) PS C:\Users\gg葱> conda env list # conda environments: # pytorch1 C:\Users\gg葱\.conda\envs\pytorch1 base * D:\ANCDD:\Documents\LMm\env\pytorch2(base) PS C:\Users\gg葱> conda create --prefixD:/Doc…

5G PLMN相关概念

PLMN PLMN&#xff08;Public Land Mobile Network&#xff0c;公用陆地移动网络&#xff09;&#xff0c;是由政府或其批准的经营者为公众提供陆地移动通信业务而建立、经营的网络。PLMN与公众交换电话网&#xff08;PSTN&#xff09;互连&#xff0c;形成整个地区或国家规模…

OpenCV中的模块:三维重建-SFM

运动中恢复结构&#xff08;SFM&#xff09;可以用来重建目标的稀疏点云并为后续的稠密重建提供相对精度更高的种子点&#xff0c;也可以用于里程计等估计相机本身的位姿。同样&#xff0c;除了采用结构光进行三维重建外&#xff0c;还用到了OpenMVG C/PMVS和COLMAP。在浏览Op…