<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Go中的HTTP debug技能 了解一下

          共 8775字,需瀏覽 18分鐘

           ·

          2022-06-21 01:18

          最近把一個(gè)負(fù)責(zé)的HTTP服務(wù)搬上了公司的內(nèi)部的k8s平臺(tái)。由于部分HTTP服務(wù)還未接入k8s平臺(tái),所以內(nèi)部服務(wù)之間的交互主要通過(guò)內(nèi)網(wǎng)域名。說(shuō)到這里,一天下午小土負(fù)責(zé)的A服務(wù)跟下游服務(wù)B的請(qǐng)求一直報(bào)警超時(shí),開始小土使用tcpdump定時(shí)抓包系統(tǒng)采集了一些pcap文件,由于小土對(duì)抓包也不是很在行,HTTP抓包也碰了一鼻子灰。在跟同事交流的同時(shí),想到了httptrace包,下面主要介紹一下httptrace和問(wèn)題的定位過(guò)程。

          HTTP trace的介紹

          Go官方于2016 年 10 月 4 日發(fā)的一篇博文Introducing HTTP Tracing[1]。翻譯見 Go的HTTP tracing。文中主要舉了兩個(gè)案例:

          1.將包含鉤子函數(shù)的 httptrace.ClientTrace 放入request的context中進(jìn)行跟蹤;

          2.對(duì)http.Client進(jìn)行跟蹤,使用 http.RoundTripper wrapper 來(lái)標(biāo)識(shí)當(dāng)前的請(qǐng)求。

          Go1.7 引入了HTTP trace,可以在HTTP客戶端請(qǐng)求過(guò)程中收集一些更細(xì)粒度的信息,httptrace包[2]提供了HTTP trace的支持,收集的信息可用于調(diào)試延遲問(wèn)題,服務(wù)監(jiān)控,編寫自適應(yīng)系統(tǒng)等。httptrace包提供了許多鉤子,在HTTP往返期間收集各種事件的信息,包括連接的創(chuàng)建、復(fù)用、DNS解析查詢、寫入請(qǐng)求和讀取響應(yīng)。

          httptrace包

          httptrace包提供了追蹤HTTP Client請(qǐng)求事件的機(jī)制,主要就是trace.go這個(gè)文件。trace_test.go 是對(duì)trace.go中的函數(shù)的單元測(cè)試。example_test.go是對(duì)httptrace的例子測(cè)試。

          大家可以從這里查看trace.go 源碼[3]

          ├── example_test.go
          ├── trace.go
          └── trace_test.go

          從官網(wǎng)博文例子中也可以看出使用了ClientTrace這個(gè)struct中的一些鉤子函數(shù)。下面羅列了ClientTrace中的函數(shù)列表。

          type ClientTrace struct {
           GetConn              func(hostPort string)                             // 在創(chuàng)建連接之前調(diào)用
           GotConn              func(GotConnInfo)                                 // 連接成功后調(diào)用
           PutIdleConn          func(err error)                                   // 當(dāng)連接用完時(shí)需要放回池子調(diào)用
           GotFirstResponseByte func()                                            // 當(dāng)讀到響應(yīng)的第一個(gè)字節(jié)時(shí)
           Got100Continue       func()                                            // 當(dāng)收到的狀態(tài)碼是"100",則會(huì)調(diào)用Got100Continue繼續(xù)響應(yīng)
           Got1xxResponse       func(code int, header textproto.MIMEHeader) error // 為每個(gè) 1xx 信息響應(yīng)頭調(diào)用,Got1xxResponse在最終的非 1xx 響應(yīng)之前返回。
           DNSStart             func(DNSStartInfo)                                // 當(dāng)DNS查詢開始時(shí)調(diào)用
           DNSDone              func(DNSDoneInfo)                                 // 當(dāng)DNS查詢結(jié)束時(shí)調(diào)用
           ConnectStart         func(network, addr string)                        // 當(dāng)一個(gè)新連接 Dial開始時(shí)
           ConnectDone          func(network, addr string, err error)             // 當(dāng)一個(gè)新的連接的Dial完成時(shí)
           TLSHandshakeStart    func()                                            // TLS 握手開始時(shí)調(diào)用,當(dāng)通過(guò)HTTP代理連接到一個(gè)HTTPS站點(diǎn)時(shí),握手發(fā)生在代理處理完CONNECT請(qǐng)求之后。
           TLSHandshakeDone     func(tls.ConnectionState, error)                  // 在TLS握手后被調(diào)用,其中包括握手成功的連接狀態(tài),或者握手失敗的非零錯(cuò)誤。
           WroteHeaderField     func(key string, value []string)                  // 在傳輸時(shí)write完每個(gè)請(qǐng)求頭后被調(diào)用。
           WroteHeaders         func()                                            // 在傳輸時(shí)write完所有請(qǐng)求頭后調(diào)用的。
           Wait100Continue      func()                                            // 如果請(qǐng)求時(shí)指定了"Expect: 100-continue",且在傳輸過(guò)程中已經(jīng)寫入,但是在寫請(qǐng)求正文之前還在等待 "100 Continue"。
           WroteRequest         func(WroteRequestInfo)                            // 在寫入請(qǐng)求后調(diào)用。在重試請(qǐng)求的情況下,它可以被多次調(diào)用。

          }

          很多同學(xué)也應(yīng)該不是很清楚100狀態(tài)碼的含義,這里科普一下

          100狀態(tài)碼 :HTTP100狀態(tài)碼代表的意思是請(qǐng)繼續(xù)請(qǐng)求,即HTTP 100 Continue 響應(yīng)狀態(tài)。HTTP 100 (Http Status Code 100) 狀態(tài)是HTTP協(xié)議的一種響應(yīng)碼,是我們請(qǐng)求訪問(wèn)網(wǎng)站時(shí),服務(wù)器端返回的1xx 請(qǐng)求信息系列響應(yīng)碼之一。狀態(tài)詳細(xì)說(shuō)明:HTTP 100 表示客戶端應(yīng)當(dāng)繼續(xù)進(jìn)行請(qǐng)求

          引用自HTTP狀態(tài)碼 100 (Continue) 含義詳解[4]。

          問(wèn)題定位破案

          最后是如何定位到問(wèn)題的呢?想必大家看了上面的一系列介紹,也應(yīng)該猜到了。主要在內(nèi)部封裝的httpclient中加入了clienttrace,并在請(qǐng)求結(jié)束后打印了trace的result,通過(guò)重新打包部署到k8s開發(fā)環(huán)境,等待問(wèn)題復(fù)現(xiàn)并查看日志,最后通過(guò)觀察traceresult發(fā)現(xiàn)是域名解析的時(shí)候出現(xiàn)了問(wèn)題,內(nèi)網(wǎng)域名解析道是空的ip信息,便把域名解析的問(wèn)題同步報(bào)告給k8s部署平臺(tái)負(fù)責(zé)的同事。

          下面是工程中的一些代碼實(shí)現(xiàn)。借此機(jī)會(huì)小土也準(zhǔn)備把httptrace工具集成到公共庫(kù)中,并可以通過(guò)apollo平臺(tái)配置httptrace開關(guān)來(lái)開啟debug定位具體問(wèn)題。

          func (c *HTTPClient) Do (request *http.Request,traceId string, debug bool) (*Response, error) {
            // ... define
            if debug {
            clientTrace, traceResult = clientTrace(traceId)
            request = request.WithContext(httptrace.WithClientTrace(request.Context(), clientTrace))
            }
            // ... retry
            res,err := c.client.Do(request)
            if debug {
            data, _ := json.Marshal(traceResult)
            log.Debugf("[%v] trace info: %s", traceId,string(data))
           }
            // ... parse
          }

          func clientTrace(traceId string ) (clientTrace *httptrace.ClientTrace,traceResult *TraceResult) {
           traceResult = &TraceResult{
              TraceId:traceId,
            }

           clientTrace := &httptrace.ClientTrace{
            DNSStart: func(info httptrace.DNSStartInfo) {
             currentTime := time.Now().Local().String()
             traceResult.DNS.Start = currentTime
             traceResult.DNS.Host = info.Host
            },
            DNSDone: func(info httptrace.DNSDoneInfo) {
                currentTime := time.Now().Local().String()
             traceResult.DNS.End = currentTime
             traceResult.DNS.Addrs = info.Addrs
             traceResult.DNS.Err = info.Err
            },
              // ... 省略一些代碼
            }
            
           return clientTrace, traceResult
          }

          小結(jié)

          這里對(duì)httptrace的一些介紹,相信大家也多了一項(xiàng)debug技能,也會(huì)對(duì)http從請(qǐng)求到響應(yīng)有了更深的了解。每一個(gè)問(wèn)題都是發(fā)現(xiàn)未知的機(jī)會(huì),希望gopher們?cè)谟龅絾?wèn)題時(shí)不要害怕,迎難而上,追根到底。

          最后希望這篇短文,能給你帶來(lái)一些幫助。如有問(wèn)題可以下方留言討論。

          參考資料

          [1]

          Introducing HTTP Tracing: https://go.dev/blog/http-tracing

          [2]

          httptrace包: https://pkg.go.dev/net/http/httptrace

          [3]

          trace.go 源碼: https://github1s.com/golang/go/blob/HEAD/src/net/http/httptrace/trace.go

          [4]

          HTTP狀態(tài)碼 100 (Continue) 含義詳解: https://seo.juziseo.com/doc/http_code/100



          推薦閱讀


          福利

          我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號(hào) 「polarisxu」,回復(fù) ebook 獲取;還可以回復(fù)「進(jìn)群」,和數(shù)萬(wàn) Gopher 交流學(xué)習(xí)。

          瀏覽 54
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  狠操综合| 荫蒂高潮大荫蒂毛萌萌 | 亚洲高清无码在线观看 | 欧美成人在线视频观看 | 中日韩特黄A片免费视频 |