13 nacos 客户端这边 LongPollingRunnable 检测到目标配置文件一直在更新

前言

最近在改造一个老项目的时候出现了这样的一个问题, 在项目启动了之后, 日志不断地输出如下的日志, 然后就很奇怪, 我的  app.properties 是一直都没有调整, 也没有改动的

然后 为什么客户端, 服务器这边的交互, 会造成 服务器这边一直都是 app.properties 呢 ? 

2023-09-06 16:28:12.143  INFO 36736 --- [.26_8858-public] c.a.n.client.config.impl.ClientWorker    : [fixed-10.30.2.26_8858-public] [polling-resp] config changed. dataId=app.properties, group=DEFAULT_GROUP, tenant=public
2023-09-06 16:28:12.143  INFO 36736 --- [.26_8858-public] c.a.n.client.config.impl.ClientWorker    : get changedGroupKeys:[app.properties+DEFAULT_GROUP+public]
2023-09-06 16:28:12.268  INFO 36736 --- [.26_8858-public] c.a.n.client.config.impl.ClientWorker    : [fixed-10.30.2.26_8858-public] [data-received] dataId=app.properties, group=DEFAULT_GROUP, tenant=public, md5=3be94293cbd3d785fe3c18913b53c785, content=
# mongo.properties 
common.mongo.tableNames=wb0097CompanyBlacklist,wb0099RecordCollectiveDiscussion..., type=properties
2023-09-06 16:28:12.447  INFO 36736 --- [.26_8858-public] c.a.n.client.config.impl.ClientWorker    : [fixed-10.30.2.26_8858-public] [polling-resp] config changed. dataId=app.properties, group=DEFAULT_GROUP, tenant=public
2023-09-06 16:28:12.447  INFO 36736 --- [.26_8858-public] c.a.n.client.config.impl.ClientWorker    : get changedGroupKeys:[app.properties+DEFAULT_GROUP+public]
2023-09-06 16:28:12.556  INFO 36736 --- [.26_8858-public] c.a.n.client.config.impl.ClientWorker    : [fixed-10.30.2.26_8858-public] [data-received] dataId=app.properties, group=DEFAULT_GROUP, tenant=public, md5=3be94293cbd3d785fe3c18913b53c785, content=
# mongo.properties 
common.mongo.tableNames=wb0097CompanyBlacklist,wb0099RecordCollectiveDiscussion..., type=properties
2023-09-06 16:28:12.685  INFO 36736 --- [.26_8858-public] c.a.n.client.config.impl.ClientWorker    : [fixed-10.30.2.26_8858-public] [polling-resp] config changed. dataId=app.properties, group=DEFAULT_GROUP, tenant=public
2023-09-06 16:28:12.685  INFO 36736 --- [.26_8858-public] c.a.n.client.config.impl.ClientWorker    : get changedGroupKeys:[app.properties+DEFAULT_GROUP+public]
2023-09-06 16:28:12.805  INFO 36736 --- [.26_8858-public] c.a.n.client.config.impl.ClientWorker    : [fixed-10.30.2.26_8858-public] [data-received] dataId=app.properties, group=DEFAULT_GROUP, tenant=public, md5=3be94293cbd3d785fe3c18913b53c785, content=
# mongo.properties 
common.mongo.tableNames=wb0097CompanyBlacklist,wb0099RecordCollectiveDiscussion..., type=properties

首先是先总结一下 这个问题的情况

在我们通常情况下, 我们项目会使用 public 作为 nacos 交互的名称空间, 然后 public 这个名称空间, nacos 服务器这边会将 tenant 为 空 的配置, 也响应给客户端 

然后 就造成了我们这里的问题

客户端这边拿着 “app.properties+DEFAULT_GROUP+public” 去 nacos 服务器这边来轮询目标配置是否有改动, 然后 nacos 服务器这边, 根据 “app.properties+DEFAULT_GROUP+public” 去查询配置, 结果没有找到目标配置, 然后识别为了 目标配置发生了变化

然后通知到客户端这边 “app.properties+DEFAULT_GROUP+public” 发生了变化, 然后通知客户端这边获取 最新的配置, 等等流程 

