原文:https://koraygocmen.com/blog/custom-dns-resolver-for-the-default-http-client-in-go
https://koraygocmen.medium.com/custom-dns-resolver-for-the-default-http-client-in-go-a1420db38a5d
將8.8.8.8:53改成某個不可以的ip, 則會失敗!!
注釋掉這行,就會用系統默認的dns設置。 也是可以訪問網頁“https://www.violetnorth.com” 成功的
__________________________________________________________________________
客戶端發起http請求,基本的經歷過程如下:
域名解析 -> TCP三次握手 -> 建立TCP連接后發起HTTP請求 -> Nginx反向代理 -> 應用層 -> 服務層 -> 緩存/數據庫
一、域名解析
首先Chrome瀏覽器會解析 www.linux178.com 這個域名(准確的叫法應該是主機名)對應的IP地址。怎么解析到對應的IP地址?
① Chrome瀏覽器 會首先搜索瀏覽器自身的DNS緩存(緩存時間比較短,大概只有1分鍾,且只能容納1000條緩存),看自身的緩存中是否有www.linux178.com 對應的條目,而且沒有過期,如果有且沒有過期則解析到此結束。
注:我們怎么查看Chrome自身的緩存?可以使用 chrome://net-internals/#dns 來進行查看
② 如果瀏覽器自身的緩存里面沒有找到對應的條目,那么Chrome會搜索操作系統自身的DNS緩存,如果找到且沒有過期則停止搜索解析到此結束.
注:怎么查看操作系統自身的DNS緩存,以Windows系統為例,可以在命令行下使用 ipconfig /displaydns 來進行查看
③ 如果在Windows系統的DNS緩存也沒有找到,那么嘗試讀取hosts文件(位於C:\Windows\System32\drivers\etc),看看這里面有沒有該域名對應的IP地址,如果有則解析成功。
④ 如果在hosts文件中也沒有找到對應的條目,瀏覽器就會發起一個DNS的系統調用,就會向本地配置的首選DNS服務器(一般是電信運營商提供的,也可以使用像Google提供的DNS服務器)發起域名解析請求(通過的是UDP協議向DNS的53端口發起請求,這個請求是遞歸的請求,也就是運營商的DNS服務器必須得提供給我們該域名的IP地址),運營商的DNS服務器首先查找自身的緩存,找到對應的條目,且沒有過期,則解析成功。如果沒有找到對應的條目,則有運營商的DNS代我們的瀏覽器發起迭代DNS解析請求,它首先是會找根域的DNS的IP地址(這個DNS服務器都內置13台根域的DNS的IP地址),找打根域的DNS地址,就會向其發起請求(請問www.linux178.com這個域名的IP地址是多少啊?),根域發現這是一個頂級域com域的一個域名,於是就告訴運營商的DNS我不知道這個域名的IP地址,但是我知道com域的IP地址,你去找它去,於是運營商的DNS就得到了com域的IP地址,又向com域的IP地址發起了請求(請問www.linux178.com這個域名的IP地址是多少?),com域這台服務器告訴運營商的DNS我不知道www.linux178.com這個域名的IP地址,但是我知道linux178.com這個域的DNS地址,你去找它去,於是運營商的DNS又向linux178.com這個域名的DNS地址(這個一般就是由域名注冊商提供的,像萬網,新網等)發起請求(請問www.linux178.com這個域名的IP地址是多少?),這個時候linux178.com域的DNS服務器一查,誒,果真在我這里,於是就把找到的結果發送給運營商的DNS服務器,這個時候運營商的DNS服務器就拿到了www.linux178.com這個域名對應的IP地址,並返回給Windows系統內核,內核又把結果返回給瀏覽器,終於瀏覽器拿到了www.linux178.com 對應的IP地址,該進行一步的動作了。
注:一般情況下是不會進行以下步驟的
如果經過以上的4個步驟,還沒有解析成功,那么會進行如下步驟(以下是針對Windows操作系統):
⑤ 操作系統就會查找NetBIOS name Cache(NetBIOS名稱緩存,就存在客戶端電腦中的),那這個緩存有什么東西呢?凡是最近一段時間內和我成功通訊的計算機的計算機名和Ip地址,就都會存在這個緩存里面。什么情況下該步能解析成功呢?就是該名稱正好是幾分鍾前和我成功通信過,那么這一步就可以成功解析。
⑥ 如果第⑤步也沒有成功,那會查詢WINS 服務器(是NETBIOS名稱和IP地址對應的服務器)
⑦ 如果第⑥步也沒有查詢成功,那么客戶端就要進行廣播查找
⑧ 如果第⑦步也沒有成功,那么客戶端就讀取LMHOSTS文件(和HOSTS文件同一個目錄下,寫法也一樣)
如果第八步還沒有解析成功,那么就宣告這次解析失敗,那就無法跟目標計算機進行通信。只要這八步中有一步可以解析成功,那就可以成功和目標計算機進行通信。
————————————————————————————————————

package main import ( "context" "io/ioutil" "log" "net" "net/http" "time" ) func main() { var ( dnsResolverIP = "8.8.8.8:53" // Google DNS resolver. dnsResolverProto = "udp" // Protocol to use for the DNS resolver dnsResolverTimeoutMs = 5000 // Timeout (ms) for the DNS resolver (optional) ) dialer := &net.Dialer{ Resolver: &net.Resolver{ PreferGo: true, Dial: func(ctx context.Context, network, address string) (net.Conn, error) { d := net.Dialer{ Timeout: time.Duration(dnsResolverTimeoutMs) * time.Millisecond, } return d.DialContext(ctx, dnsResolverProto, dnsResolverIP) }, }, } dialContext := func(ctx context.Context, network, addr string) (net.Conn, error) { return dialer.DialContext(ctx, network, addr) } http.DefaultTransport.(*http.Transport).DialContext = dialContext httpClient := &http.Client{} // Testing the new HTTP client with the custom DNS resolver. resp, err := httpClient.Get("https://www.violetnorth.com") if err != nil { log.Fatalln(err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatalln(err) } log.Println(string(body)) }