用 DNS 做服务发现,TTL 设多少?客户端缓存、启动时只解析一次、不缓存各会怎样?SRV 的优先级和权重怎么起作用?服务晚上线会被负缓存拖多久?应答太大怎么办?这篇用自己写的权威 DNS 服务和 dnspython 逐项量出来。
解析方式 | 错误请求数(每次共 700 个;遵守 TTL 的做了 3 次,删除时刻 4.0 / 4.7 / 5.4 s) | 错误发生的时间范围(秒) | DNS 收到的查询数 ----------------------+--------------------------------------------------------+--------------------------------+----------- (a) 遵守 TTL · TTL 2 s | 0 / 19 / 13 | - | 4.7~6.0 | 5.4~6.0 | 7 / 7 / 7 (a) 遵守 TTL · TTL 5 s | 19 / 4 / 83 | 4.0~5.0 | 4.7~5.0 | 5.5~10.0 | 3 / 3 / 3 (a) 遵守 TTL · TTL 10 s | 105 / 90 / 83 | 4.0~10.0 | 4.7~10.0 | 5.5~10.0 | 2 / 2 / 2 (b) 启动时解析一次 | 168 | 4.0~13.9 | 1 (c) 不缓存 | 0 | - | 700 遵守 TTL 的缓存:删掉记录之后,缓存最多还能再用一个 TTL(具体多久取决于删除时刻与缓存过期的相位,所以做了 3 次),这期间约三分之一的请求打到已经停掉的后端;TTL 越短错误越少,但 DNS 查询越多;启动时只解析一次的客户端永远不会发现变化,错误一直持续到结束;不缓存的客户端在删除之后立刻不再出错,代价是每个请求一次 DNS 查询(50 个/秒)
缓存的 TTL 决定摘掉实例后还会有多少请求发过去;不缓存最准但每个请求一次 DNS 查询;只解析一次永远发现不了变化。
情形 | A 被选 | B 被选 | C 被选 | 占比 ---------+------+------+-------+--------------------- A、B、C 都在 | 2478 | 7522 | 0 | 24.8% / 75.2% / 0.0% A、B 都被删掉 | 0 | 0 | 10000 | 100.0% 给 C 优先级是“备用”的语义:只要还有优先级更高(数字更小)的目标,低优先级的目标一点流量都得不到;同优先级里按权重 1 : 3 分(约 25% / 75%);高优先级的都被删掉之后,备用的 C 才接管全部流量。注意:SRV 本身不包含健康状态,“都挂了”要靠有人把它们从区域里删掉
优先级是备用语义;同优先级内按权重分;SRV 不含健康状态。
客户端 | 服务上线时刻 | 客户端第一次解析成功 | 上线之后又过了多久才发现 | DNS 收到的查询数 -----------+--------+------------+--------------+----------- 遵守缓存(含负缓存) | 2.0 s | 20.2 s | 18.1 s | 2 不缓存 | 2.0 s | 2.0 s | 0.0 s | 3 “不存在”也是会被缓存的答案:服务晚于客户端的第一次查询才登记,遵守负缓存的客户端要等到负缓存 TTL(SOA minimum)过期才会再去问;不缓存的客户端在上线后下一次查询就成功
“不存在”也被缓存,新服务要等负缓存 TTL。
目标数 N | UDP 应答里的记录数 | TC 标志 | UDP 应答大小(字节) | TCP 应答里的记录数 | TCP 应答大小(字节) ------+-------------+-------+--------------+-------------+------------- 5 | 5 | False | 241 | 5 | 241 20 | 0 | True | 31 | 20 | 871 40 | 0 | True | 31 | 40 | 1711 100 | 0 | True | 31 | 100 | 4231 应答超过 512 字节时,这个服务端把答案清空并置 TC 位,告诉客户端“去用 TCP 重查”(这是我这个服务端的做法;有的服务端会保留放得下的那部分记录再置 TC);客户端库一般会自动回退到 TCP,多一次连接;实例数多的服务不适合只靠一个 SRV 记录集合,要么用 EDNS 提高 UDP 上限,要么拆分
应答超过 512 字节会被截断,要回退 TCP。