1. 异常概述

在 Java 网络编程中,UnknownHostException 是一个常见但不容忽视的异常。本文将通过实际示例解析其触发原因,并提供预防与处理的最佳实践。

该异常通常出现在应用尝试通过主机名建立网络连接时,却无法解析出对应的 IP 地址。对于后端服务、微服务调用或第三方 API 集成场景,这类问题可能导致服务启动失败或运行时请求中断——属于典型的“部署后踩坑”问题。

2. 异常触发场景

UnknownHostException 表示 无法解析指定主机名对应的 IP 地址

最常见的两种情况:

  • 主机名拼写错误
  • DNS 配置问题或传播延迟

示例:拼写错误导致异常

String hostname = "http://locaihost";  // 注意:locaihost 是 locahost 的拼写错误
URL url = new URL(hostname);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.getResponseCode(); // ❌ 抛出 UnknownHostException

⚠️ 上述代码中,locaihost 并非合法主机名,DNS 无法解析,JVM 直接抛出异常。

小贴士:http:// 不应包含在主机名中。正确写法应为 "locaihost" 或完整使用 new URL("http", "locaihost", "/")。此处错误叠加,加剧了问题隐蔽性。

DNS 相关问题

另一种隐蔽原因:DNS 未生效或配置错误

比如你刚注册了一个新域名 api.myapp.com,并更新了 DNS 记录,但本地或服务器所在网络尚未同步。DNS 全球传播可能需要 最长 48 小时,期间部分节点仍无法解析。

此外,企业内网常使用私有 DNS,若配置不当(如错误的 DNS 服务器地址),也会导致本该能访问的服务报 UnknownHostException

3. 如何预防?

预防优于补救。以下是几个简单粗暴但有效的预防措施:

仔细核对主机名

  • 检查拼写(如 localhost vs locahsoct
  • 去除前后空格:使用 .trim()
  • 避免将协议(http://)混入主机名字段

验证 DNS 可达性

  • 使用 nslookupdig 命令测试解析:
    nslookup api.myapp.com
    
  • 确保服务器使用的 DNS 服务正常(如 8.8.8.8 或内网 DNS)
  • 新域名上线后预留缓冲时间,避免立即强依赖

配置合理的超时与重试机制

HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setConnectTimeout(5000);  // 5秒连接超时
con.setReadTimeout(10000);    // 10秒读取超时

避免因 DNS 暂时不可达导致线程长时间阻塞。

4. 异常处理策略

UnknownHostException 继承自 IOException,属于 受检异常(checked exception),必须显式处理。

基础处理方式

try {
    con.getResponseCode();
} catch (UnknownHostException e) {
    System.err.println("无法解析主机: " + e.getMessage());
    con.disconnect(); // ✅ 断开连接,防止资源泄漏
} catch (IOException e) {
    System.err.println("IO 错误: " + e.getMessage());
}

⚠️ **关键点:发生 UnknownHostException 后务必调用 disconnect()**。否则连接可能保持打开状态,累积导致连接池耗尽或内存泄漏。

生产环境建议

在微服务或高并发场景下,建议结合以下策略:

  • 日志记录 + 告警:记录失败主机名、时间、调用上下文
  • 自动重试(带退避):短暂 DNS 故障可通过指数退避重试恢复
  • 降级机制:如调用第三方支付接口失败,可切换备用域名或进入离线模式
  • 使用连接池(如 HikariCP 配合 HTTP 客户端):限制并发连接数,防雪崩

使用现代 HTTP 客户端(推荐)

避免直接使用 HttpURLConnection,推荐采用 OkHttpApache HttpClient,它们对 DNS 异常有更好的封装和控制能力。

示例(OkHttp):

OkHttpClient client = new OkHttpClient.Builder()
    .connectTimeout(5, TimeUnit.SECONDS)
    .dns(hostname -> {
        try {
            return Dns.SYSTEM.lookup(hostname);
        } catch (UnknownHostException e) {
            throw new RuntimeException("DNS解析失败: " + hostname, e);
        }
    })
    .build();

你甚至可以自定义 DNS 实现,支持缓存或多 DNS 源 fallback。

5. 总结

UnknownHostException 虽然基础,但在实际项目中极易因配置疏忽引发线上故障。核心要点:

  • ✅ 拼写检查 + DNS 验证是第一道防线
  • ✅ 必须捕获并妥善处理该异常,避免资源泄漏
  • ✅ 结合超时、重试、降级提升系统容错能力
  • ✅ 优先使用成熟的 HTTP 客户端库替代原生 HttpURLConnection

所有示例代码已整理至 GitHub:https://github.com/baeldung/core-java-modules/tree/master/core-java-exceptions-2


原始标题:java.net.UnknownHostException: Invalid Hostname for Server | Baeldung