上三篇把 HTTP(【001】)、TLS(【002】)、TCP/IP 与超时(【003】)从上到下捋了一遍。但你有没有想过,浏览器敲下 api.example.com 之后,第一件事不是握手,而是先问一句:「这个名字对应哪个 IP?」——这就是 DNS 干的活。平时它安安静静地工作,你几乎感觉不到;可一旦出问题,表现往往是「全站打不开」「接口偶尔连不上」「切了服务器 IP 但流量还往旧机器跑」,而且应用日志里只有一句冷冰冰的 UnknownHostException,没有更多线索。

做微服务之后,DNS 的戏份更重了:K8s 里 Service 靠 CoreDNS 解析,Nacos 里服务发现本质上也是「名字 → 地址」的映射,小程序要求域名必须备案且走 HTTPS……这些看似不相关的事,根子上都和「域名怎么变成 IP」有关。下面我按「域名结构 → 解析过程 → 缓存与 TTL → Java/Spring Boot 里的坑 → 微服务内部 DNS → 小程序合法域名 → 排障」的顺序往下聊。


1. 域名的层级结构 🌳

域名不是一个扁平的字符串,它是一棵从右往左读的树:

api.example.com.
 │     │      │  │
 │     │      │  └─ 根域(通常省略这个点)
 │     │      └──── 顶级域(TLD):com / cn / org / net …
 │     └─────────── 二级域:example(你花钱注册的那个)
 └───────────────── 三级域 / 子域:api(你自己随便配的)

1.1 常见域名层级

层级 示例 谁管
根域 . 全球 13 组根服务器 ICANN
顶级域 TLD .com .cn .io .dev 各注册局
二级域 example.com 你(在域名注册商买的)
三级域 / 子域 api.example.com admin.example.com 你自己在 DNS 控制台配
四级及更深 user.api.example.com 你自己配(少见,但合法)

和你的关系

  • 买域名 = 买二级域的管理权。
  • 子域随便加,不用再花钱,但每个子域都需要对应的 DNS 记录。
  • 小程序合法域名要求的是完整域名(含子域),不是只登记二级域就行。

1.2 常见记录类型

记录类型 作用 示例
A 域名 → IPv4 地址 api.example.com → 203.0.113.10
AAAA 域名 → IPv6 地址 api.example.com → 2001:db8::1
CNAME 域名 → 另一个域名(别名) cdn.example.com → example.com.cdn.provider.net
MX 邮件服务器 example.com → mail.example.com
TXT 文本记录(SPF、域名验证等) Let’s Encrypt DNS-01 验证、企业邮箱验证
NS 指定该域的权威 DNS 服务器 example.com → ns1.dnsprovider.com
SRV 服务定位(含端口) K8s headless Service、某些 RPC 框架

CNAME 的工程直觉

  • CDN 接入时,云厂商让你把 cdn.example.com CNAME 到他们的域名——这样用户解析 cdn.example.com 时,会跟着 CNAME 链最终拿到 CDN 边缘节点的 IP。
  • CNAME 不能和其他记录共存于同一个名字(RFC 限制)。所以裸域(example.com)通常不能直接 CNAME,要么用 A 记录,要么用部分 DNS 厂商提供的「CNAME 拉平 / ALIAS」功能。
  • 配了 CNAME 后,TTL 要看两层:你自己的 CNAME 记录 TTL + 目标域名的 A 记录 TTL,取较短的那个才是实际缓存时间。

2. 解析过程:从浏览器到权威 DNS 🔍

你在浏览器敲下 https://api.example.com/api/users,在 TCP 握手(【003】)之前,要先拿到 IP。整个过程大致是:

2.1 解析链路

① 浏览器 DNS 缓存(秒级,关标签页可能就没了)
    ↓ 没命中
② OS DNS 缓存(Windows: ipconfig /displaydns;Linux: systemd-resolved 等)
    ↓ 没命中
③ hosts 文件(Windows: C:\Windows\System32\drivers\etc\hosts;Linux: /etc/hosts)
    ↓ 没命中
④ 本地 DNS 服务器(递归解析器,通常是路由器/公司 DNS/运营商 DNS/公共 DNS 如 8.8.8.8)
    ↓ 缓存没命中,开始递归
