当前位置: 首页 > news >正文

8、Nacos服务注册服务端源码分析(七)

本文收录于专栏 Nacos 中 。

文章目录

  • 前言
  • 确定前端路由
  • CatalogController.listDetail()
  • ServiceManager
  • 总结


前言

前文我们分析了Nacos中客户端注册时数据分发的设计链路,本文根据Nacos前端页面请求,看下前端页面中的服务列表的数据源于哪里。

确定前端路由

我们已经向Nacos中注册了一个服务,现在去前端确定查询的路由是什么
在这里插入图片描述
确定前端请求路由:/nacos/v1/ns/catalog/services
通过路由确定后端代码位置:

package com.alibaba.nacos.naming.controllers;
CatalogController.listDetail()

CatalogController.listDetail()

/*** List service detail information.** @param withInstances     whether return instances* @param namespaceId       namespace id* @param pageNo            number of page* @param pageSize          size of each page* @param serviceName       service name* @param groupName         group name* @param containedInstance instance name pattern which will be contained in detail* @param hasIpCount        whether filter services with empty instance* @return list service detail*/
@Secured(action = ActionTypes.READ)
@GetMapping("/services")
public Object listDetail(@RequestParam(required = false) boolean withInstances,@RequestParam(defaultValue = Constants.DEFAULT_NAMESPACE_ID) String namespaceId,@RequestParam(required = false) int pageNo, @RequestParam(required = false) int pageSize,@RequestParam(name = "serviceNameParam", defaultValue = StringUtils.EMPTY) String serviceName,@RequestParam(name = "groupNameParam", defaultValue = StringUtils.EMPTY) String groupName,@RequestParam(name = "instance", defaultValue = StringUtils.EMPTY) String containedInstance,@RequestParam(required = false) boolean hasIpCount) throws NacosException {//前端withInstances传的是false,不走这个分支if (withInstances) {return judgeCatalogService().pageListServiceDetail(namespaceId, groupName, serviceName, pageNo, pageSize);}//确定是走的这里获取的服务列表return judgeCatalogService().pageListService(namespaceId, groupName, serviceName, pageNo, pageSize, containedInstance, hasIpCount);
}

查看judgeCatalogService().pageListService(namespaceId, groupName, serviceName, pageNo, pageSize, containedInstance, hasIpCount);的实现:

