Java端HTTPDNS+OkHttp最佳实践

更新时间:
复制为 MD 格式

本文档介绍在 Java 应用中使用 OkHttp 接入 HTTPDNS 的方案。

1. 背景说明

如果您在 Java 端使用的网络框架是 OkHttp,通过调用 OkHttp 提供的自定义 DNS 服务接口,可以简洁优雅地使用 HTTPDNS 的服务。

OkHttp 是一个处理网络请求的开源项目,是 Java/Android 端最火热的轻量级框架,由移动支付 Square 公司贡献。随着 OkHttp 的不断成熟,越来越多的开发者使用 OkHttp 作为网络框架。

OkHttp 默认使用系统 DNS 服务 InetAddress 进行域名解析,同时也暴露了自定义 DNS 服务的接口,通过该接口我们可以优雅地使用 HTTPDNS。

2. 实现自定义 DNS 接口

OkHttp 暴露了 Dns 接口,通过实现该接口可以自定义 DNS 服务:

import com.alibaba.sdk.java.httpdns.HTTPDNSResult;
import com.alibaba.sdk.java.httpdns.HttpDnsClient;
import com.alibaba.sdk.java.httpdns.RequestIpType;
import okhttp3.Dns;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

public class OkHttpDns implements Dns {
    
    private final HttpDnsClient httpDnsClient;
    
    public OkHttpDns(HttpDnsClient httpDnsClient) {
        this.httpDnsClient = httpDnsClient;
    }
    
    @Override
    public List<InetAddress> lookup(String hostname) throws UnknownHostException {
        HTTPDNSResult result = httpDnsClient.getHttpDnsResultForHostSyncNonBlocking(
            hostname, 
            RequestIpType.both
        );
        
        List<InetAddress> inetAddresses = new ArrayList<>();
        
        try {
            if (result != null && result.getIps() != null) {
                for (String ip : result.getIps()) {
                    inetAddresses.add(InetAddress.getByName(ip));
                }
            }
            if (result != null && result.getIpv6s() != null) {
                for (String ip : result.getIpv6s()) {
                    inetAddresses.add(InetAddress.getByName(ip));
                }
            }
        } catch (UnknownHostException e) {
            // 忽略单个 IP 解析失败
        }
        
        if (!inetAddresses.isEmpty()) {
            return inetAddresses;
        }
        
        // 降级到系统 DNS
        return Dns.SYSTEM.lookup(hostname);
    }
}

由于 OkHttp 内部有重试机制,重试时可能会选择不同的 IP 建立连接,所以在 lookup() 方法中返回包含多个 IP 的列表,可以有效避免单一 IP 不可用导致请求失败的情况。

3. 配置 OkHttpClient

创建 OkHttpClient 对象,传入自定义 DNS 服务:

HttpDnsClient httpDnsClient = HttpDnsClient.getClient(accountId);

OkHttpClient okHttpClient = new OkHttpClient.Builder()
    .dns(new OkHttpDns(httpDnsClient))
    .build();

4. 总结

OkHttp + HTTPDNS 的主要优势:

  • 实现简单:只需实现 Dns 接口即可接入

  • 通用性强:在 HTTPS、SNI 以及设置 Cookie 等场景均适用,无需额外处理证书校验、域名检查等环节

  • 自动重试:返回多个 IP 时,OkHttp 会自动尝试不同的 IP 建立连接