NamingService
注册服务的相关接口
void registerInstance(String serviceName, String ip, int port)
void registerInstance(String serviceName, String groupName, String ip, int port)
void registerInstance(String serviceName, String ip, int port, String clusterName)
void registerInstance(String serviceName, String groupName, String ip, int port, String clusterName)
void registerInstance(String serviceName, Instance instance)
void registerInstance(String serviceName, String groupName, Instance instance)
com.alibaba.nacos.client.naming.NacosNamingService
-》void registerInstance(String serviceName, String groupName, String ip, int port)
初始化方法 init(Properties properties)
/**
* 初始化方法,用于配置和启动与Nacos服务交互的客户端组件。
*
* @param properties 包含初始化所需配置的属性对象。
* @throws NacosException 如果初始化过程中发生错误,则抛出此异常。
*/
private void init(Properties properties) throws NacosException {
// 异步预加载成本组件,可能是为了优化性能或准备后续操作所需的数据。
PreInitUtils.asyncPreLoadCostComponent();
// 验证初始化参数是否有效。
ValidatorUtils.checkInitParam(properties);
// 从配置属性中初始化命名空间,命名空间用于隔离不同的环境或租户。
this.namespace = InitUtils.initNamespaceForNaming(properties);
// 初始化序列化机制,确保客户端和服务器之间传输的对象可以正确序列化和反序列化。
InitUtils.initSerialization();
// 初始化服务器地址,这通常是从配置属性中读取的Nacos服务器的地址。
// 注意:此方法的具体实现在代码段中未给出,但它是必要的。
initServerAddr(properties);
// 初始化Web服务的根上下文,可能与Web服务相关的配置或路径设置有关。
InitUtils.initWebRootContext(properties);
// 初始化缓存目录的路径,用于存储本地缓存的数据,如服务列表等。
initCacheDir(properties);
// 初始化日志文件的名称,以便于日志管理和查询。
initLogName(properties);
// 创建命名代理实例,用于与Nacos服务进行交互,如注册服务、发现服务等。
this.serverProxy = new NamingProxy(this.namespace, this.endpoint, this.serverList, properties);
// 创建心跳反应器实例,负责定时向Nacos服务器发送心跳,以维持服务的注册状态。
this.beatReactor = new BeatReactor(this.serverProxy, initClientBeatThreadCount(properties));
// 创建主机反应器实例,负责处理服务实例的注册、注销、心跳等事件,并维护本地缓存的服务列表。
// 它还考虑了是否在启动时加载缓存、是否启用空保护推送以及轮询线程的数量等配置。
this.hostReactor = new HostReactor(this.serverProxy, beatReactor, this.cacheDir, isLoadCacheAtStart(properties),
isPushEmptyProtect(properties), initPollingThreadCount(properties));
}
命名空间 和 分组名称的区别
命名空间 namespace 的作用
资源隔离:Nacos Namespace(命名空间)是Nacos提供的一种资源隔离的机制。它可以将一个Nacos服务实例划分到不同的逻辑分组中,每个分组都有独立的配置和服务注册中心。这样,不同的应用或者不同的环境(如开发、测试、生产)可以归类到不同的命名空间中,实现资源的隔离。
分组名称 groupName 的作用
服务隔离:GroupName(组名)是Nacos中次于命名空间的隔离概念。在同一个命名空间下,不同的Group之间的服务是相互隔离的。这意味着,即使两个服务有相同的名称,只要它们属于不同的Group,它们就不会相互干扰。
NamingProxy
创建命名代理实例,用于与Nacos服务进行交互,如注册服务、发现服务等
private NamingProxy serverProxy;
// 创建命名代理实例,用于与Nacos服务进行交互,如注册服务、发现服务等。
this.serverProxy = new NamingProxy(this.namespace, this.endpoint
, this.serverList, properties);
BeatReactor
创建心跳反应器实例,负责定时向Nacos服务器发送心跳
private BeatReactor beatReactor;
// 创建心跳反应器实例,负责定时向Nacos服务器发送心跳,以维持服务的注册状态。
this.beatReactor = new BeatReactor(this.serverProxy, initClientBeatThreadCount(properties));
HostReactor
创建主机反应器实例,负责处理服务实例的注册、注销、心跳等事件,并维护本地缓存的服务列
private HostReactor hostReactor;
// 创建主机反应器实例,负责处理服务实例的注册、注销、心跳等事件,并维护本地缓存的服务列表。
// 它还考虑了是否在启动时加载缓存、是否启用空保护推送以及轮询线程的数量等配置。
this.hostReactor = new HostReactor(this.serverProxy, beatReactor, this.cacheDir, isLoadCacheAtStart(properties),
isPushEmptyProtect(properties), initPollingThreadCount(properties));
NamingProxy和HostReactor区别
NamingProxy:功能定位不同,NamingProxy主要负责与Nacos服务端的通信。它是客户端与Nacos服务端之间的桥梁,用于执行各种与服务注册、发现、注销以及心跳等相关的操作。
HostReactor:主要负责获取、保存和更新服务实例的信息。它是客户端中管理服务实例信息的关键组件。
服务注册
com.alibaba.nacos.client.naming.NacosNamingService
-》void registerInstance(String serviceName, String groupName, String ip, int port)
/**
* 向Nacos服务注册中心注册一个服务实例。
*
* @param serviceName 服务名称,用于在Nacos中唯一标识一个服务。
* @param groupName 分组名称,用于对服务进行分组,默认为DEFAULT_GROUP。
* @param ip 服务实例的IP地址。
* @param port 服务实例的端口号。
* @param clusterName 集群名称,用于标识服务实例所属的集群。
* @throws NacosException 如果注册过程中发生任何错误,将抛出此异常。
*/
@Override
public void registerInstance(String serviceName, String groupName, String ip, int port, String clusterName)
throws NacosException {
// 创建一个服务实例对象
Instance instance = new Instance();
// 设置服务实例的IP地址
instance.setIp(ip);
// 设置服务实例的端口号
instance.setPort(port);
// 设置服务实例的权重,默认为1.0,表示服务实例的负载权重
instance.setWeight(1.0);
// 设置服务实例所属的集群名称
instance.setClusterName(clusterName);
// 调用重载的registerInstance方法,将填充好的服务实例注册到Nacos服务注册中心
// 注意:这里假设存在另一个registerInstance方法,该方法接受serviceName、groupName和Instance作为参数
registerInstance(serviceName, groupName, instance);
}
将服务实例注册到注册中心
NamingProxy:创建命名代理实例,用于与Nacos服务进行交互,如注册服务、发现服务等‘
private NamingProxy serverProxy;
/**
* 向Nacos服务注册中心注册一个服务实例。
*
* @param serviceName 服务名称,用于在Nacos中标识服务,但不包括分组名。
* @param groupName 分组名称,用于对服务进行分组,默认为DEFAULT_GROUP。
* @param instance 要注册的服务实例对象,包含实例的详细信息。
* @throws NacosException 如果注册过程中发生任何错误,将抛出此异常。
*/
@Override
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
// 验证服务实例的合法性,比如IP、端口等是否合法
NamingUtils.checkInstanceIsLegal(instance);
// 根据服务名和分组名构建完整的服务名称(包括分组名)
String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName);
// 如果服务实例是临时的(需要发送心跳来维持注册状态)
if (instance.isEphemeral()) {
// 构建心跳信息,包含服务名称和服务实例信息
BeatInfo beatInfo = beatReactor.buildBeatInfo(groupedServiceName, instance);
// 将心跳信息添加到心跳反应器中,以便后续定时发送心跳
beatReactor.addBeatInfo(groupedServiceName, beatInfo);
}
// 调用服务代理(ServerProxy)的registerService方法,将服务实例注册到Nacos服务注册中心
// 这个方法可能会与Nacos服务端进行网络通信,以完成注册操作
serverProxy.registerService(groupedServiceName, groupName, instance);
}
心跳检测
BeatReactor:创建心跳反应器实例,负责定时向Nacos服务器发送心跳。
使用线程池创建定时任务:executorService.schedule
/**
* 向心跳信息映射中添加一个服务的心跳信息,并安排一个定时任务来周期性地更新它。
*
* @param serviceName 服务名称,用于标识服务。
* @param beatInfo 包含服务实例心跳信息的对象,如IP、端口、心跳周期等。
*/
public void addBeatInfo(String serviceName, BeatInfo beatInfo) {
// 记录日志,输出正在添加的心跳信息
NAMING_LOGGER.info("[BEAT] adding beat: {} to beat map.", beatInfo);
// 构建心跳信息的唯一键,通常基于服务名、IP和端口
String key = buildKey(serviceName, beatInfo.getIp(), beatInfo.getPort());
// 尝试将心跳信息添加到映射中,如果键已存在,则返回旧的心跳信息
BeatInfo existBeat = null;
// 修正#1733问题,确保如果相同的服务实例已存在,则将其标记为已停止
if ((existBeat = dom2Beat.put(key, beatInfo)) != null) {
existBeat.setStopped(true); // 如果旧的心跳信息存在,则将其标记为已停止
}
// 使用ExecutorService安排一个定时任务,该任务将周期性地执行BeatTask,以更新心跳信息
// BeatTask的构造函数接收心跳信息作为参数
// 定时任务的执行周期由beatInfo.getPeriod()决定,单位为毫秒
executorService.schedule(new BeatTask(beatInfo), beatInfo.getPeriod(), TimeUnit.MILLISECONDS);
// 更新监控指标,记录当前dom2Beat映射的大小
// MetricsMonitor是一个用于监控和记录系统状态的组件
// getDom2BeatSizeMonitor()方法返回一个监控项,用于跟踪dom2Beat映射的大小
// set方法用于设置监控项的值
MetricsMonitor.getDom2BeatSizeMonitor().set(dom2Beat.size());
}
心跳检测任务
心跳检测不仅向注册中心发送心跳,如果服务未找到,则尝试注册服务实例
/**
* BeatTask 类实现了 Runnable 接口,用于执行心跳任务。
* 它封装了心跳信息的发送逻辑,并根据服务器响应调整心跳间隔和轻量级心跳的启用状态。
*/
class BeatTask implements Runnable {
private BeatInfo beatInfo; // 心跳信息对象,包含服务实例的详细信息
/**
* 构造函数,接收一个 BeatInfo 对象作为参数。
*
* @param beatInfo 包含服务实例心跳信息的对象
*/
public BeatTask(BeatInfo beatInfo) {
this.beatInfo = beatInfo;
}
@Override
public void run() {
// 如果心跳信息已被标记为停止,则直接返回
if (beatInfo.isStopped()) {
return;
}
long nextTime = beatInfo.getPeriod(); // 初始化下一次心跳的时间间隔为当前心跳周期
try {
// 发送心跳请求到服务器
JsonNode result = serverProxy.sendBeat(beatInfo, BeatReactor.this.lightBeatEnabled);
// 从响应中提取新的心跳间隔
long interval = result.get("clientBeatInterval").asLong();
// 提取并更新轻量级心跳的启用状态
boolean lightBeatEnabled = false;
if (result.has(CommonParams.LIGHT_BEAT_ENABLED)) {
lightBeatEnabled = result.get(CommonParams.LIGHT_BEAT_ENABLED).asBoolean();
}
BeatReactor.this.lightBeatEnabled = lightBeatEnabled;
// 如果服务器返回了新的心跳间隔,则更新下一次心跳的时间间隔
if (interval > 0) {
nextTime = interval;
}
// 处理响应中的状态码
int code = NamingResponseCode.OK;
if (result.has(CommonParams.CODE)) {
code = result.get(CommonParams.CODE).asInt();
}
// 如果服务未找到,则尝试注册服务实例
if (code == NamingResponseCode.RESOURCE_NOT_FOUND) {
Instance instance = new Instance();
// 设置实例的详细信息,这里假设实例的instanceId由其他逻辑生成
instance.setPort(beatInfo.getPort());
instance.setIp(beatInfo.getIp());
instance.setWeight(beatInfo.getWeight());
instance.setMetadata(beatInfo.getMetadata());
instance.setClusterName(beatInfo.getCluster());
instance.setServiceName(beatInfo.getServiceName());
instance.setInstanceId(instance.getInstanceId()); // 注意:这里假设instanceId已正确设置
instance.setEphemeral(true);
// 尝试注册服务实例
try {
serverProxy.registerService(beatInfo.getServiceName(),
NamingUtils.getGroupName(beatInfo.getServiceName()), instance);
} catch (Exception ignore) {
// 忽略注册失败的异常,可能是服务已存在或其他原因
}
}
} catch (NacosException ex) {
// 处理 Nacos 相关的异常
NAMING_LOGGER.warn("[CLIENT-BEAT] failed to send beat: {}, code: {}, msg: {}",
JacksonUtils.toJson(beatInfo), ex.getErrCode(), ex.getErrMsg());
} catch (Exception unknownEx) {
// 处理未知异常
NAMING_LOGGER.error("[CLIENT-BEAT] failed to send beat: {}, unknown exception msg: {}",
JacksonUtils.toJson(beatInfo), unknownEx.getMessage(), unknownEx);
} finally {
// 无论是否成功,都重新安排下一次心跳任务
executorService.schedule(new BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS);
}
}
}
访问Open-Api 注册服务实例
服务地址注册服务实例: /v1/ns/ instance
/**
* 向Nacos服务注册中心注册一个服务实例。
*
* @param serviceName 服务名称,用于在Nacos中唯一标识服务。
* @param groupName 分组名称,用于对服务进行分组,默认为DEFAULT_GROUP。
* @param instance 要注册的服务实例对象,包含实例的详细信息,如IP、端口、权重等。
* @throws NacosException 如果注册过程中发生任何错误,将抛出此异常。
*/
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
// 记录日志,输出注册服务的相关信息
NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance: {}", namespaceId, serviceName, instance);
// 准备HTTP请求的参数
final Map<String, String> params = new HashMap<String, String>(16);
// 设置命名空间ID,如果namespaceId为null或空字符串,则可能使用默认命名空间
params.put(CommonParams.NAMESPACE_ID, namespaceId);
// 设置服务名称
params.put(CommonParams.SERVICE_NAME, serviceName);
// 设置分组名称
params.put(CommonParams.GROUP_NAME, groupName);
// 设置集群名称,从服务实例中获取
params.put(CommonParams.CLUSTER_NAME, instance.getClusterName());
// 设置服务实例的IP地址
params.put("ip", instance.getIp());
// 设置服务实例的端口号
params.put("port", String.valueOf(instance.getPort()));
// 设置服务实例的权重
params.put("weight", String.valueOf(instance.getWeight()));
// 设置服务实例是否启用
params.put("enable", String.valueOf(instance.isEnabled()));
// 设置服务实例的健康状态(注意:这里通常不由客户端直接设置,而是由Nacos服务端或健康检查机制来维护)
params.put("healthy", String.valueOf(instance.isHealthy()));
// 设置服务实例是否为临时实例(需要发送心跳)
params.put("ephemeral", String.valueOf(instance.isEphemeral()));
// 设置服务实例的元数据,将其转换为JSON字符串
params.put("metadata", JacksonUtils.toJson(instance.getMetadata()));
// 调用reqApi方法,向Nacos服务端发送HTTP POST请求,完成服务实例的注册
// UtilAndComs.nacosUrlInstance是Nacos服务注册相关的URL路径
// params是请求参数
// HttpMethod.POST指定了请求方法为POST
reqApi(UtilAndComs.nacosUrlInstance, params, HttpMethod.POST);
}
服务发现
发现服务相关接口
List getAllInstances(String serviceName)
List getAllInstances(String serviceName, String groupName)
List getAllInstances(String serviceName, boolean subscribe)
List getAllInstances(String serviceName, String groupName, boolean subscribe)
List getAllInstances(String serviceName, List clusters)
List getAllInstances(String serviceName, String groupName, List clusters)
List getAllInstances(String serviceName, List clusters, boolean subscribe)
List getAllInstances(String serviceName, String groupName, List clusters, boolean subscribe)
List selectInstances(String serviceName, boolean healthy) ;
com.alibaba.nacos.client.naming.NacosNamingService
-》List getAllInstances(String serviceName)
-》List getAllInstances(String serviceName, String groupName)
-》List getAllInstances(String serviceName, boolean subscribe)
获取服务实例
判断是否订阅来获取服务
如果是订阅模式获取服务,则通过订阅机制获取服务实例
如果不是订阅模式获取服务,直接从服务器获取服务实例
HostReactor:创建主机反应器实例,负责处理服务实例的注册、注销、心跳等事件
@Override
public List<Instance> getAllInstances(String serviceName, String groupName, List<String> clusters,
boolean subscribe) throws NacosException {
// 声明一个用于存储服务信息的变量
ServiceInfo serviceInfo;
// 根据是否订阅服务来决定如何获取服务信息
// 如果需要订阅服务变更,则通过订阅机制获取服务信息
if (subscribe) {
serviceInfo = hostReactor.getServiceInfo(
NamingUtils.getGroupedName(serviceName, groupName), // 将服务名和分组名组合成完整的服务标识
StringUtils.join(clusters, ",") // 将集群列表拼接成字符串,用于指定需要获取哪些集群的实例
);
} else {
// 如果不需要订阅服务变更,则直接从服务器获取服务信息
serviceInfo = hostReactor.getServiceInfoDirectlyFromServer(
NamingUtils.getGroupedName(serviceName, groupName), // 同上,组合服务标识
StringUtils.join(clusters, ",") // 同上,拼接集群列表
);
}
// 声明一个列表用于存储服务实例
List<Instance> list;
// 检查服务信息是否存在以及服务实例列表是否为空
if (serviceInfo == null || CollectionUtils.isEmpty(list = serviceInfo.getHosts())) {
// 如果服务信息不存在或实例列表为空,则返回一个空的实例列表
return new ArrayList<Instance>();
}
// 返回服务实例列表
return list;
}
订阅模式获取服务
public ServiceInfo getServiceInfo(final String serviceName, final String clusters) {
// 打印当前是否处于故障转移模式
NAMING_LOGGER.debug("failover-mode: " + failoverReactor.isFailoverSwitch());
// 根据服务名和集群生成服务信息的唯一键
String key = ServiceInfo.getKey(serviceName, clusters);
// 如果当前处于故障转移模式,则尝试从故障转移反应器中获取服务信息
if (failoverReactor.isFailoverSwitch()) {
return failoverReactor.getService(key);
}
// 尝试从本地缓存中获取服务信息
ServiceInfo serviceObj = getServiceInfo0(serviceName, clusters);
// 如果本地缓存中没有找到服务信息
if (null == serviceObj) {
// 创建一个新的ServiceInfo对象,并初始化其服务名和集群
serviceObj = new ServiceInfo(serviceName, clusters);
// 将新创建的服务信息对象放入本地缓存中
serviceInfoMap.put(serviceObj.getKey(), serviceObj);
// 将服务名加入更新队列,表示需要更新该服务的信息
updatingMap.put(serviceName, new Object());
// 立即更新服务信息
updateServiceNow(serviceName, clusters);
// 更新完成后,从更新队列中移除服务名
updatingMap.remove(serviceName);
}
// 如果服务名在更新队列中,但服务信息已存在(可能是并发更新)
else if (updatingMap.containsKey(serviceName)) {
// 如果设置了更新等待间隔,则等待一段时间以避免重复更新
if (UPDATE_HOLD_INTERVAL > 0) {
synchronized (serviceObj) {
try {
// 等待一段时间,让其他线程完成更新
serviceObj.wait(UPDATE_HOLD_INTERVAL);
} catch (InterruptedException e) {
// 如果等待被中断,记录错误日志
NAMING_LOGGER.error("[getServiceInfo] serviceName:" + serviceName + ", clusters:" + clusters, e);
}
}
}
}
// 无论是否进行了更新,都尝试安排一个异步任务来检查并更新服务信息(如果尚未安排)
scheduleUpdateIfAbsent(serviceName, clusters);
// 从本地缓存中返回服务信息
return serviceInfoMap.get(serviceObj.getKey());
}
访问Open-Api 拉取服务实例
public void updateService(String serviceName, String clusters) throws NacosException {
// 尝试从本地缓存中获取旧的服务信息对象
ServiceInfo oldService = getServiceInfo0(serviceName, clusters);
try {
// 向服务注册中心(如Nacos Server)发起查询请求,获取最新的服务列表信息
// 参数包括服务名、集群名、UDP端口(用于服务健康检查等),以及是否订阅服务变更(此处为false,表示仅查询不订阅)
String result = serverProxy.queryList(serviceName, clusters, pushReceiver.getUdpPort(), false);
// 如果查询结果不为空,则处理返回的服务列表JSON字符串
// 此处processServiceJson方法可能会更新本地缓存中的服务信息
if (StringUtils.isNotEmpty(result)) {
processServiceJson(result);
}
} finally {
// 无论查询操作是否成功或抛出异常,都执行以下代码块
// 如果旧的服务信息对象存在,则对其加锁并通知所有等待该对象的线程
// 这通常用于释放因等待服务信息更新而阻塞的线程
if (oldService != null) {
synchronized (oldService) {
oldService.notifyAll();
}
}
}
}
获取完成添加定时任务
无论是否进行了更新,都尝试安排一个异步任务来检查并更新服务信息(如果尚未安排)
/**
* 计划更新服务信息(如果尚未安排)
*
* 此方法用于确保对于给定的服务名称和集群,只安排一次更新任务。如果更新任务已经存在于计划中,则不会重复安排。
*
* @param serviceName 服务名称,表示要更新的服务标识
* @param clusters 集群名称,支持多个集群名称以逗号分隔。指定了服务所属的集群
*
* 方法逻辑说明:
* 1. 首先,通过服务名称和集群名称生成一个唯一的键(使用ServiceInfo.getKey方法),并检查这个键是否已经存在于futureMap中。
* 如果存在,说明已经安排了更新任务,因此直接返回,不执行任何操作。
* 2. 如果键不存在于futureMap中,则进入同步块,以确保在多线程环境下对futureMap的修改是线程安全的。
* 3. 在同步块内,再次检查该键是否已存在于futureMap中(这是双重检查锁定模式的一部分,用于减少不必要的同步开销)。
* 如果仍然不存在,则继续执行。
* 4. 创建一个新的UpdateTask(更新任务),该任务负责更新指定服务名称和集群下的服务信息。
* 5. 使用某种调度机制(如ScheduledExecutorService)将UpdateTask添加到任务队列中,并获取返回的ScheduledFuture对象。
* 6. 将生成的键和对应的ScheduledFuture对象存入futureMap中,以便后续可以跟踪或取消该更新任务(如果需要)。
*/
public void scheduleUpdateIfAbsent(String serviceName, String clusters) {
// 第一步:检查是否已经安排了更新任务
if (futureMap.get(ServiceInfo.getKey(serviceName, clusters)) != null) {
return;
}
// 第二步:进入同步块,确保线程安全
synchronized (futureMap) {
// 双重检查锁定模式:再次检查是否已经安排了更新任务
if (futureMap.get(ServiceInfo.getKey(serviceName, clusters)) != null) {
return;
}
// 第三步:创建并添加更新任务
ScheduledFuture<?> future = addTask(new UpdateTask(serviceName, clusters));
// 第四步:将任务与键关联并存入futureMap
futureMap.put(ServiceInfo.getKey(serviceName, clusters), future);
}
}
// 注意:addTask方法和futureMap的定义没有在此代码段中给出,但我们可以假设
// addTask方法负责将UpdateTask添加到某个调度器(如ScheduledExecutorService)中,
// 并返回对应的ScheduledFuture对象,以便后续可以跟踪或取消任务。
// futureMap是一个Map,用于存储服务信息键与ScheduledFuture对象的映射关系。
服务地址发现服务: /v1/ns/ instance/list
/**
* 查询服务列表
*
* @param serviceName 服务名称
* @param clusters 集群名称,多个集群以逗号分隔
* @param udpPort UDP端口,用于服务健康检查等
* @param healthyOnly 是否只查询健康的服务实例
* @return 返回包含服务列表信息的JSON字符串,如果查询失败或没有服务实例则返回空字符串或抛出异常
* @throws NacosException 如果在查询过程中发生错误,如网络问题、服务注册中心不可达等,将抛出此异常
*/
public String queryList(String serviceName, String clusters, int udpPort, boolean healthyOnly)
throws NacosException {
// 初始化请求参数
final Map<String, String> params = new HashMap<String, String>(8);
// 设置命名空间ID,用于隔离不同的服务环境
params.put(CommonParams.NAMESPACE_ID, namespaceId);
// 设置服务名称
params.put(CommonParams.SERVICE_NAME, serviceName);
// 设置集群名称
params.put("clusters", clusters);
// 设置UDP端口,用于服务健康检查等
params.put("udpPort", String.valueOf(udpPort));
// 设置客户端IP,用于日志记录或安全校验
params.put("clientIP", NetUtils.localIP());
// 设置是否只查询健康的服务实例
params.put("healthyOnly", String.valueOf(healthyOnly));
// 发起GET请求,查询服务列表
// UtilAndComs.nacosUrlBase 是Nacos服务的基础URL
// "/instance/list" 是查询服务列表的API路径
// params 是请求参数
// HttpMethod.GET 表示使用GET方法发起请求
return reqApi(UtilAndComs.nacosUrlBase + "/instance/list", params, HttpMethod.GET);
}
直接从服务器获取服务实例
/**
* 直接从服务注册中心获取服务信息
*
* @param serviceName 服务名称
* @param clusters 集群名称,支持多个集群名称以逗号分隔
* @return 返回包含服务信息的ServiceInfo对象,如果查询结果为空或发生异常,则返回null
* @throws NacosException 如果在查询过程中遇到Nacos服务注册中心的错误(如网络问题、服务不可用等),将抛出此异常
*/
public ServiceInfo getServiceInfoDirectlyFromServer(final String serviceName, final String clusters)
throws NacosException {
// 调用serverProxy的queryList方法查询服务列表,UDP端口设置为0(可能表示不使用UDP端口进行健康检查),且不订阅服务变更
String result = serverProxy.queryList(serviceName, clusters, 0, false);
// 如果查询结果不为空,则使用JacksonUtils工具类将JSON字符串转换为ServiceInfo对象
if (StringUtils.isNotEmpty(result)) {
return JacksonUtils.toObj(result, ServiceInfo.class);
}
// 如果查询结果为空,则返回null
return null;
}
本地缓存
Map<String, ServiceInfo> serviceInfoMap
保存服务实例,也就是本地缓存。