1. 程式人生 > >HttpDNS功能說明及實現

HttpDNS功能說明及實現

功能說明

HTTPDNS使用HTTP協議進行域名解析,代替現有基於UDP的DNS協議,域名解析請求直接傳送到阿里雲的HTTPDNS伺服器,從而繞過運營商的Local DNS,能夠避免Local DNS造成的域名劫持問題和排程不精準問題。

功能 說明
防劫持 繞過運營商Local DNS,避免域名劫持,讓每一次訪問都暢通無阻。
精準排程 基於訪問的來源IP,獲得最精準的解析結果,讓客戶端就近接入業務節點。
0ms解析延遲 通過熱點域名預解析、快取DNS解析結果、解析結果懶更新策略等方式實現0解析延遲
快速生效 避免Local DNS不遵循權威TTL,解析結果長時間無法更新的問題
降低解析失敗率 有效降低無線場景下解析失敗的比率

防劫持

HTTPDNS代替了傳統的LocalDNS完成遞迴解析的功能,基於HTTP協議的設計可以適用於幾乎所有的網路環境,同時保留了鑑權、HTTPS等更高安全性的擴充套件能力,避免惡意攻擊劫持行為。

精準排程

傳統域名解析的排程精準性問題,本質根源在於Local DNS的部署和分配機制上。由於碎片化的管理方式,這些環節的服務質量同樣很難得到保障。HTTPDNS在遞迴解析實現上優化了與權威DNS的互動,通過<a name="https://datatracker.ietf.org/doc/rfc7871">edns-client-subnet</a>協議將終端使用者的IP資訊直接交付給權威DNS,這樣權威DNS就可以忽略Local DNS IP資訊,根據終端使用者的IP資訊進行精準排程,避免Local DNS的座標干擾

DNS解析0延遲:

  • 構建客戶端DNS快取;

      通過合理的DNS快取,我們確保每次網路互動的DNS解析都是從記憶體中獲取IP資訊,從而大幅降低DNS解析開銷。根據業務的不同,我們可以
      制訂更豐富的快取策略,如根據運營商快取,可以在網路切換的場景下複用已快取的不同運營商線路的域名IP資訊,避免網路切換後進行鏈
      路重選擇引入的DNS網路解析開銷。另外,我們還可以引入IP本地化離線儲存,在客戶端重啟時快速從本地讀取域名IP資訊,大幅提升首頁
      載入效率。
    
  • 熱點域名預解析;

      在客戶端啟動過程中,我們可以通過熱點域名的預解析完成熱點域名的快取載入。當真正的業務請求發生時,直接由記憶體中讀取目標域名的IP
      資訊,避免傳統DNS的網路開銷。
    
  • 懶更新策略;

      絕大多數場景下業務域名的IP資訊變更並不頻繁,特別是在單次APP的使用週期內,域名解析獲取的IP往往是相同的(特殊業務場景除外)。
      因此我們可以利用DNS懶更新策略來實現TTL過期後的DNS快速解析。所謂DNS懶更新策略即客戶端不主動探測域名對應IP的TTL時間,當業務
      請求需要訪問某個業務域名時,查詢記憶體快取並返回該業務域名對應的IP解析結果。如果IP解析結果的TTL已過期,則在後臺進行非同步DNS網
      絡解析與快取結果更新。通過上述策略,使用者的所有DNS解析都在與記憶體互動,避免了網路互動引入的延遲。
    

實現方案

服務端:

服務端提供API介面,app端直接通過ip地址訪問,ip地址可以有多個

請求方式:HTTP GET

URL引數說明:

名稱 是否必須 描述
host 必須 要解析的域名
ip 可選 使用者的來源IP,如果沒指定這個引數,預設使用請求連線的源IP

請求示例:

考慮到服務IP防攻擊之類的安全風險,為保障服務可用性,HTTPDNS同時提供多個服務IP,當某個服務IP在異常情況下不可用時,可以使用其它服務IP進行重試。

請求成功時,HTTP響應的狀態碼為200,響應結果用JSON格式表示,示例如下:

{
  "host": "www.suning.com",
  "ips": [
    "112.84.104.48"
  ],
  "ttl": 57,
  "origin_ttl": 120
}

請求失敗的響應示例:

{
  "code": "MissingArgument"
}

錯誤碼列表如下:

錯誤碼 HTTP狀態碼 描述
MissingArgument 400 缺少必要引數
InvalidHost 400 域名格式不合法
MethodNotAllowed 405 不支援的HTTP方法
InternalError 500 服務端內部錯誤

錯誤處理