⑤ 根 DNS → 返回 .com 的 NS
⑥ .com TLD DNS → 返回 example.com 的 NS
⑦ example.com 权威 DNS → 返回 api.example.com 的 A 记录(IP)
    ↓ 结果逐层缓存,带 TTL
⑧ 拿到 IP,开始 TCP 握手

2.2 递归 vs 迭代

  • 递归查询:客户端问本地 DNS 服务器,「你帮我查到底」,本地 DNS 服务器一路追到权威,把最终结果返回给客户端。你的应用发的就是递归查询
  • 迭代查询:本地 DNS 服务器问根 DNS,根说「我不知道,你去问 .com」,本地 DNS 再去问 .com……每一步只给「下一个该问谁」。DNS 服务器之间用的是迭代

你要记的结论:应用侧只管发一次查询,等结果就行。但如果本地 DNS 服务器挂了或慢了,你的应用就会卡在解析阶段——表现为 UnknownHostException 或连接前的长时间等待。

2.3 hosts 文件:开发环境的老朋友 📝

# Windows: C:\Windows\System32\drivers\etc\hosts
# Linux/Mac: /etc/hosts

127.0.0.1   local.test
127.0.0.1   api.local.test
192.168.1.100   dev-db.internal

优先级:hosts 文件通常优先于 DNS 查询(OS 默认行为,Linux 由 /etc/nsswitch.confhosts: 行控制)。

常见用途

  • 本地开发时把域名指向 127.0.0.1,配合自签证书(【002】)模拟 HTTPS。
  • 临时把生产域名指向测试 IP 做验证(用完务必删掉,否则你会困惑很久为什么「只有我的电脑访问不对」)。

常见事故

  • 忘了删 hosts 里的条目,导致「别人都正常,就我连不上」。
  • Docker 容器里没有宿主机的 hosts 条目——容器有自己的 /etc/hosts

3. TTL 与缓存:「改了 DNS 为什么没生效」 ⏳

3.1 TTL 是什么

每条 DNS 记录都带一个 TTL(Time To Live),单位秒,告诉缓存「这条结果你可以用多久」。

# 用 nslookup 看 TTL
nslookup -type=A api.example.com

# 用 dig 看更详细(Linux / Git Bash + bind-utils)
dig api.example.com A

dig 输出里的 ANSWER SECTION

api.example.com.    300    IN    A    203.0.113.10
                     ↑
                   TTL=300 秒(5 分钟)

3.2 缓存在哪些地方

缓存位置 刷新方式
浏览器 浏览器内部 DNS 缓存 关闭浏览器 / chrome://net-internals/#dns → Clear
OS 系统 DNS 缓存 Windows: ipconfig /flushdns;Linux: systemd-resolve --flush-caches 或重启 nscd
本地 DNS 服务器 运营商/公司 DNS 递归器 你控制不了,等 TTL 过期
JVM InetAddress 缓存 见下文 3.3

3.3 JVM 的 DNS 缓存(Java 开发必知)⚠️

Java 的 InetAddress 有自己的缓存,独立于 OS

JVM 属性 含义 默认值
networkaddress.cache.ttl 成功解析的缓存秒数 有 SecurityManager 时为 -1(永久缓存);无 SM 时跟随系统 TTL(JDK 实现相关)
networkaddress.cache.negative.ttl 解析失败的缓存秒数 10 秒

永久缓存的坑:如果 JVM 启动时解析了 api.example.com → 1.2.3.4,之后你在 DNS 控制台改成了 5.6.7.8JVM 可能永远不会感知到(直到重启)。

怎么调

# JVM 启动参数(推荐方式)
-Dsun.net.inetaddr.ttl=60
-Dsun.net.inetaddr.negative.ttl=10

# 或在 $JAVA_HOME/conf/security/java.security 里改
networkaddress.cache.ttl=60
networkaddress.cache.negative.ttl=10

微服务场景下建议设 30~60 秒:服务实例上下线时,调用方需要在合理时间内感知到 IP 变化。设太短会增加 DNS 查询压力,设太长会导致流量打到已下线的实例。

3.4 域名切换时的 TTL 策略

场景:要把 api.example.com 从旧服务器(IP-A)迁移到新服务器(IP-B)。

