首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >修改服务器 DNS 后,正在运行的 Go/Java 程序不生效,必须重启进程?

修改服务器 DNS 后,正在运行的 Go/Java 程序不生效,必须重启进程?

原创
作者头像
高老师
发布2026-09-22 14:31:33
发布2026-09-22 14:31:33
420
举报

Markdown 技术博客:修改服务器DNS后,正在运行的Go/Java程序不生效,必须重启进程?

前言

昨天线上遇到一个非常经典的DNS踩坑问题:

我在Linux服务器修改了 /etc/resolv.conf 更换DNS nameserver,但是已经在运行的Go程序,新DNS配置完全不生效,只有手动重启进程之后,新DNS才会正常使用

我一开始理所当然地以为:系统resolv.conf是全局动态配置,改完文件,所有正在运行的程序会自动感知并使用新DNS。

实际现象狠狠打脸,顺着这个问题深挖,发现Go、Java在这块的行为还不一样,很多后端开发都会有这个认知误区,于是整理成这篇笔记。

重要区分两个概念(绝大多数人混淆在这里) 1. DNS服务器地址(nameserver):去哪里发起DNS查询(resolv.conf里的nameserver配置) 2. 域名解析结果缓存:域名解析得到的IP,程序在内存缓存多久

这两个完全独立,不要混在一起。本次问题是nameserver不自动刷新,不是域名IP缓存。

一、核心结论一句话

Linux下修改 /etc/resolv.conf 的nameserver:

已经启动运行的进程,默认不会自动重新加载resolv.conf,不会自动切换到新DNS服务器;必须重启进程,进程启动瞬间才会读取这份配置。

resolv.conf 不是实时监听的全局热配置,它是进程启动时一次性读取的配置文件。

二、Go语言的行为细节

Go分两种解析模式,取决于编译参数CGO_ENABLED

1. CGO_ENABLED=0(netgo,纯Go内置DNS解析器,线上很常用)

  • 程序启动时一次性加载 /etc/resolv.conf,存入内存// 强制重新加载 resolv.conf,刷新nameserver net.DefaultResolver.DialContext = nil net.DefaultResolver.LoadResolvConf()可以封装成一个管理接口,例如/admin/reload-dns,修改DNS配置后调用接口刷新,无需重启Go应用。
  • 不会自动监听文件变更,修改resolv.conf,老进程看不到新nameserver
  • ✅ 优势:支持代码手动重载DNS配置,不用重启服务

2. CGO_ENABLED=1(调用系统glibc的getaddrinfo)

  • 底层交给操作系统C库处理,和Java行为基本一致
  • glibc不会自动监听resolv.conf变更,没有原生办法动态刷新nameserver,只能重启进程

查看当前Go编译使用的CGO配置go env CGO_ENABLED

三、Java的行为细节(坑比Go更多)

OpenJDK在Linux上默认调用glibc getaddrinfo,和Go开启CGO的行为一致。

  1. nameserver(DNS服务器地址) JVM进程启动时,glibc读取一次resolv.conf;运行期间修改resolv.conf,JVM不会自动切换DNS服务器,原生JDK没有官方API重载resolv.conf。想动态刷新glibc的resolv只能用JNA/JNI调用C函数res_init(),属于hack方案,生产环境不推荐。
  2. Java独有大坑:JVM内置域名IP内存缓存 就算nameserver已经更新成功,JVM内部还会缓存「域名→IP」解析结果,这个是JVM独立维护,和glibc无关!# JVM启动参数 -Dnetworkaddress.cache.ttl=30 -Dnetworkaddress.cache.negative.ttl=10
  3. networkaddress.cache.ttl:解析成功的IP缓存时间。无SecurityManager默认30s;开启SecurityManager会变成永久缓存(-1)
  4. networkaddress.cache.negative.ttl:域名解析失败的缓存时间,默认10s

⚠️ 重点提醒:关闭JVM DNS缓存(设置为0)只能让每次查询都去DNS服务器请求,不能刷新nameserver。 nameserver地址如果没更新,就算关闭缓存,请求依旧发往旧DNS服务器。

启动示例:

代码语言:bash
复制
java -Dnetworkaddress.cache.ttl=0 -Dnetworkaddress.cache.negative.ttl=0 -jar app.jar

四、Go vs Java 行为对比表

项目

Go(CGO=0 netgo)

Go(CGO=1)

Java OpenJDK(Linux默认)

修改系统nameserver,运行进程自动生效

❌ 不会自动监听

❌ 不会自动监听

❌ 不会自动监听

是否支持代码动态重载nameserver

✅ 原生LoadResolvConf()

❌ 依赖glibc,无原生方案

❌ 无官方API,只能JNA hack

是否自带域名IP内存缓存

❌ net包默认不缓存解析结果

❌ 由glibc控制

✅ JVM自带独立DNS内存缓存

五、额外容易踩的坑(生产必看)

坑1:/etc/resolv.conf 会被系统自动覆盖

云服务器很多默认启用systemd-resolved / NetworkManager。

你手动修改resolv.conf,过一段时间系统自动覆盖回旧配置。

解决方案:不要直接编辑resolv.conf,修改网卡/network配置。

坑2:本机存在本地DNS缓存服务

服务器上跑了systemd-resolved、dnsmasq、nscd。

就算应用读到新nameserver,域名查询可能被本地缓存拦截,拿到旧解析结果。

坑3:容器环境(Docker / K8s)

宿主机修改DNS不会影响容器内部

容器内resolv.conf是容器创建时注入的配置。只重启容器内应用进程没用,必须重建Pod/容器,新容器才会加载新DNS配置。

六、线上推荐方案

方案1:简单稳妥(绝大多数业务首选)

修改DNS配置 → 重启业务进程。一次性刷新nameserver + 应用内部DNS缓存,稳定无副作用。

方案2:不重启服务(仅Go netgo可用)

在Go服务增加管理接口,调用LoadResolvConf(),修改resolv.conf后调用接口刷新DNS配置,实现无重启切换DNS服务器。

Java不推荐这种方案,实现复杂、稳定性差。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • Markdown 技术博客:修改服务器DNS后,正在运行的Go/Java程序不生效,必须重启进程?
    • 前言
    • 一、核心结论一句话
    • 二、Go语言的行为细节
      • 1. CGO_ENABLED=0(netgo,纯Go内置DNS解析器,线上很常用)
      • 2. CGO_ENABLED=1(调用系统glibc的getaddrinfo)
    • 三、Java的行为细节(坑比Go更多)
    • 四、Go vs Java 行为对比表
    • 五、额外容易踩的坑(生产必看)
      • 坑1:/etc/resolv.conf 会被系统自动覆盖
      • 坑2:本机存在本地DNS缓存服务
      • 坑3:容器环境(Docker / K8s)
    • 六、线上推荐方案
      • 方案1:简单稳妥(绝大多数业务首选)
      • 方案2:不重启服务(仅Go netgo可用)
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档