異常下的出錯相容邏輯,主要包括非同步請求,重試,降級

非同步請求

訪問HTTPDNS服務時,應該使用非同步請求的策略,避免解析延遲太大而對業務造成影響,特別是在網路環境異常或HTTPDNS服務IP異常不可
用時,如果用同步訪問,需要等待網路超時後才會返回解析失敗,這個超時時間較大,可能對業務的使用體驗造成很大影響。

非同步請求策略:解析域名時,如果當前快取中有TTL未過期的IP,可直接使用;如果沒有,則立刻讓此次請求降級走原生LocalDNS解析,同
時另起執行緒非同步地發起HTTPDNS請求進行解析,更新快取,這樣後續解析域名時就能命中快取。

重試

訪問HTTPDNS服務解析域名時,如果請求HTTPDNS服務端失敗,即HTTP請求沒有返回,可以進行重試。

大部分情況下,這種訪問失敗是由於網路原因引起的,重試可以解決。

降級

不管是因為什麼原因,當通過HTTPDNS服務無法獲得域名對應的IP時,都必須降級:使用標準的DNS解析,通過Local DNS去解析域名。

Android端:

OkHttp預設使用系統DNS服務InetAddress進行域名解析,但同時也暴露了自定義DNS服務的介面,通過該介面我們可以優雅地使用HttpDns。

  • 自定義DNS介面

OkHttp暴露了一個Dns介面,通過實現該介面,我們可以自定義Dns服務:

public class OkHttpDns implements Dns {
    private static final Dns SYSTEM = Dns.SYSTEM;
    HttpDnsService httpdns;//httpdns 解析服務
    private static OkHttpDns instance = null;
    private OkHttpDns(Context context) {
        this.httpdns = HttpDns.getService(context, "account id");
    }
    public static OkHttpDns getInstance(Context context) {
        if(instance == null) {
            instance = new OkHttpDns(context);
        }
        return instance;
    }
    @Override
    public List<InetAddress> lookup(String hostname) throws UnknownHostException {
        //通過非同步解析介面獲取ip
        String ip = httpdns.getIpByHostAsync(hostname);
        if(ip != null) {
            //如果ip不為null,直接使用該ip進行網路請求
            List<InetAddress> inetAddresses = Arrays.asList(InetAddress.getAllByName(ip));
            Log.e("OkHttpDns", "inetAddresses:" + inetAddresses);
            return inetAddresses;
        }
        //如果返回null,走系統DNS服務解析域名
        return Dns.SYSTEM.lookup(hostname);
    }
}
  • 建立OkHttpClient

建立OkHttpClient物件,傳入OkHttpDns物件代替預設Dns服務:

private void okhttpDnsRequest() {
    OkHttpClient client = new OkHttpClient.Builder()
    .dns(OkHttpDns.getInstance(getApplicationContext()))
    .build();
    Request request = new Request.Builder()
    .url("http://www.aliyun.com")
    .build();
    Response response = null;
    client.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            e.printStackTrace();
        }
        @Override
        public void onResponse(Call call, Response response) throws IOException {
            if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
                DataInputStream dis = new DataInputStream(response.body().byteStream());
                int len;
                byte[] buff = new byte[4096];
                StringBuilder result = new StringBuilder();
                while ((len = dis.read(buff)) != -1) {
                    result.append(new String(buff, 0, len));
                }
                Log.d("OkHttpDns", "Response: " + result.toString());
            }
        });
}
  • 總結

相比於通用方案,OkHttp+HttpDns有以下兩個主要優勢:

實現簡單,只需通過實現Dns介面即可接入HttpDns服務

通用性強,該方案在HTTPS,SNI以及設定Cookie等場景均適用。規避了證書校驗,域名檢查等環節

IOS端:

基於NSURLProtocol可攔截iOS系統上基於上層網路庫NSURLConnection/NSURLSession發出的網路請求;

通過以下介面註冊自定義NSURLProtocol,用於攔截上層網路請求,並建立新的網路請求接管資料傳送、接收、重定向等處理邏輯,將結果反饋給原始請求。

[NSURLProtocol registerClass:[CustomProtocol class]];

自定義NSURLProtocol處理過程概述:

  • 在canInitWithRequest中過濾要需要做HTTPDNS域名解析的請求;
  • 請求攔截後,做HTTPDNS域名解析;
  • 解析完成後,同普通請求一樣,替換URL.host欄位,替換HTTP Header Host域,並接管該請求的資料傳送、接收、重定向等處理;



作者:剛哥說
連結:https://www.jianshu.com/p/15ff2aeb5b5b
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。