推荐步骤

  1. 提前降 TTL:迁移前 24~48 小时,把 TTL 从 3600(1 小时)降到 60(1 分钟)。等旧 TTL 过期,全网缓存都刷成短 TTL。
  2. 切换 A 记录:改成 IP-B。因为 TTL 只有 60 秒,大部分客户端 1 分钟内就能拿到新 IP。
  3. 观察:确认流量全部切到 IP-B,旧服务器不再有请求。
  4. 恢复 TTL:稳定后把 TTL 改回 3600 或更长,减少 DNS 查询量。

反例:TTL 还是 3600,直接改 IP → 最坏情况下,部分用户 1 小时后才能访问到新服务器。如果旧服务器已经关了,这 1 小时内他们就全挂了。


4. Java / Spring Boot 里的 DNS 相关问题 ☕

4.1 UnknownHostException 排查清单

看到这个异常,按顺序查:

  1. 域名拼写:低级但高频。api.exmaple.com(拼错了)、多了空格、配置文件里用了中文句号。
  2. DNS 服务器可达吗:容器里 /etc/resolv.conf 指向的 DNS 是否通(nslookup 试一下)。
  3. hosts 文件:是否有错误条目覆盖了正确解析。
  4. JVM DNS 缓存:之前解析失败被缓存了(negative.ttl 默认 10 秒,等一下再试)。
  5. 网络策略:K8s NetworkPolicy、安全组是否拦了 UDP 53(DNS 端口)。
  6. IPv6 优先:某些环境下 JVM 优先解析 AAAA 记录,但网络不支持 IPv6 → 超时后才 fallback 到 A 记录,表现为「解析特别慢」。可用 -Djava.net.preferIPv4Stack=true 强制 IPv4。

4.2 Spring Boot 配置里的域名

spring:
  datasource:
    url: jdbc:mysql://db.internal:3306/mydb    # 用域名而非 IP
  data:
    redis:
      host: redis.internal
  cloud:
    openfeign:
      client:
        config:
          order-service:
            url: http://order-service:8080       # 微服务名或内部域名

用域名还是 IP?

  • 生产推荐域名:IP 变了只需改 DNS,不用改配置重启应用。
  • 内网域名:K8s Service 名、Nacos 服务名、内部 DNS 区域(如 *.internal)。
  • 直接写 IP 的场景:本地开发 127.0.0.1、或确实没有 DNS 的极简环境。

4.3 连接池与 DNS 变更的关系

Hikari 连接池:连接建立时解析域名拿到 IP,之后复用的是 TCP 连接(已经绑定了 IP)。即使 DNS 改了,已有连接不会自动切到新 IP——要等连接到达 max-lifetime 被回收、重建时才会重新解析。

所以

  • max-lifetime 不要设太大(建议 < 数据库 wait_timeout,且合理短,如 30 分钟)。
  • 数据库做主从切换(域名不变、IP 变了)后,如果连接池里全是旧连接,可能需要滚动重启或触发连接池刷新。

HTTP 连接池(OkHttp、Apache HttpClient)同理:Keep-Alive 的连接绑定的是 IP,DNS 变了不会自动感知。连接池的 keepAliveDuration / maxIdleTime 决定了旧连接多久被淘汰。


5. 微服务里的「DNS」:不只是公网那一套 🏗️

微服务架构下,「名字 → 地址」的需求更频繁,但实现方式不止传统 DNS 一种。

5.1 K8s Service DNS(CoreDNS)

K8s 集群内,每个 Service 自动获得一个 DNS 名:

<service-name>.<namespace>.svc.cluster.local

例如:

order-service.default.svc.cluster.local → ClusterIP 10.96.xxx.xxx

Pod 里的 /etc/resolv.conf 指向 CoreDNS,所以你在 Java 里写 http://order-service:8080/api/orders 就能解析——前提是在同一个 namespace(同 namespace 可省略后缀)。

常见问题

现象 原因
UnknownHostException: order-service 跨 namespace 没写全名;或 Service 还没创建
解析到 ClusterIP 但连不上 Service 的 selector 没匹配到 Pod;Pod 没 Ready
解析慢 CoreDNS Pod 资源不足、ndots 配置导致多次无效查询

ndots 的坑:K8s 默认 ndots:5,意味着域名里点号少于 5 个时,会先尝试拼接搜索域(如 order-service.default.svc.cluster.localorder-service.svc.cluster.local……)。对于外部域名(如 api.example.com,只有 2 个点),会先尝试好几个内部拼接全失败后,才去查真正的外部 DNS——白白多了几次查询延迟

