905 words
5 minutes
Guava InternetDomainName 域名解析原理

Guava 是什么#

Guava 是 Google 开源的 Java 核心工具库,提供了一系列 JDK 没有但开发中常用的功能:

  • 集合增强ImmutableListMultisetBiMapTable
  • 缓存CacheBuilder 本地缓存(Caffeine 是它的现代替代)
  • 字符串处理SplitterJoinerCharMatcher
  • 前置条件Preconditions.checkNotNull()checkArgument()
  • I/OFilesByteStreamsCharStreams 简化流操作
  • 并发ListenableFuture(在 CompletableFuture 出现之前是 Java 并发的主流方案)
  • 事件总线EventBus 进程内发布/订阅

其中 InternetDomainName 是一个容易被忽视但设计精巧的类,专门用于域名的结构化解析。


InternetDomainName 的核心原理#

InternetDomainName 的核心原理是基于 Mozilla Public Suffix List (PSL) 来解析域名的层级结构。

为什么需要 PSL#

域名有层级,但光看 . 的数量无法判断”注册级别”在哪里:

  • example.com → 注册域是 example.com(1 级 + 公共后缀 com
  • example.co.uk → 注册域是 example.co.uk(1 级 + 公共后缀 co.uk,不是 uk
  • example.github.io → 公共后缀是 github.io,注册域是 example.github.io

所以 Guava 把 Mozilla 维护的 PSL(~9000 条规则)编译进了 JAR,用查表法判断。

PSL 规则匹配算法#

从右往左匹配,规则有三种类型:

规则类型示例含义
普通规则comcom 是公共后缀
通配规则*.ckxxx.ck 都是公共后缀
例外规则!www.ckwww.ck 不是公共后缀(覆盖通配)

匹配时最长规则优先:先匹配例外,再匹配通配,最后匹配普通。

关键概念#

blog.example.co.uk
─────┬─────┬──┬──
│ │ └─ public suffix: co.uk
│ └─ top private domain: example.co.uk ← 你能注册的
└─ subdomain
  • publicSuffix() — 查 PSL 得到公共后缀(co.uk
  • topPrivateDomain() — 公共后缀 + 往左一级 = 注册域(example.co.uk
  • isUnderPublicSuffix() — 是否在某个公共后缀之下
  • hasPublicSuffix() — 该域名本身是否包含已知的公共后缀

源码级流程#

InternetDomainName.from("blog.example.co.uk")
① 校验格式(小写化、ASCII、标签长度 1-63、总长 ≤253)
② 按 "." 拆成 labels: ["blog", "example", "co", "uk"]
③ 从右往左拼接,逐级查 PSL Trie 树
④ 找到匹配的 public suffix → "co.uk"
⑤ topPrivateDomain = public suffix 左边第一个 label + suffix → "example.co.uk"

PSL 在 Guava 里被预处理成一个紧凑的 Trie(前缀树) 结构(TrieParser 生成的字节码),查找是 O(n)(n = 标签数),非常快。


为什么 InternetDomainName 不接受 URL#

一个自然的疑问:既然都是从右往左匹配,为什么不直接传 URL 进去?

因为”从右往左匹配 PSL”和”从 URL 里提取域名”是两个完全不同的问题。

从 URL 提取域名并不简单#

http://user:pass@example.co.uk:8080/path?q=1
│ │ │
auth host port

URL 中包含认证信息、端口号、IPv6 地址、路径、查询参数等,如果 InternetDomainName.from() 内部默默做这些提取,调用者反而不知道发生了什么。

三个设计理由#

1. 单一职责 — PSL 匹配是域名层的事,URL 解析是网络层的事。混在一起违反正交性。

2. 避免隐式行为 — 如果传入 http://evil.com@safe.com,你是取 evil.com 还是 safe.com?静默提取会藏 bug。直接报错,用户就知道用错了。

3. 调用者已有工具

// Java 标准库已经能提取 host
String host = new URL(input).getHost();
InternetDomainName.from(host); // 清晰、两步各管各的

“反正也是从右往左匹配”——那只是 PSL Trie 查找的方向,跟输入格式无关。拿到干净的域名标签序列之前,URL 的解析工作跟 PSL 没有关系。


一句话总结#

InternetDomainName 的本质:用 Mozilla 的公共后缀列表建 Trie,从右往左匹配,确定域名的”注册边界”在哪里。不接受 URL 不是技术限制,是 API 设计原则——不替调用者做隐式转换,强制显式分离关注点。

Guava InternetDomainName 域名解析原理
https://sgjki547.top/posts/2026-06-09-guava-internetdomainname域名解析原理/
Author
SGJki
Published at
2026-06-09
License
CC BY-NC-SA 4.0