【004】DNS 与域名:对微服务、小程序合法域名意味着什么
上三篇把 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.comCNAME 到他们的域名——这样用户解析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.conf 的 hosts: 行控制)。
常见用途:
- 本地开发时把域名指向
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.8,JVM 可能永远不会感知到(直到重启)。
怎么调:
# 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)。
推荐步骤:
- 提前降 TTL:迁移前 24~48 小时,把 TTL 从 3600(1 小时)降到 60(1 分钟)。等旧 TTL 过期,全网缓存都刷成短 TTL。
- 切换 A 记录:改成 IP-B。因为 TTL 只有 60 秒,大部分客户端 1 分钟内就能拿到新 IP。
- 观察:确认流量全部切到 IP-B,旧服务器不再有请求。
- 恢复 TTL:稳定后把 TTL 改回 3600 或更长,减少 DNS 查询量。
反例:TTL 还是 3600,直接改 IP → 最坏情况下,部分用户 1 小时后才能访问到新服务器。如果旧服务器已经关了,这 1 小时内他们就全挂了。
4. Java / Spring Boot 里的 DNS 相关问题 ☕
4.1 UnknownHostException 排查清单
看到这个异常,按顺序查:
- 域名拼写:低级但高频。
api.exmaple.com(拼错了)、多了空格、配置文件里用了中文句号。 - DNS 服务器可达吗:容器里
/etc/resolv.conf指向的 DNS 是否通(nslookup试一下)。 - hosts 文件:是否有错误条目覆盖了正确解析。
- JVM DNS 缓存:之前解析失败被缓存了(
negative.ttl默认 10 秒,等一下再试)。 - 网络策略:K8s NetworkPolicy、安全组是否拦了 UDP 53(DNS 端口)。
- 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.local、order-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)。
第四步:修复
- 修复内部 DNS 备用节点(根因)。
- JVM 参数加上:
-Dsun.net.inetaddr.negative.ttl=3(失败缓存缩短到 3 秒,减少影响面)。 - 业务代码加重试:对
UnknownHostException做一次重试(间隔 2 秒),覆盖 DNS 瞬时抖动。 - 考虑加本地 DNS 缓存(如
dnsmasq或systemd-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.com、admin.example.com),但不覆盖裸域(example.com)和二级子域(a.b.example.com)。需要裸域的话,证书 SAN 里要同时包含*.example.com和example.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 的工程实践里。
更多推荐
所有评论(0)