当前位置: 首页 > 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…...

uniapp 对接腾讯云IM群组成员管理(增删改查)

UniApp 实战&#xff1a;腾讯云IM群组成员管理&#xff08;增删改查&#xff09; 一、前言 在社交类App开发中&#xff0c;群组成员管理是核心功能之一。本文将基于UniApp框架&#xff0c;结合腾讯云IM SDK&#xff0c;详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

在四层代理中还原真实客户端ngx_stream_realip_module

一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡&#xff08;如 HAProxy、AWS NLB、阿里 SLB&#xff09;发起上游连接时&#xff0c;将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后&#xff0c;ngx_stream_realip_module 从中提取原始信息…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

视觉slam十四讲实践部分记录——ch2、ch3

ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

PAN/FPN

import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...