缓解

  • 外部域名末尾加点:api.example.com.(绝对域名,跳过搜索域拼接)。
  • 或在 Pod spec 里调低 ndots(需评估对内部解析的影响)。

5.2 Nacos / Eureka / Consul 服务发现

这些注册中心做的事和 DNS 类似——「服务名 → 实例列表(IP:Port)」——但走的不是 DNS 协议,而是自己的 HTTP/gRPC API:

调用方 → 问 Nacos:order-service 有哪些实例?
Nacos → 返回:[192.168.1.10:8080, 192.168.1.11:8080]
调用方 → 客户端负载均衡,选一个发请求

和 DNS 的区别

维度 传统 DNS 服务注册中心
协议 UDP/TCP 53 端口 HTTP/gRPC
健康检查 无(DNS 不管实例是否健康) 有(心跳、健康检查,不健康自动摘除)
负载均衡 轮询(多 A 记录)或 GSLB 客户端侧策略(随机、权重、一致性哈希等)
变更感知 依赖 TTL 过期 推送 / 长轮询,秒级感知
元数据 只有 IP(SRV 可带端口) 可带版本、权重、标签等

Spring Cloud 里的对照

spring:
  cloud:
    nacos:
      discovery:
        server-addr: nacos.internal:8848
        namespace: dev

@FeignClient(name = "order-service") 时,Feign + LoadBalancer 会从 Nacos 拿实例列表,不走 DNS。但如果你写的是 url = "http://order-service:8080"(硬编码 URL),那就走 DNS 了——两种方式别混。

5.3 DNS 与服务发现的混合场景

实际项目里常常两者并存:

  • 集群内微服务互调:走 Nacos/K8s Service(服务发现)。
  • 调外部依赖(支付回调、合作伙伴 API):走公网 DNS。
  • 数据库、Redis、MQ:走内部 DNS 或直接 IP。

排障时先分清:这个调用走的是 DNS 还是服务注册中心?UnknownHostException 说明走的是 DNS;如果是 Nacos 找不到服务,报的是 No instances available for order-service——完全不同的排查方向。


6. 小程序合法域名:备案、HTTPS 与 DNS 的交叉地带 📱

小程序(微信、支付宝、抖音等)对后端接口域名有一套平台级限制,和纯浏览器环境不同。这些限制的根子大半和 DNS、证书、备案有关。

6.1 基本要求(以微信小程序为代表,其他平台类似)

要求 说明 和前几篇的关系
必须 HTTPS wx.request 只允许 https:// 开头的 URL 【002】证书与 TLS
域名必须备案 大陆服务器的域名需要 ICP 备案号 政策要求,非技术层面
域名需在后台登记 小程序管理后台 → 开发管理 → 服务器域名 登记的是完整域名,不是泛域名
不能用 IP 地址 https://203.0.113.10/api 不行 必须走 DNS 解析
默认端口 443 非 443 端口部分平台不支持或需额外配置 【002】提过
证书链完整 缺中间证书 → 部分手机校验失败 【002】fullchain
TLS 版本 平台可能要求 TLS 1.2+ 【002】协议与套件

6.2 常见翻车场景

场景 1:域名没备案

现象:小程序调接口报「不在以下 request 合法域名列表中」
原因:域名没备案,或备案了但没在小程序后台登记
排查:先在浏览器打开同一 URL 确认能访问 → 再去小程序后台检查域名列表

场景 2:证书链不完整

现象:浏览器正常(浏览器会自动补链),但小程序/安卓低版本报 SSL 错误
原因:服务器只配了叶子证书,没配中间证书
排查:openssl s_client -connect api.example.com:443 -servername api.example.com
      看 Certificate chain 是否完整(【002】7.2 节)

场景 3:开发环境用 IP 或 localhost

现象:开发者工具里勾了「不校验合法域名」能通,真机预览不行
原因:真机不认 IP、不认 localhost、不认自签证书
正确做法:
  - 开发阶段用「不校验」快速迭代
  - 联调/体验版起,必须用已备案域名 + 正式证书
  - 或用内网穿透工具(如 ngrok)映射到有证书的域名

场景 4:域名切换后小程序还打旧 IP

