前言
本章節介紹HTTPDNS iOS SDK的接入方法。
推薦工程使用cocoapods管理依賴。
當前SDK最新版本支援
iOS Deployment Target 10.0及以上。當前SDK打包方式為靜態庫。
支援模擬器
x86_64、arm64架構以及真機arm64架構。
第一步:將SDK添加到您的應用
1 指定Master倉庫和阿里雲倉庫
HTTPDNS iOS SDK和其他EMAS產品的iOS SDK,都是發布到阿里雲EMAS官方維護的github倉庫中,因此,您需要在您的Podfile檔案中包含該倉庫地址。
source 'https://github.com/CocoaPods/Specs.git'
source 'https://github.com/aliyun/aliyun-specs.git'2 添加依賴
為您需要依賴HTTPDNS iOS SDK的target添加如下依賴。
use_framework!
pod 'AlicloudHTTPDNS', 'x.x.x'樣本依賴中的SDK版本號碼請以發布說明文檔中的最新版本號碼為準。
3 安裝依賴
在您的Terminal中進入Podfile所在目錄,執行以下命令安裝依賴。
pod install --repo-update安裝完成後,注意使用.xcworkspace檔案重新開啟工程。
第二步:使用SDK
1. 引入標頭檔
在需要使用HTTPDNS的代碼檔案中引入標頭檔。
#import <AlicloudHttpDNS/AlicloudHttpDNS.h>import AlicloudHttpDNS2. 構造HTTPDNS執行個體並進行配置
建議在-[AppDelegate application:didFinishLaunchingWithOptions:]方法中構造HTTPDNS全域執行個體,並進行相關配置。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// 使用阿里雲HTTPDNS控制台分配的AccountId構造全域執行個體
// 全域只需要初始化一次,secretKey需要到控制台獲得鑒權密鑰並在初始化時進行配置
HttpDnsService *httpdns = [[HttpDnsService alloc] initWithAccountID:<Your AccountId> secretKey:@"<Your SecretKey>"];
// 開啟日誌,調試排查問題時使用
[httpdns setLogEnabled:NO];
// 設定httpdns網域名稱解析網路請求是否需要走HTTPS方式
[httpdns setHTTPSRequestEnabled:YES];
// 設定啟動節點,請根據使用情境選擇合適的啟動節點
[httpdns setRegion:ALICLOUD_HTTPDNS_SINGAPORE_REGION_KEY];
// 設定開啟持久化緩衝,使得APP啟動後可以複用上次活躍時緩衝在本地的IP,提高啟動後擷取網域名稱解析結果的速度
[httpdns setPersistentCacheIPEnabled:YES];
// 設定允許使用已經到期的IP,當網域名稱的IP配置比較穩定時可以使用,提高解析效率
[httpdns setReuseExpiredIPEnabled:YES];
// 設定底層HTTPDNS網路請求逾時時間,單位為秒
[httpdns setNetworkingTimeoutInterval:2];
return YES;
}func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// 使用阿里雲HTTPDNS控制台分配的AccountId構造全域執行個體
// 全域只需要初始化一次,secretKey需要到控制台獲得鑒權密鑰並在初始化時進行配置
let httpdns = HttpDnsService(accountID: <Your AccountId>, secretKey: "<Your SecretKey>")
// 開啟日誌,調試排查問題時使用
httpdns.setLogEnabled(false)
// 設定httpdns網域名稱解析網路請求是否需要走HTTPS方式
httpdns.setHTTPSRequestEnabled(true)
// 設定啟動服務節點,請根據使用情境選擇合適的啟動節點
httpdns.setRegion(ALICLOUD_HTTPDNS_SINGAPORE_REGION_KEY)
// 設定開啟持久化緩衝,使得APP啟動後可以複用上次活躍時緩衝在本地的IP,提高啟動後擷取網域名稱解析結果的速度
httpdns.setPersistentCacheIPEnabled(true)
// 設定允許使用已經到期的IP,當網域名稱的IP配置比較穩定時可以使用,提高解析效率
httpdns.setReuseExpiredIPEnabled(true)
// 設定是否支援IPv6位址解析,只有開啟這個開關,解析介面才有能力解析網域名稱的IPv6地址並返回
httpdns.setIPv6Enabled(true)
return true
}通過開啟
setPersistentCacheIPEnabled:YES與setReuseExpiredIPEnabled:YES可以實現 樂觀 DNS 緩衝,使解析結果持久化儲存並允許使用到期 IP,從而使大部分 DNS 解析可直接在本地完成,提升首屏載入和請求效能。對於解析結果 IP 不頻繁變更的業務網域名稱,建議開啟此功能。具體用法可參考是否啟用持久化緩衝與 是否允許使用到期IP介面說明。setHTTPSRequestEnabled參數設定為true後,計費會增加,請仔細閱讀產品計費文檔。
如果您對網域名稱資訊或SDNS參數有更高的安全訴求,可以通過初始化介面設定aesSecretKey 來啟用加密功能。使用內容加密後計費會增加, 請仔細閱讀產品計費文檔。
使用預解析介面啟用網域名稱預解析,可顯著提升樂觀 DNS 緩衝的命中率,使大部分解析請求直接從本機快取返回,減少即時解析帶來的時延。另一方面,預解析會增加解析請求量,建議僅對核心業務網域名稱 / 高頻訪問網域名稱開啟,以獲得最佳效能與成本平衡。
setRegion 預設使用中國內地服務節點,如果APP需要在海外環境使用HTTPDNS,為了提升解析效率,可以主動設定本SDK的啟動服務節點。具體配置方法,請查看設定region節點。
3. 擷取服務執行個體
HTTPDNS iOS SDK以全域service執行個體的方式提供網域名稱解析服務,您可以通過以下方式擷取執行個體。
HttpDnsService *httpdns = [HttpDnsService sharedInstance];let httpdns = HttpDnsService.sharedInstance()4. 進行網域名稱解析
HTTPDNS提供了多種網域名稱解析方式,包括預解析/同步解析/非同步解析/同步非阻塞解析。下面以同步非阻塞解析介面作為例子。
HttpDnsService *httpdns = [HttpDnsService sharedInstance];
HttpdnsResult *result = [httpdns resolveHostSyncNonBlocking:@"www.aliyun.com" byIpType:HttpdnsQueryIPTypeAuto];
if (result) {
// 使用網域名稱解析結果
} else {
// 同步非阻塞介面,為了最快的解析速度,若緩衝中無有效解析結果,會立即返回空值,同時在後台發起新的解析請求
// 因此,要做好走LocalDNS解析,或者仍然直接給網路程式庫傳完整網域名稱的方式降級
// 可以使用強同步介面、或者回調形式的介面確保獲得HTTPDNS解析的結果
}let httpdns = HttpDnsService.sharedInstance()
if let result = httpdns.resolveHostSyncNonBlocking("www.aliyun.com", by: HttpdnsQueryIPType.auto) {
// 使用網域名稱解析結果
} else {
// 同步非阻塞介面,為了最快的解析速度,若緩衝中無有效解析結果,會立即返回空值,同時在後台發起新的解析請求
// 因此,要做好走LocalDNS解析,或者仍然直接給網路程式庫傳完整網域名稱的方式降級
// 可以使用強同步介面、或者回調形式的介面確保獲得HTTPDNS解析的結果
}請根據您的實際使用情境選擇合適的網域名稱解析介面。
如果返回的result一直為
nil,請檢查是否已經在阿里雲HTTPDNS控制台上添加該網域名稱。為了網路異常情況導致返回結果為
nil時不影響商務程序,建議降級到LocalDNS解析作為兜底邏輯。
5. 使用網域名稱解析結果
不同情況下,網域名稱解析結果可能包含多種情況。
空結果,如在使用同步非阻塞介面,或者網路異常時。
只有IPv4的地址,在本網環境為IPv4單棧且指定包含IPv4的請求類型,或網域名稱只配置了IPv4的地址。
只有IPv6的地址,在啟用IPv6且指定解析IPv6的地址時。考慮到當前IPv6的推廣程度,這種情況一般不會發生。
同時擁有IPv4、IPv6的地址,在啟用IPv6且指定瞭解析雙棧地址,或指定了自動判斷網路類型且是雙棧環境下,同時網域名稱也配置了IPv4、IPv6地址的情況下。
本樣本中,配置開啟了IPv6解析,且請求IP類型設定為Both,若網域名稱同時配置了IPv4、IPv6地址,則解析結果也會同時包含。因此,若需要優先選擇IPv4地址,則可以按如下代碼處理解析結果。
HttpDnsService *httpdns = [HttpDnsService sharedInstance];
HttpdnsResult *result = [httpdns resolveHostSyncNonBlocking:@"www.aliyun.com" byIpType:HttpdnsQueryIPTypeAuto];
if (!result) {
// 無有效ip,走兜底邏輯
}
if (result.hasIpv4Address) {
NSString *ip = result.firstIpv4Address;
// 使用ip
NSArray<NSString *> *ips = result.ips;
// 使用ip列表
} else if (result.hasIpv6Address) {
NSString *ip = result.firstIpv6Address;
// 使用ip
NSArray<NSString *> *ips = result.ipv6s;
// 使用ip列表
} else {
// 無有效ip,走兜底邏輯
}let httpdns = HttpDnsService.sharedInstance()
if let result = httpdns.resolveHostSyncNonBlocking("www.aliyun.com", by: HttpdnsQueryIPType.auto) {
if (result.hasIpv4Address()) {
let ip = result.firstIpv4Address()
// 使用ip
let ipList = result.ips
// 使用ip列表
} else if (result.hasIpv6Address()) {
let ip = result.firstIpv6Address()
// 使用ip
let ipList = result.ipv6s
// 使用ip列表
} else {
// 無有效ip,走兜底邏輯
}
} else {
// 無有效ip,走兜底邏輯
}範例代碼
HTTPDNS iOS SDK接入工程範例參見HTTPDNS iOS Demo。
注意事項
務必編寫降級代碼
降級代碼指的是HTTPDNS未擷取到期望結果時的處理代碼。通常您可以降級到使用LocalDNS進行解析,即,為網路程式庫傳入原始網域名稱,讓網路程式庫自行走本地LocalDNS解析。
記錄從HTTPDNS擷取的IP及sessinId
我們提供了用於解析問題排查的解決方案,需要您將從HTTPDNS擷取的IP及sessionId記錄到日誌中,詳情請參考如何使用“會話追蹤方案”排查解析異常。
設定HTTP要求標頭HOST欄位
標準的HTTP協議中服務端會將HTTP要求標頭HOST欄位的值作為請求的網域名稱資訊進行解析。使用HTTPDNS後,您可能需要將HTTP請求URL中的HOST欄位替換為HTTPDNS解析獲得的IP,這時標準的網路程式庫會將您的IP賦值給HTTP要求標頭的HOST欄位,進而導致服務端的解析異常(服務端認可的是您的網域名稱資訊,而非IP資訊)。
為瞭解決這個問題,您可以主動設定HTTP請求HOST欄位的值,如以下這個簡單樣本:
- (void)sampleRequestUsingHttpdns { HttpDnsService *httpdns = [HttpDnsService sharedInstance]; NSString *originalUrlStr = @"http://www.aliyun.com/"; NSURL* url = [NSURL URLWithString:originalUrlStr]; // 同步介面擷取IP HttpdnsResult* result = [httpdns resolveHostSyncNonBlocking:url.host byIpType:HttpdnsQueryIPTypeAuto]; NSLog(@"resolve result: %@", result); NSString *validIp = nil; if (result) { if (result.hasIpv4Address) { validIp = result.firstIpv4Address; } } NSMutableURLRequest *request; if (validIp) { // 通過HTTPDNS擷取IP成功,進行URL替換和HOST頭設定 NSRange hostFirstRange = [originalUrlStr rangeOfString:url.host]; NSString* newUrl = [originalUrlStr stringByReplacingCharactersInRange:hostFirstRange withString:validIp]; request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:newUrl]]; // 佈建要求HOST欄位 [request setValue:url.host forHTTPHeaderField:@"host"]; } else { // 本處示範如何做好降級處理 // 通過HTTPDNS無法擷取IP,直接使用原有的URL進行網路請求 request = [[NSMutableURLRequest alloc] initWithURL:url]; } // 發送請求 NSURLSession *session = [NSURLSession sharedSession]; NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { if (error) { NSLog(@"error: %@", error); } else { NSLog(@"response: %@", response); } }]; [task resume]; }func sampleRequestUsingHttpdns() { let httpdns = HttpDnsService.sharedInstance() let originalUrlStr = "http://www.aliyun.com/" let url = URL(string: originalUrlStr)! // 同步介面擷取IP let result = httpdns.resolveHostSyncNonBlocking(url.host!, by: HttpdnsQueryIPType.auto) print("resolve result: \(result?.description ?? "")") var validIp: String? if let result = result { if result.hasIpv4Address() { validIp = result.firstIpv4Address() } } var request: URLRequest if let validIp = validIp { // 通過HTTPDNS擷取IP成功,進行URL替換和HOST頭設定 let hostFirstRange = originalUrlStr.firstRange(of: url.host!)! let newUrl = originalUrlStr.replacingCharacters(in: hostFirstRange, with: validIp) request = URLRequest(url: URL(string: newUrl)!) // 佈建要求HOST欄位 request.setValue(url.host!, forHTTPHeaderField: "host") } else { // 本處示範如何做好降級處理 // 通過HTTPDNS無法擷取IP,直接使用原有的URL進行網路請求 request = URLRequest(url: url) } // 發送請求 let session = URLSession.shared let task = session.dataTask(with: request) { (data, response, error) in if let error = error { print("error: \(error)") } else { print("response: \(response?.description ?? "")") } } task.resume() }重要這個簡單樣本中,也要注意這些細節:
該樣本僅展示了一定解析成功的情況,未考慮兜底邏輯。
簡單起見,該樣本請求的是HTTP的地址,因此要在
plist.info中配置NSAllowsArbitraryLoads才能訪問。如果請求地址為HTTPS類型,則需要參考後文的HTTPS情境指導。
Cookie欄位
部分網路程式庫支援Cookie的自動儲存管理,當您使用HTTPDNS進行IP URL請求時,部分網路程式庫會將您URL中的IP資訊作為Cookie對應的網域名稱資訊進行儲存管理(而非HTTP要求標頭HOST欄位資訊),進而造成Cookie管理與使用上的困擾,因此您需要關閉Cookie的自動管理功能(預設關閉)。
HTTPS/WebView/SNI情境
HTTPS情境,請參考iOS端Native情境使用HTTPDNS。
代理情況下的使用
當存在中間HTTP代理時,用戶端發起的請求中請求行會使用絕對路徑的URL,在您開啟HTTPDNS並採用IP URL進行訪問時,中間代理將識別您的IP資訊並將其作為真實訪問的HOST資訊傳遞給目標伺服器,這時目標伺服器將無法處理這類無真實HOST資訊的HTTP請求。移動網關提供了
X-Online-Host的私人協議欄位來解決這個問題,比如:目標 URL:http://www.aliyun.com/product/oss/ 通過 HTTPDNS 解析出來的www.aliyun.com的IP:X.X.X.X 代理:10.0.0.172:80 您的HTTP要求標頭: GET http://X.X.X.X/product/oss/ HTTP/1.1 # 通過代理髮起的HTTP要求標頭,請求行是一個絕對路徑 Host: www.aliyun.com # 這個Header會被代理網關忽略,代理網關會使用請求行絕對路徑中的host欄位作為來源站點的host,即1.1.X.X X-Online-Host: www.aliyun.com # 這個Header就是移動網關為了傳遞真實Host添加的私人頭部,來源站點需要配置識別該私人頭部以擷取真實的Host資訊同樣您可以通過下述方法進行
X-Online-Host要求標頭域的設定,並在服務端設定對該私人頭域的解析。[request setValue:url.host forHTTPHeaderField:@"X-Online-Host"];說明在絕大多數情境下,我們建議您檢測當前裝置是否開啟了網路代理程式,然後在代理模式下不使用HTTPDNS進行網域名稱解析。