然后这个 ClientWorker 这里的 “LongPollingRunnable” 会一直递归去处理这个, 拿着客户端这边关心的 配置列表, 向 nacos 服务器这边 “/config/listener” 进行轮询 是否有改动, 如果改动了, 则客户端这边 重新获取最新的配置

本文 主要是在已知问题的情况下去调试这一系列流程, 相比于 刚碰到这个问题的时候, 会简单的多 

客户端和服务器的交互

主要是 “/config/listener” 的这个轮询请求 

携带的参数为 “Listening-Configs” 中包含了请求的一个配置列表, 每一个配置包含了 dataId, groupId, tenantId

比如这里拆解为三个配置如下, 然后发送给 naocs 服务器这边 

xmay+DEFAULT_GROUP+public -> NULL

xmay.properties+DEFAULT_GROUP+public -> 3be94293cbd3d785fe3c18913b53c785

xmay-security.properties+DEFAULT_GROUP+public -> NULL

Nacos 服务器这边的处理如下, 根据 “xmay.properties+DEFAULT_GROUP+public” 去查询, 没有找到对应的缓存配置 

因为服务端这边的配置缓存是根据 数据库中的记录的 tenant 来进行存储的, 该记录在数据库中 tenant 为 “”, 然后缓存的 key 是 “xmay.properties+DEFAULT_GROUP”

然后这里返回服务端的 md5 为 空字符串, 然后 外层比较失败

然后将给定的 groupKey 添加到变动列表中, 视为变动了 

然后 再外层结束本次 “/config/listener” 的请求, 然后这里的 probeRequestSize 是请求的 “Listening-Configs” 的数据长度, 为客户端这边传递过来的 “Listening-Configs” 字符串 

服务器这边 groupKey 的生成

在 nacos 中 容器初始化的时候, ExternelDumpService 实例初始化的时候, 从数据库中加载 各个配置信息, 添加到 ConfigCacheService.CACHE 中 

这里是生成 groupKey, makeSure 是生成新的 CacheItem, 然后将给定的条目添加到 ConfigCacheService.CACHE 中 

然后这个 groupKey 的生成规则如下, 如果 tenant 为空, 则会去掉 tenant 

比如我们这里 数据库中的 tenant 为空, 则 key 为 “xmay.properties+DEFAULT_GROUP”

客户端这边 groupKey 的生成

这里是从 cacheDatas 中获取的各个配置项, 配置项中携带了 dataId, groupId, tenant 等等信息 

然后 cacheDatas 的数据来自于 ClientWorker, 然后我们看一下这部分的配置 从哪里来

虽然 我们基本上都知道 tenant, 是来自于 bootstrap.yml 中配置的 spring.cloud.nacos.config.namespace, spring.cloud.nacos.config.group, 但是我们这里需要从代码层面 跟踪一下

CacheData 的 tenant 的数据来自于上游的 ServerHttpAgent

然后 ServerHttpAgent 的配置信息来自于 nacosConfigProperties.assembleConfigServiceProperties

然后这里是 nacosConfigProperties.assembleConfigServiceProperties 的处理如下, 读取的就是 spring.cloud.nacos.config 中的相关的配置了 

解决方式

  1. 因为数据库中, 我们对应的需要获取的配置信息 namespace 为 空, 所以我们这里客户端将 namespace 配置为 “” 既可以解决问题 
  2. 使用一个固定的 namespace, 比如 xxx, 而不是使用 默认的 “” namespace
  3. 在较高版本的 nacos 客户端中, 可以通过 spring.cloud.nacos.config.refreshEnabled 关闭配置的刷新, 进而就不会有 LongPollingRunnable 的执行线程, 也可以解决问题 
  4. 关闭这部分输出日志, 眼不见为净, 但是实际上 nacos 客户端 和 服务器 在一直交互, 在我们的项目中很多地方, 默认都是灭有这块的日志, 但是 nacos 客户端 和 服务器 在一直交互

LongPollingRunnable 的开关

在 ApplicationContext 加载完成之后, 这里的处理会将各个配置项添加到 ClientWorker.cacheMap

然后后面 ClientWorker 发现 ClientWorker.cacheMap 中有需要监听的 配置信息, 则会启动 LongPollingRunnable