现象:DNS 已改,浏览器正常,但部分用户的小程序还连旧服务器
原因:
  - 微信客户端有自己的 DNS 缓存(不受你控制)
  - 运营商 DNS 缓存未过期
  - JVM DNS 缓存(如果你的后端也调了这个域名)
缓解:提前降 TTL(3.4 节的策略);切换后保持旧服务器运行一段时间做兜底

6.3 多环境域名管理建议

环境 域名示例 备案 证书 小程序后台
本地开发 localhost / local.test 不需要 自签 / mkcert 勾「不校验」
测试环境 test-api.example.com 需要 正式证书(或 Let’s Encrypt) 可不登记(用体验版+不校验)
预发环境 staging-api.example.com 需要 正式证书 建议登记(模拟真实)
生产环境 api.example.com 必须 正式证书 必须登记

一个省心的做法:所有环境共用同一个二级域(example.com),子域区分环境,证书用泛域名证书(*.example.com)覆盖所有子域——一张证书管全部,续期也只需一次。


7. CDN 与 DNS 的协作:CNAME 链与就近接入 🌍

CDN 的核心思路是「让用户访问离自己最近的节点」,而这个「就近」的实现,很大程度上依赖 DNS。

7.1 接入 CDN 后的解析链路

用户请求 cdn.example.com
    ↓
① 本地 DNS 查 cdn.example.com → CNAME → example.com.cdn-provider.net
    ↓
② 本地 DNS 查 example.com.cdn-provider.net → CDN 智能 DNS
    ↓
③ CDN 智能 DNS 根据用户 IP(本地 DNS 的 IP)判断地理位置
    → 返回最近的边缘节点 IP(如 120.xxx.xxx.xxx)
    ↓
④ 用户连接边缘节点,边缘节点有缓存就直接返回,没有就回源到你的服务器

7.2 和后端的关系

  • 静态资源(JS/CSS/图片)走 CDN 域名,API 接口走源站域名——别把 API 也扔 CDN 后面(除非你清楚 CDN 对动态请求的处理策略)。
  • 回源 Host:CDN 回源时带的 Host 头要和你的 Nginx/Spring Boot 虚拟主机匹配,否则可能 404 或走错应用。
  • 缓存策略:CDN 会缓存响应。如果你的 API 响应没设 Cache-Control: no-store,CDN 可能把 JSON 也缓存了——用户 A 看到用户 B 的数据(【001】4.5 节提过 Cache-Control)。

7.3 GSLB(全局负载均衡)

大厂或多机房部署时,DNS 层面做地理负载均衡:同一个域名,北方用户解析到北京机房 IP,南方用户解析到广州机房 IP。实现方式:

  • 云厂商 DNS 的「智能解析」:按线路(电信/联通/移动)、按地域返回不同 IP。
  • 自建权威 DNS + 健康检查:某机房挂了,自动把该区域的解析切到存活机房。

和你的关系:如果你的项目做了多机房,DNS 切换是灾备的关键一环。切换速度取决于 TTL——再次印证 3.4 节的策略。


8. DNS 安全:劫持、污染与防护(知道边界)🛡️

8.1 常见威胁

威胁 原理 表现
DNS 劫持 中间人或恶意 DNS 服务器返回假 IP 访问正确域名却到了钓鱼页面
DNS 污染 在传输途中注入伪造的 DNS 响应 部分地区/网络无法解析某些域名
DNS 缓存投毒 向递归 DNS 服务器注入假记录 大范围用户被导向错误 IP

8.2 防护手段(了解即可)

手段 说明
DNSSEC 对 DNS 响应做数字签名,防篡改;部署率仍不高
DoH(DNS over HTTPS) DNS 查询走 HTTPS 加密通道(浏览器支持渐多)
DoT(DNS over TLS) DNS 查询走 TLS 加密(Android 9+ 的「私人 DNS」)
使用可信 DNS 公共 DNS(8.8.8.8、1.1.1.1、114.114.114.114)比某些运营商 DNS 更可靠

和后端的关系:你的服务器本身不太会被 DNS 劫持影响(服务器是被连接的一方),但如果你的 Java 应用作为客户端调外部 API(支付、短信、地图),DNS 被污染会导致请求打到错误地址。生产服务器的 /etc/resolv.conf 建议指向可控的内部 DNS 或可信公共 DNS,而不是随便用 DHCP 分配的。


