本文檔介紹在 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 建立串連