然后 各个配置项上面有一个 isRefreshable 的标记, 比如 ext-config 的这部分的 isRefreshable 默认为 false 

namespace 为 “public” 是怎么获取到 nacos 这边的 tenant 为空 的配置的?

来自于 nacos 这边的兼容处理, 将客户端这边的 “public” 的 namespace 转换为了获取服务器这边的 “” 的 namespace

也就是客户端那边传递 “” 或者传递 “public”, 服务器这边均是获取的 “” 的 namespace 的数据 

存在的问题就是, 如果服务器这边加入的 “public” 的 namespace 的 config_info 的记录, 从客户端这边永远也获取不到 

原始问题的原始推导

呵呵 原始问题, 我们这边只能够获取客户端这边的相关信息, 客户端这边发送的请求, 客户端这边拿到的服务器的响应 

可以很明显观察到的一个现象是 “/config/listener” 这边很快的就响应回来了, 然后 按照正常情况 nacos 这边配置没有更新的话, 它应该是有一定的超时时间的, 是客户端这边传入的 “Long-Pulling-Timeout” 默认值为 30s

但是 它立即就返回回来了, 然后 响应了一个改动 “xmay.properties+DEFAULT_GROUP+public”

这里面肯定是存在问题的, 然后接着 是来代码层面跟一下 nacos 这边的处理, 大致的判断一下问题, 结合日志, dump 信息 确认一下问题

在 “/config/listener” 中看到一部分代码很相似, 这部分代码会立即返回 响应改动的 配置列表给客户端, 然后就这里的是会输出日志的, 然后我们结合日志 来确认一下走的是这段代码

日志信息如下, 搜索 “instant” 可以发现 代码中仅有此处有如下 字面量, 可以确认 “/config/listener” 这边确实是走的如下逻辑, 可以推断出的就是 整个比较过程是在 Md5Util.compareMd5

2023-09-06 16:01:07,023|1|null|10.70.202.14|get|xmay.properties|DEFAULT_GROUP|public||unknown
2023-09-06 16:01:07,238|1|instant|10.70.202.14|polling|3|144|1
2023-09-06 16:01:07,344|2|null|10.70.202.14|get|xmay.properties|DEFAULT_GROUP|public||unknown
2023-09-06 16:01:07,557|0|instant|10.70.202.14|polling|3|144|1

比较的处理如下, 最开始我以为是 客户端 和 服务器 这边计算 md5 的方式有差异, 到时 md5 的计算一直匹配不上, 然后 这就需要 服务器这边的 dump 的信息了 

然后 从 nacos 上面 dump 一份 堆dump 文件, 然后 基于 jhat 来进行分析

找到 groupKey 包含 “xmay.properties” 的 com.alibaba.nacos.config.server.model.CacheItem

可以看到的是 md5 这边计算的结果, 服务器 和 客户端这边是一致的

然后 偶然的情况下 看到了这个 groupKey, 发现 服务器这边的groupKey 和 客户端这边传递过来服务器生成的groupKey 是不匹配的, 然后就有了 本文上面开篇的调试流程

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-12 09:40:05       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-12 09:40:05       101 阅读
  3. 在Django里面运行非项目文件

    2024-04-12 09:40:05       82 阅读
  4. Python语言-面向对象

    2024-04-12 09:40:05       91 阅读

热门阅读

  1. 程序“猿”初学者学习计划

    2024-04-12 09:40:05       132 阅读
  2. 基础算法学习笔记(C++)

    2024-04-12 09:40:05       133 阅读
  3. 支持向量机和感知机有什么关联?

    2024-04-12 09:40:05       148 阅读
  4. win下VScode中C++里Compile&Debug

    2024-04-12 09:40:05       118 阅读
  5. string的使用

    2024-04-12 09:40:05       41 阅读
  6. MySQL面试题系列-15

    2024-04-12 09:40:05       36 阅读
  7. 尝试CSDN语法使用-1

    2024-04-12 09:40:05       43 阅读
  8. tcp 为什么要三次握手

    2024-04-12 09:40:05       39 阅读
  9. 怎么“访问”Spring容器管理的bean?

    2024-04-12 09:40:05       41 阅读
  10. 深入理解Vue 3中的自定义Hooks

    2024-04-12 09:40:05       146 阅读