9. 排障工具与命令备忘 🔧

9.1 nslookup(Windows / Linux / Mac 都有)

# 基本查询
nslookup api.example.com

# 指定 DNS 服务器查询(排除本地 DNS 问题)
nslookup api.example.com 8.8.8.8

# 查特定记录类型
nslookup -type=CNAME cdn.example.com
nslookup -type=MX example.com
nslookup -type=TXT example.com

9.2 dig(Linux / Git Bash,需安装 bind-utils)

# 详细查询(含 TTL、权威信息)
dig api.example.com A

# 指定 DNS 服务器
dig @8.8.8.8 api.example.com A

# 追踪完整解析链路(从根开始)
dig +trace api.example.com A

# 只看精简结果
dig +short api.example.com A

dig +trace 特别有用:它模拟递归解析器的行为,从根 DNS 一路查到权威,能看到每一跳返回了什么——排查「某个环节返回了错误结果」时的利器。

9.3 curl --resolve(绕过 DNS 直连指定 IP)

# 把 api.example.com 强制解析到 203.0.113.10,不走 DNS
curl -v --resolve api.example.com:443:203.0.113.10 "https://api.example.com/api/users/1"

用途

  • 新服务器还没切 DNS,想提前验证 HTTPS 和接口是否正常。
  • 怀疑 DNS 解析到了错误 IP,用 --resolve 绕过确认。

9.4 Windows 专用

# 查看 OS DNS 缓存
ipconfig /displaydns

# 清除 OS DNS 缓存
ipconfig /flushdns

# 查看当前 DNS 服务器配置
ipconfig /all | findstr "DNS"

9.5 Linux 专用

# 查看 DNS 配置
cat /etc/resolv.conf

# 查看解析顺序(hosts 文件 vs DNS 的优先级)
cat /etc/nsswitch.conf | grep hosts

# systemd-resolved 环境下查看缓存统计
resolvectl statistics

# 清除 systemd-resolved 缓存
resolvectl flush-caches

9.6 K8s 内 DNS 排障

# 在 Pod 内测试 DNS 解析
kubectl exec -it <pod-name> -- nslookup order-service
kubectl exec -it <pod-name> -- nslookup order-service.default.svc.cluster.local

# 查看 Pod 的 DNS 配置
kubectl exec -it <pod-name> -- cat /etc/resolv.conf

# 查看 CoreDNS 日志
kubectl logs -n kube-system -l k8s-app=kube-dns

# 用 debug Pod 排查(如果业务 Pod 没有 nslookup)
kubectl run dns-debug --image=busybox:1.36 --rm -it --restart=Never -- nslookup order-service

10. 综合示例:一次「域名解析间歇性失败」的排障 💻

场景:生产环境,Spring Boot 应用调合作伙伴的支付回调确认接口 https://pay.partner.com/api/verify,偶尔报 UnknownHostException,但大部分时候正常。

第一步:确认不是拼写问题

# 在应用所在服务器上手动解析
nslookup pay.partner.com
dig pay.partner.com A

能解析出来 → 域名本身没问题,是间歇性的。

第二步:检查 DNS 服务器

cat /etc/resolv.conf
nameserver 10.0.0.2    ← 内部 DNS
nameserver 10.0.0.3    ← 备用
# 分别测两台 DNS 的响应
dig @10.0.0.2 pay.partner.com A
dig @10.0.0.3 pay.partner.com A

发现 10.0.0.3 偶尔超时 → 内部 DNS 备用节点不稳定。当主 DNS 也偶尔慢时,OS 切到备用,备用又超时 → 解析失败。

第三步:检查 JVM DNS 缓存配置

# 看 JVM 启动参数
ps aux | grep java | grep inetaddr

没有设置 networkaddress.cache.negative.ttl → 默认 10 秒。解析失败后 10 秒内的所有请求都会直接失败(不会重新查 DNS)。

第四步:修复

  1. 修复内部 DNS 备用节点(根因)。
  2. JVM 参数加上-Dsun.net.inetaddr.negative.ttl=3(失败缓存缩短到 3 秒,减少影响面)。
  3. 业务代码加重试:对 UnknownHostException 做一次重试(间隔 2 秒),覆盖 DNS 瞬时抖动。
  4. 考虑加本地 DNS 缓存(如 dnsmasqsystemd-resolved),减少对上游 DNS 的依赖。