@Override
public Object pageListService(String namespaceId, String groupName, String serviceName, int pageNo, int pageSize,String instancePattern, boolean ignoreEmptyService) throws NacosException {ObjectNode result = JacksonUtils.createEmptyJsonNode();List<ServiceView> serviceViews = new LinkedList<>();//获取服务列表Collection<Service> services = patternServices(namespaceId, groupName, serviceName);if (ignoreEmptyService) {services = services.stream().filter(each -> 0 != serviceStorage.getData(each).ipCount()).collect(Collectors.toList());}result.put(FieldsConstants.COUNT, services.size());services = doPage(services, pageNo - 1, pageSize);for (Service each : services) {ServiceMetadata serviceMetadata = metadataManager.getServiceMetadata(each).orElseGet(ServiceMetadata::new);ServiceView serviceView = new ServiceView();serviceView.setName(each.getName());serviceView.setGroupName(each.getGroup());serviceView.setClusterCount(serviceStorage.getClusters(each).size());serviceView.setIpCount(serviceStorage.getData(each).ipCount());serviceView.setHealthyInstanceCount(countHealthyInstance(serviceStorage.getData(each)));serviceView.setTriggerFlag(isProtectThreshold(serviceView, serviceMetadata) ? "true" : "false");serviceViews.add(serviceView);}result.set(FieldsConstants.SERVICE_LIST, JacksonUtils.transferToJsonNode(serviceViews));return result;
}private Collection<Service> patternServices(String namespaceId, String group, String serviceName) {boolean noFilter = StringUtils.isBlank(serviceName) && StringUtils.isBlank(group);if (noFilter) {//我们前端默认传的这两个参数都是空,所以会走这里的逻辑return ServiceManager.getInstance().getSingletons(namespaceId);}Collection<Service> result = new LinkedList<>();StringJoiner regex = new StringJoiner(Constants.SERVICE_INFO_SPLITER);regex.add(getRegexString(group));regex.add(getRegexString(serviceName));String regexString = regex.toString();for (Service each : ServiceManager.getInstance().getSingletons(namespaceId)) {if (each.getGroupedServiceName().matches(regexString)) {result.add(each);}}return result;
}

ServiceManager.getInstance()这里一看就是一个经典的单例写法,那我们接下来把精力放到getSingletons这个方法上。
namespaceId默认是public

ServiceManager

public Set<Service> getSingletons(String namespace) {return namespaceSingletonMaps.getOrDefault(namespace, new HashSet<>(1));
}

通过代码我们发现,获取制定namespace下的服务是从一个map中获取的。

/*** Nacos service manager for v2.** @author xiweng.yy*/
public class ServiceManager {private static final ServiceManager INSTANCE = new ServiceManager();private final ConcurrentHashMap<Service, Service> singletonRepository;private final ConcurrentHashMap<String, Set<Service>> namespaceSingletonMaps;//...
}

我们可以发现ServiceManager这个类是一个单例模式的实现,其中维护了两个map,其中一个namespaceSingletonMaps用于存放制定namespace下的服务,那么这个map中的数据是在什么时机存放进去的呢?

/**1. Get singleton service. Put to manager if no singleton.2.  3. @param service new service4. @return if service is exist, return exist service, otherwise return new service*/
public Service getSingleton(Service service) {singletonRepository.computeIfAbsent(service, key -> {NotifyCenter.publishEvent(new MetadataEvent.ServiceMetadataEvent(service, false));return service;});Service result = singletonRepository.get(service);namespaceSingletonMaps.computeIfAbsent(result.getNamespace(), namespace -> new ConcurrentHashSet<>());namespaceSingletonMaps.get(result.getNamespace()).add(result);return result;
}

观察代码我们发现,往map中写数据的只有这一个方法,那么这个方法是在什么时机被调用的呢?
我们重新梳理之前客户端注册的部分逻辑:

  1. InstanceRequestHandler接收所有实例注册、注销相关的请求
  2. InstanceRequestHandler处理注册请求时,会调用EphemeralClientOperationServiceImpl中的registerInstance方法
  3. registerInstance方法中除了我们之前讲的发布客户端服务注册事件ClientOperationEvent.ClientRegisterServiceEvent之外,还会往ServiceManager中的map添加数据

registerInstance方法对ServiceManager的处理逻辑如下:

Service singleton = ServiceManager.getInstance().getSingleton(service);

总结

通过以上梳理,我们知道了前端服务列表中获取的数据是源于ServiceManager类中一个map的缓存,缓存中的数据是在客户端服务注册时添加进去的。

先梳理脉络,然后以点到面,一切都会逐渐清晰。

相关文章:

8、Nacos服务注册服务端源码分析(七)

本文收录于专栏 Nacos 中 。 文章目录 前言确定前端路由CatalogController.listDetail()ServiceManager总结 前言 前文我们分析了Nacos中客户端注册时数据分发的设计链路&#xff0c;本文根据Nacos前端页面请求&#xff0c;看下前端页面中的服务列表的数据源于哪里。 确定前端…...

MySQL使用Xtrabackup在线做主从

1、主库上操作 1.1前提 172.16.11.2&#xff08;主库&#xff09; 172.16.11.4&#xff08;从库&#xff09; 在执行备份之前&#xff0c;确保数据库没有锁定&#xff0c;以避免备份期间的任何写操作。 确保主库上的 MySQL 服务器正在运行&#xff0c;以便备份数据的一致性。…...

scala基础入门

一、Scala安装 下载网址&#xff1a;Install | The Scala Programming Language ideal安装 &#xff08;1&#xff09;下载安装Scala plugins &#xff08;2&#xff09;统一JDK环境&#xff0c;统一为8 &#xff08;3&#xff09;加载Scala &#xff08;4&#xff09;创建工…...

【Java-LangChain:面向开发者的提示工程-5】推断

第五章 推断 推断任务可以看作是模型接收文本作为输入&#xff0c;并执行某种分析的过程。其中涉及提取标签、提取实体、理解文本情感等等。如果你想要从一段文本中提取正面或负面情感&#xff0c;在传统的机器学习工作流程中&#xff0c;需要收集标签数据集、训练模型、确定如…...

【C++】手撕vector(vector的模拟实现)

手撕vector目录&#xff1a; 一、基本实现思路方针 二、vector的构造函数剖析&#xff08;构造歧义拷贝构造&#xff09; 2.1构造函数使用的歧义问题 2.2 vector的拷贝构造和赋值重载&#xff08;赋值重载不是构造哦&#xff0c;为了方便写在一起&#xff09; 三、vector的…...

智能指针那些事

​《Effective Modern C》学习笔记之条款二十一&#xff1a;优先选用std::make_unique和std::make_shared,而非直接new - 知乎...

Fiddler抓取手机https包的步骤

做接口测试时&#xff0c;有时我们需要使用fiddler进行抓包分析&#xff0c;那么如何抓取https包。主要分为以下七步&#xff1a; 1.设置fiddler选项&#xff1a;Tools->Options,按如下图勾选 2.下载并安装Fiddler证书生成器 下载地址&#xff1a;http://www.telerik.com/…...

idea没有maven工具栏解决方法

背景&#xff1a;接手的一些旧项目&#xff0c;有pom文件&#xff0c;但是用idea打开的时候&#xff0c;没有认为是maven文件&#xff0c;所以没有maven工具栏&#xff0c;不能进行重新加载pom文件中的依赖。 解决方法&#xff1a;选中pom.xml文件&#xff0c;右键 选择添加为…...

levelDB引擎

一、背景 1.1、影响磁盘性能的因素&#xff1a; 主要受限于磁盘的寻道时间&#xff0c;优化磁盘数据访问的方法是尽量减少磁盘的IO次数。磁盘数据访问效率取决于磁盘IO次数&#xff0c;而磁盘IO次数又取决于数据在磁盘上的组织方式。磁盘数据存储大多采用B树类型数据结构&…...

IM同步服务

设计概述 后台同步方案的设计就是数据存储结构的设计&#xff0c;如何快速体现“信息变化”&#xff0c;如何快速计算出“变化信息”。后台数据存储结构是由同步协议中同步契约决定的。 设计方案 该方案的同步是按照业务粒度来划分&#xff0c;只需要同步sdk要求同步的数据。…...

MySQL 运维常用脚本

常用功能脚本 1.导出整个数据库 mysqldump -u 用户名 -p –default-character-setlatin1 数据库名 > 导出的文件名(数据库默认编码是latin1) mysqldump -u wcnc -p smgp_apps_wcnc > wcnc.sql 2.导出一个表 mysqldump -u 用户名 -p 数据库名 表名> 导出的文件…...

ABC322刷题记

ABC322刷题记 T1.A A - First ABC 2。 妥妥的简单题…… 用find函数做就行。&#xff08;如果不存在那个子串就返回-1&#xff0c;否则返回第一次出现位置&#xff09; 注意题目中编号是从1开始的。 时间复杂度&#xff1a;O(log(n))。find函数有一定代价&#xff0c;我记…...

visual studio的安装及scanf报错的解决

visual studio是一款很不错的c语言编译器 下载地址&#xff1a;官网 点击后跳转到以下界面 下滑后点击下载Vasual Sutdio&#xff0c;选择社区版即可 选择位置存放下载文件后&#xff0c;即可开始安装 安装时会稍微等一小会儿。然后会弹出这个窗口&#xff0c;我们选择安装位…...

React生命周期

React的生命周期主要是指React组件从创建到销毁的过程&#xff0c;包括三个阶段&#xff1a;挂载期&#xff08;实例化期&#xff09;、更新期&#xff08;存在期&#xff09;、卸载期&#xff08;销毁期&#xff09; 挂载期&#xff1a; constructor&#xff08;props&#…...

SpringBoot整合RocketMQ笔记

SpringBoot版本为2.3.12.Release RocketMQ对比kafka 学习链接 https://zhuanlan.zhihu.com/p/335216381 代码实战 https://www.cnblogs.com/RedOrange/p/17401238.html Centos安装rocketmq https://blog.csdn.net/chuige2013/article/details/123783612 RocketMQ详细配置与…...

【【萌新的RiscV学习之在写代码之前对于关键路径的分析-11】】

萌新的RiscV学习之在写代码之前对于关键路径的分析-11 首先我们最简单的control 模块 全分段 因为只有分段 &#xff0c; 分开使用之后 &#xff0c; 各个阶段的具体功能才会合理使用 就像是为了后续 “气泡” 赋值 为 0 还有单独比较前递这种 EX &#xff1a; ALUOP ALUSrc …...

A. Sequence with Digits

题目&#xff1a;样例&#xff1a; 输入 8 1 4 487 1 487 2 487 3 487 4 487 5 487 6 487 7输出 42 487 519 528 544 564 588 628 思路&#xff1a; 暴力模拟题&#xff0c;看这数据范围&#xff0c;有些人可能会被唬住&#xff0c;以为是高精度或者容易超时&#xff0c;实际上…...

gitlab配置webhook限制提交注释

一、打开gitlab相关配置项 vim /etc/gitlab/gitlab.rb gitlab_shell[custom_hooks_dir] "/etc/gitlab/custom_hooks" 二、创建相关文件夹 mkdir -p /etc/gitlab/custom_hooks mkdir -p /etc/gitlab/custom_hooks/post-receive.d mkdir -p /etc/gitlab/custom_h…...

蓝桥杯Python scratch C++选拔赛stema个人如何报名?

如果不会操作&#xff0c;可以微信makytony协助。...

Cesium实现动态旋转四棱锥(2023.9.11)

Cesium实现动态悬浮旋转四棱锥效果 2023.9.11 1、引言2、两种实现思路介绍2.1 思路一&#xff1a;添加已有的四棱锥&#xff08;金字塔&#xff09;模型实现&#xff08;简单但受限&#xff09;2.2 思路二&#xff1a;自定义四棱锥几何模型实现&#xff08;复杂且灵活&#xff…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下&#xff1a; struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank&#xff1f;由于时间太久&#xff0c;我真忘记了。搜搜发现&#xff0c;还真有人和我一样。见下面的链接&#xff1a;https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

微服务商城-商品微服务

数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建

华为云FlexusDeepSeek征文&#xff5c;DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色&#xff0c;华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型&#xff0c;能助力我们轻松驾驭 DeepSeek-V3/R1&#xff0c;本文中将分享如何…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

C#中的CLR属性、依赖属性与附加属性

CLR属性的主要特征 封装性&#xff1a; 隐藏字段的实现细节 提供对字段的受控访问 访问控制&#xff1a; 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性&#xff1a; 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑&#xff1a; 可以…...