dubbo 服务消费原理分析之应用级服务发现
文章目录
- 前言
- 一、MigrationRuleListener
- 1、迁移状态模型
- 2、Provider 端升级
- 3、Consumer 端升级
- 4、服务消费选址
- 5、MigrationRuleListener.onRefer
- 6、MigrationRuleHandler.doMigrate
- 6、MigrationRuleHandler.refreshInvoker
- 7、MigrationClusterInvoker.migrateToApplicationFirstInvoker
- 8、MigrationInvoker.refreshInterfaceInvoker
- 9、MigrationInvoker.refreshServiceDiscoveryInvoker
- 10、MigrationInvoker.calcPreferredInvoker
- 11、RegistryProtocol.getServiceDiscoveryInvoker
前言
文章基于3.1.0版本进行分析
<dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo</artifactId><version>3.1.0</version></dependency>
一、MigrationRuleListener
1、迁移状态模型
在 Dubbo 3 之前地址注册模型是以接口级粒度注册到注册中心的,而 Dubbo 3 全新的应用级注册模型注册到注册中心的粒度是应用级的。从注册中心的实现上来说是几乎不一样的,这导致了对于从接口级注册模型获取到的 invokers 是无法与从应用级注册模型获取到的 invokers 进行合并的。
为了帮助用户从接口级往应用级迁移,Dubbo 3 设计了 Migration 机制,基于三个状态的切换实现实际调用中地址模型的切换
当前共存在三种状态,FORCE_INTERFACE(强制接口级),APPLICATION_FIRST(应用级优先)、FORCE_APPLICATION(强制应用级)。
FORCE_INTERFACE:只启用兼容模式下接口级服务发现的注册中心逻辑,调用流量 100% 走原有流程
APPLICATION_FIRST:开启接口级、应用级双订阅,运行时根据阈值和灰度流量比例动态决定调用流量走向
FORCE_APPLICATION:只启用新模式下应用级服务发现的注册中心逻辑,调用流量 100% 走应用级订阅的地址
2、Provider 端升级
在不改变任何 Dubbo 配置的情况下,可以将一个应用或实例升级到 3.x 版本,升级后的 Dubbo 实例会默保保证与 2.x 版本的兼容性,即会正常注册 2.x 格式的地址到注册中心,因此升级后的实例仍会对整个集群仍保持可见状态。
3、Consumer 端升级
对于双订阅的场景,消费端虽然可同时持有 2.x 地址与 3.x 地址,但选址过程中两份地址是完全隔离的:要么用 2.x 地址,要么用 3.x 地址,不存在两份地址混合调用的情况,这个决策过程是在收到第一次地址通知后就完成了的。
4、服务消费选址
调用发生前,框架在消费端会有一个“选址过程”,注意这里的选址和之前 2.x 版本是有区别的,选址过程包含了两层筛选
- 先进行地址列表(ClusterInvoker)筛选(接口级地址 or 应用级地址)
- 再进行实际的 provider 地址(Invoker)筛选
双注册带来的资源消耗
双注册不可避免的会带来额外的注册中心存储压力,但考虑到应用级地址发现模型的数据量在存储方面的极大优势,即使对于一些超大规模集群的用户而言,新增的数据量也并不会带来存储问题。总体来说,对于一个普通集群而言,数据增长可控制在之前数据总量的 1/100 ~ 1/1000
以一个中等规模的集群实例来说: 2000 实例、50个应用(500 个 Dubbo 接口,平均每个应用 10 个接口)。
假设每个接口级 URL 地址平均大小为 5kb,每个应用级 URL 平均大小为 0.5kb
老的接口级地址量:2000 * 500 * 5kb ≈ 4.8G
新的应用级地址量:2000 * 50 * 0.5kb ≈ 48M
双注册后仅仅增加了 48M 的数据量。
接下来分析MigrationRuleListener的实际处理逻辑
5、MigrationRuleListener.onRefer
@Overridepublic void onRefer(RegistryProtocol registryProtocol, ClusterInvoker<?> invoker, URL consumerUrl, URL registryURL) {MigrationRuleHandler<?> migrationRuleHandler = handlers.computeIfAbsent((MigrationInvoker<?>) invoker, _key -> {((MigrationInvoker<?>) invoker).setMigrationRuleListener(this);return new MigrationRuleHandler<>((MigrationInvoker<?>) invoker, consumerUrl);});// 迁移规则执行 rule是封装了迁移的配置规则的信息对应类型MigrationRule类型,在初始化对象的时候进行了配置初始化migrationRuleHandler.doMigrate(rule);}
6、MigrationRuleHandler.doMigrate
从url中读取迁移方式和阈值,用于进行迁移的调用决策
- 获取迁移方式
- 获取决策阈值(默认-1.0)
public synchronized void doMigrate(MigrationRule rule) {if (migrationInvoker instanceof ServiceDiscoveryMigrationInvoker) {refreshInvoker(MigrationStep.FORCE_APPLICATION, 1.0f, rule);return;}// initial step : APPLICATION_FIRST// 迁移方式,MigrationStep 一共有3种枚举情况:FORCE_INTERFACE, APPLICATION_FIRST, FORCE_APPLICATION// 默认 APPLICATION_FIRST 开启接口级、应用级双订阅MigrationStep step = MigrationStep.APPLICATION_FIRST;float threshold = -1f;try { // URL中获取迁移方式step = rule.getStep(consumerURL);// threshold: 决策阈值(默认-1.0)计算与获取threshold = rule.getThreshold(consumerURL);} catch (Exception e) {logger.error("Failed to get step and threshold info from rule: " + rule, e);}// 刷新调用器对象 来进行决策服务发现模式if (refreshInvoker(step, threshold, rule)) {// refresh success, update rulesetMigrationRule(rule);}}
6、MigrationRuleHandler.refreshInvoker
通过迁移配置和当前提供者注册信息来决定创建什么类型的调用器对象(Invoker)来为后续服务调用做准备
private boolean refreshInvoker(MigrationStep step, Float threshold, MigrationRule newRule) {if (step == null || threshold == null) {throw new IllegalStateException("Step or threshold of migration rule cannot be null");}MigrationStep originStep = currentStep;if ((currentStep == null || currentStep != step) || !currentThreshold.equals(threshold)) {boolean success = true;switch (step) {// 接口和应用双订阅,默认case APPLICATION_FIRST:migrationInvoker.migrateToApplicationFirstInvoker(newRule);break;// 仅应用case FORCE_APPLICATION:success = migrationInvoker.migrateToForceApplicationInvoker(newRule);break;// 仅接口case FORCE_INTERFACE:default:success = migrationInvoker.migrateToForceInterfaceInvoker(newRule);}if (success) {// 设置迁移模式和阈值setCurrentStepAndThreshold(step, threshold);logger.info("Succeed Migrated to " + step + " mode. Service Name: " + consumerURL.getDisplayServiceKey());report(step, originStep, "true");} else {// migrate failed, do not save new step and rulelogger.warn("Migrate to " + step + " mode failed. Probably not satisfy the threshold you set "+ threshold + ". Please try re-publish configuration if you still after check.");report(step, originStep, "false");}return success;}// ignore if step is same with previous, will continue override rule for MigrationInvokerreturn true;}
7、MigrationClusterInvoker.migrateToApplicationFirstInvoker
默认使用接口级和应用级双订阅来兼容迁移的服务的
对应实现类 MigrationInvoker.migrateToApplicationFirstInvoker
@Overridepublic void migrateToApplicationFirstInvoker(MigrationRule newRule) {CountDownLatch latch = new CountDownLatch(0);// 刷新接口级InvokerrefreshInterfaceInvoker(latch);// 刷新应用级InvokerrefreshServiceDiscoveryInvoker(latch);// directly calculate preferred invoker, will not wait until address notify// calculation will re-occurred when address notify later// 计算当前使用应用级还是接口级服务发现的Invoker对象calcPreferredInvoker(newRule);}
8、MigrationInvoker.refreshInterfaceInvoker
刷新接口级Invoker
protected void refreshInterfaceInvoker(CountDownLatch latch) {// 去除旧的监听clearListener(invoker);// 需要更新if (needRefresh(invoker)) {if (logger.isDebugEnabled()) {logger.debug("Re-subscribing interface addresses for interface " + type.getName());}if (invoker != null) {invoker.destroy();}// 创建invokerinvoker = registryProtocol.getInvoker(cluster, registry, type, url);}setListener(invoker, () -> {latch.countDown();if (reportService.hasReporter()) {reportService.reportConsumptionStatus(reportService.createConsumptionReport(consumerUrl.getServiceInterface(), consumerUrl.getVersion(), consumerUrl.getGroup(), "interface"));}if (step == APPLICATION_FIRST) {calcPreferredInvoker(rule);}});}
9、MigrationInvoker.refreshServiceDiscoveryInvoker
刷新应用级Invoker
protected void refreshServiceDiscoveryInvoker(CountDownLatch latch) {clearListener(serviceDiscoveryInvoker);if (needRefresh(serviceDiscoveryInvoker)) {if (logger.isDebugEnabled()) {logger.debug("Re-subscribing instance addresses, current interface " + type.getName());}if (serviceDiscoveryInvoker != null) {serviceDiscoveryInvoker.destroy();}// 获取应用级invokerserviceDiscoveryInvoker = registryProtocol.getServiceDiscoveryInvoker(cluster, registry, type, url);}setListener(serviceDiscoveryInvoker, () -> {latch.countDown();if (reportService.hasReporter()) {reportService.reportConsumptionStatus(reportService.createConsumptionReport(consumerUrl.getServiceInterface(), consumerUrl.getVersion(), consumerUrl.getGroup(), "app"));}if (step == APPLICATION_FIRST) {calcPreferredInvoker(rule);}});}
10、MigrationInvoker.calcPreferredInvoker
计算当前使用应用级还是接口级服务发现的Invoker对象
private synchronized void calcPreferredInvoker(MigrationRule migrationRule) {if (serviceDiscoveryInvoker == null || invoker == null) {return;}// 通过阈值指定invokerSet<MigrationAddressComparator> detectors = ScopeModelUtil.getApplicationModel(consumerUrl == null ? null : consumerUrl.getScopeModel()).getExtensionLoader(MigrationAddressComparator.class).getSupportedExtensionInstances();if (CollectionUtils.isNotEmpty(detectors)) {// pick preferred invoker// the real invoker choice in invocation will be affected by promotionif (detectors.stream().allMatch(comparator -> comparator.shouldMigrate(serviceDiscoveryInvoker, invoker, migrationRule))) {this.currentAvailableInvoker = serviceDiscoveryInvoker;} else {this.currentAvailableInvoker = invoker;}}}
11、RegistryProtocol.getServiceDiscoveryInvoker
这里主要做了两件事
- 创建注册中心目录
- 通过代理对象的invoker
public <T> ClusterInvoker<T> getServiceDiscoveryInvoker(Cluster cluster, Registry registry, Class<T> type, URL url) {// 创建注册中心目录DynamicDirectory<T> directory = new ServiceDiscoveryRegistryDirectory<>(type, url);// 创建invokerreturn doCreateInvoker(directory, cluster, registry, type);}
服务目录(RegistryDirectory),负责框架与注册中心的订阅,并动态更新本地Invoker列表、路由列表、配置信息的逻辑
对服务目录的分析在 dubbo 服务消费原理分析之服务目录 讲解
相关文章:

dubbo 服务消费原理分析之应用级服务发现
文章目录 前言一、MigrationRuleListener1、迁移状态模型2、Provider 端升级3、Consumer 端升级4、服务消费选址5、MigrationRuleListener.onRefer6、MigrationRuleHandler.doMigrate6、MigrationRuleHandler.refreshInvoker7、MigrationClusterInvoker.migrateToApplicationFi…...

QT如何在对话框中插入表格
在Qt中,如果你想要在对话框中插入表格,通常会使用QTableWidget或QTableView结合QStandardItemModel(对于QTableView)或直接在QTableWidget中操作。这里,我将介绍如何使用QTableWidget在对话框中插入表格,因…...

如何使用SSHFS通过SSH挂载远程文件系统?
SHFS(SSH 文件系统)是一款功能强大的工具,它允许用户通过 SSH 挂载远程文件系统,从而提供一种安全便捷的方式来访问远程文件,就像访问本地文件一样。本文将引导您完成使用 SSHFS 挂载远程文件系统的过程,为…...

SEELE 框架是
SEELE 框架是一个相对新颖的组织管理和优化框架,旨在帮助团队或企业更好地实现目标。它的核心思想是通过科学的管理方法来提升组织的执行力和决策能力。以下是对 SEELE 框架的详细讲解,包括定义、内容、实施步骤、实施策略以及推荐的实践方法和工具。 一…...

高教社杯数模竞赛特辑论文篇-2013年B题:碎纸复原模型与算法(续)(附MATLAB代码实现)
目录 4.3 三维碎纸复原模型 4.3.1 三维模型的降维 4.3.2 三维碎纸复原算法 4.3.3 模型求解 五、模型改进与推广 5.1 模型优点 5.2 模型缺点 5.3 模型改进 5.3.1 适用彩色图片的改进 5.3.2 最小干预度算法 5.4 模型推广 参考文献 代码实现 模拟退火法代码 GUI 程序代码 层次特征…...

Java操作Miscrosoft Office各类文件格式的开源免费工具库
Aspose.Words库 是一个商业Java库,还封装了常用的word、pdf、防伪码、水印等诸多功能。Apache 库需要注意的前置问题 问题1:Word的两个格式doc和docx,POI并没有提供统一的处理类。分别用 HWPFDocument 处理doc文档,用 XWPFTempl…...

Redis 缓存淘汰算法策略详解
引言 Redis 作为一款高性能的内存数据库,在处理大量数据时,由于内存有限,需要在数据达到设定的内存上限后,使用缓存淘汰策略来决定哪些数据应该被移除,以腾出空间存储新的数据。这一过程被称为缓存淘汰,通…...

Kubernetes PV生命周期的四个阶段
Kubernetes PV生命周期的四个阶段 1. Available(可用)2. Bound(已绑定)3. Released(已释放)4. Failed(失败)💖The Begin💖点点关注,收藏不迷路💖 在Kubernetes中,PersistentVolume(PV)的生命周期主要包括以下四个阶段: 1. Available(可用) 状态:PV刚创建…...

Azure OpenAI models being unable to correctly identify model
题意:Azure OpenAI模型无法正确识别模型。 问题背景: In Azure OpenAI Studio, while I am able to deploy a GPT-4 instance, the responses are based solely on GPT-3.5 Turbo. I test the same prompts in my personal ChatGPT sub and it returns …...

项目小结二()
一.个人信息的界面 这里可以进行用户信息的修改,并渲染数据上去 二.这两天,出现的问题: 1.mybatis中 字段取别名 (还没验证,是否正确) 问题描述:由于实体类中的变量名,与数据库中…...

《论层次架构及其在软件系统中的应用》写作框架,软考高级系统架构设计师
论文真题 层次架构作为软件系统设计的一种基本模式,对于实现系统的模块化、可维护性和可扩展性具有至关重要的作用。在软件系统的构建过程中,采用层次架构不仅可以使系统结构更加清晰,还有助于提高开发效率和质量。因此,对层次架构的理解和应用是软件工程师必备的技能之一…...

校篮球联赛系统小程序的设计
管理员账户功能包括:系统首页,个人中心,管理员管理,公告管理,基础数据管理,球队管理,球员管理,赛事信息管理,用户管理,轮播图信息 微信端账号功能包括&#…...

在 HKCR 新增项和值
HKEY_CLASSES_ROOT HKEY_CURRENT_USER\Software\Classes ∪ HKEY_LOCAL_MACHINE\Software\Classes ; 1. Win11 HKCR 根键默认是 System 所有, Win10 HKCR 根键默认是 Administrators 所有。 ; 2. 以 System、管理员 还是 普通用户 登录系统? ; 在注册表里&#x…...

Spring Boot 注解探秘:JSON 处理的魔法世界
在 Spring Boot 应用开发中,高效处理 JSON 数据同样至关重要。Spring Boot 不仅在 Bean 管理方面表现出色,提供强大的注解系统以助力开发者轻松管理 Bean 的生命周期和依赖注入,在 JSON 数据处理上也毫不逊色。本文将深入探讨 Spring Boot 中…...

利用AI驱动智能BI数据可视化-深度评测Amazon Quicksight(一)
项目简介 随着生成式人工智能的兴起,传统的 BI 报表功能已经无法满足用户对于自动化和智能化的需求,今天我们将介绍亚马逊云科技平台上的AI驱动数据可视化神器 – Quicksight,利用生成式AI的能力来加速业务决策,从而提高业务生产…...

Linux常见指令、ls、pwd、cd、touch、mkdir、rmdir、rm等的介绍
文章目录 前言一、ls二、pwd三、cd四、touch五、 mkdir六、rmdir七、rm总结 前言 Linux常见指令、ls、pwd、cd、touch、mkdir、rmdir、rm等的介绍 一、ls 列出该目录下的所有子目录与文件。对于文件,将列出文件名以及其他信息 -a 列出目录下的所有文件,…...

【Kubernetes】常见面试题汇总(八)
目录 22.简述 Kubernetes 中 Pod 的健康检查方式? 23.简述 Kubernetes Pod 的 LivenessProbe 探针的常见方式? 24.简述 Kubernetes Pod 的常见调度方式? 22.简述 Kubernetes 中 Pod 的健康检查方式? 对 Pod 的健康检查可以通过…...

CentOS 7系统双网卡配置动态链路聚合(bond4)
一、应用场景 在机房建设时,服务器的网卡需要配置成bond4,可以使用我下面的配置文件和脚本来进行配置,简化配置流程。 bond4,即动态链路聚合,它可以将服务器上的两个物理网卡聚合为一个,两个网口逻辑成一…...

ubuntu 20.04 一直卡在登录界面,即使密码正确也无法登录(失败记录)
ubuntu 20.04 一直卡在登录界面,即使密码正确也无法登录 这次是装实体机,一次失败的尝试。。。 名称型号CPUIntel Xeon E5-2673 V3GPURTX 3060 mobile 安装的时候不要选install third-party software for graphics and Wi-fi hardware and additional …...

【深度学习】神经网络-怎么理解DNN、CNN、RNN?
怎么分清DNN、RNN、CNN? 最“大”的概念是人工神经网络(Artificial Neural Network, ANN),它是较为广泛的术语,通常指的是一类模拟生物神经网络的数学模型,其中包括神经元、权重和连接。在这个术语下&#…...

组织应在其网络安全策略中考虑MLSecOps吗?
随着越来越多的组织拥抱人工智能 (AI) 和机器学习 (ML) 来优化操作并获得竞争优势,关于如何最好地保障这一强大技术的安全性的问题也日益受到关注。其中的核心是用于训练ML模型的数据,这对模型的行为和性能有着根本影响。因此,组织需要密切关…...

Windows安装HeidiSQL教程(图文)
一、软件简介 HeidiSQL是一款开源的数据库管理工具,主要用于管理MySQL、MariaDB、SQL Server、PostgreSQL和SQLite等数据库系统。它提供了直观的用户界面,使用户可以轻松地连接到数据库服务器、执行SQL查询、浏览和编辑数据、管理数据库结构等操作。 跨…...

存储课程学习笔记5_iouring的练习(io_uring,rust_echo_bench,fio)
我们知道,在处理大量高并发网络时,一般考虑并发,以及设计对应的方案(比如select,poll,epoll)等。 那么如果频繁进行文件或者磁盘的操作,如何考虑性能和并发,这里就可以考虑用到io_uring。 0&a…...

前端HTML+CSS+JS的入门学习
一.HTML HTML(HyperText Markup Language)即超文本标记语言,是用于创建网页和网页应用程序的标准标记语言。它不是一种编程语言,而是一种标记语言,通过一系列的元素(elements)来告诉浏览器如何…...

通信电路和信道的区别与联系
通信电路和信道的区别 区分通信电路和信道主要在于理解它们的功能范围与作用机制。通信电路侧重于信息的处理和信号的调整,而信道更侧重于信号的实际传输。电路可以视为信道的接入点,但它们的设计和优化考量各不相同。例如,电路设计重视的传…...

基于深度学习的蛋白质结构预测
基于深度学习的蛋白质结构预测是利用深度学习模型来预测蛋白质的三维结构,这在生物学和药物研发领域具有重要意义。蛋白质的功能在很大程度上取决于其三维结构,准确预测蛋白质结构可以帮助科学家理解蛋白质的功能和相互作用,并加速药物发现的…...

基于 Redis 的分布式锁实现原理及步骤
实现分布式锁的目的是在分布式系统中,保证多个节点之间对共享资源的并发访问是互斥的。常用的分布式锁实现方式有以下几种:基于数据库、基于 Redis、基于 Zookeeper。下面详细介绍基于 Redis 的分布式锁实现原理及步骤。 一、Redis 分布式锁原理 唯一性…...

21_动态规划与数据结构结合
菜鸟:老鸟,我最近在处理一个数据操作时遇到了性能问题。我需要计算一个数组中某些子数组的和,但直接计算太慢了,有没有什么更高效的方法? 老鸟:你提到的这个问题其实可以通过动态规划结合数据结构来解决。…...

React与Vue的对比
异同总结 相同点: 都有组件化思想 都支持服务器端渲染 都有Virtual DOM(虚拟dom) 数据驱动视图 都有支持native的方案:Vue的weex、React的React native 都有自己的构建工具:Vue的vue-cli、React的Create React A…...

精密量测软件(仿KLA免费浏览器程序ProfilmOnline)
KLA在线软件分析图 软件仿KLA公司免费浏览器软件ProfilmOnline,软件地址ProfilmOnline - 用于3D轮廓仪和AFM的表面成像、分析和测量软件 可以直接从profilmonline上下载3D图加载对比分析,当前已完成的内容有 1、调平 2、尖峰去噪 3、能量密度图&…...