11. 域名注册与管理的工程常识 📋

这一节不深入注册流程,但有几个和开发相关的点值得提一句:

11.1 域名到期

域名和证书一样会过期。过期后域名可能被释放、被他人抢注——如果你的生产服务挂在这个域名上,后果不用多说。

建议

  • 域名自动续费开起来。
  • 域名管理账号不要绑在某个离职同事的个人邮箱上(真实事故)。

11.2 域名转移与 NS 变更

换 DNS 服务商时,需要在注册商处修改 NS 记录。NS 变更的全球生效时间可能长达 24~48 小时(因为 TLD 层的 NS 缓存 TTL 通常很长)。

操作顺序:先在新 DNS 服务商配好所有记录 → 再改 NS → 等生效 → 确认后再删旧 DNS 服务商的记录。反过来做会导致解析中断

11.3 泛域名证书与泛域名解析

  • 泛域名解析*.example.com → 某 IP,所有子域都指向同一个地址。方便但要注意安全——任何人知道你的域名都能构造 anything.example.com 打到你的服务器。
  • 泛域名证书*.example.com 覆盖一级子域(api.example.comadmin.example.com),但不覆盖裸域(example.com)和二级子域(a.b.example.com)。需要裸域的话,证书 SAN 里要同时包含 *.example.comexample.com

12. 和【001】【002】【003】叠在一起:P0 网络四篇的完整分层 📌

到这里,P0 阶段的网络核心知识已经从上到下串了一遍:

[ 浏览器输入 api.example.com ]
        │
        ▼
[ DNS 解析 ]  ← 本篇(004)
  hosts → OS 缓存 → 本地 DNS → 递归查询 → 权威 DNS
  JVM 缓存 / K8s CoreDNS / Nacos 服务发现
        │  拿到 IP
        ▼
[ TCP 连接 ]  ← 【003】
  三次握手 → connect timeout
  数据传输 → read timeout / 重传 / 窗口
  四次挥手 → TIME_WAIT / CLOSE_WAIT
        │
        ▼
[ TLS 握手 ]  ← 【002】
  证书链 → SAN → 协议版本
  SSLHandshakeException / PKIX
        │
        ▼
[ HTTP 请求/响应 ]  ← 【001】
  方法 → 状态码 → Header → Body
  CORS / 内容协商 / 缓存
        │
        ▼
[ 网关 → Tomcat → 业务代码 → 下游 ]

排障时从上往下过一遍:域名能解析吗 → IP 能连通吗 → TLS 能握手吗 → HTTP 状态码是什么 → 网关还是应用的问题。每一层有自己的工具和配置,别跨层猜。


小结 💡

  • 域名是一棵树,从右往左读:根 → TLD → 二级域(你买的)→ 子域(你配的)。记录类型里 A / CNAME / TXT / SRV 是后端最常碰到的。
  • 解析链路:浏览器缓存 → OS 缓存 → hosts → 本地 DNS → 递归查询到权威。任何一环出问题都可能导致 UnknownHostException 或解析到错误 IP。
  • TTL 决定缓存时长:改了 DNS 没生效,先看 TTL。域名迁移前提前降 TTL是基本操作。JVM 有独立的 DNS 缓存,生产建议设 30~60 秒,别让它永久缓存。
  • 微服务里「名字 → 地址」有两条路:传统 DNS(K8s CoreDNS、公网 DNS)和服务注册中心(Nacos/Eureka)。排障时先分清走的是哪条路,报错信息完全不同。
  • 小程序合法域名是 DNS + HTTPS + 备案的交叉要求:域名要备案、证书链要完整、必须 HTTPS 且默认 443、域名要在后台登记。开发阶段可「不校验」,上线前必须全部对齐。
  • CDN 靠 CNAME + 智能 DNS 实现就近接入;API 接口别随便过 CDN,注意 Cache-Control 防止动态数据被缓存。
  • 排障三板斧nslookup / dig 确认解析结果、curl --resolve 绕过 DNS 直连验证、ipconfig /flushdns 或 JVM 参数清缓存。

下一篇(005)预告 🎯:REST 风格与资源设计——URL 怎么切、Controller 怎么分层、和前端的接口契约怎么定,把【001】里的方法与状态码落到 Spring Boot 的工程实践里。

更多推荐