Golang http.RoundTripper 筆記

RoundTripper is an interface representing the ability to execute a single HTTP transaction, obtaining the Response for a given Request.

對於http客戶端,能夠使用不一樣的實現了 RoundTripper 接口的Transport實現來配置它的行爲git

RoundTripper 有點像 http.Client 的中間件github

接口定義golang

type RoundTripper interface { 
       RoundTrip(*Request) (*Response, error)
}

須要實現RoundTrip函數api

type SomeClient struct {}

func (s *SomeClient) RoundTrip(r *http.Request)(*Response, error) {
    //Something comes here...Maybe
}

場景

原文: https://lanre.wtf/blog/2017/0...緩存

  • 緩存 responses,好比 app須要訪問 Github api,獲取 trending repos,這個數據變更不頻繁,假設30分鐘變更一次,你顯然不但願每次都要點擊api都要來請求Github api,解決這個問題的方法是實現這樣的http.RoundTripper服務器

    • 有緩存時從緩存取出response數據
    • 過時,數據經過從新請求api獲取
  • 根據須要設置http header, 一個容易想到的例子go-github一個Github的 api的go客戶端。某些github api不須要認證,有些須要認證則須要提供本身的http client,好比 ghinstallation,下面是ghinstallation 的 RoundTrip 函數實現,設置 Authorization 頭

clipboard.png

  • 限速(Rate limiting) 控制請求速率

實際的例子

實現http.RoundTripper 緩存 http response的邏輯。app

一個http server的實現函數

import (
    "fmt"
    "net/http"
)

func main() {
    // server/main.go
    mux := http.NewServeMux()

    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        // This is here so we can actually see that the responses that have been cached don't get here
        fmt.Println("The request actually got here")

        w.Write([]byte("You got here"))
    })

    http.ListenAndServe(":8000", mux)
}

http client中建立新的 http.Transport 實現 http.RoundTripper接口oop

主程序main實現
https://github.com/adelowo/ro...google

func main() {
    cachedTransport := newTransport()

    // cachedTransport 是自定義實現http.RoundTripper接口的 Transport
    client := &http.Client{
        Transport: cachedTransport,
        Timeout:   time.Second * 5,
    }

    // 每5秒清除緩存
    cacheClearTicker := time.NewTicker(time.Second * 5)

    //每秒請求一次,能夠看出response是從緩存獲取仍是從服務器請求
    reqTicker := time.NewTicker(time.Second * 1)

    terminateChannel := make(chan os.Signal, 1)

    signal.Notify(terminateChannel, syscall.SIGTERM, syscall.SIGHUP)

    req, err := http.NewRequest(http.MethodGet, "http://localhost:8000", strings.NewReader(""))

    if err != nil {
        panic("Whoops")
    }

    for {
        select {
        case <-cacheClearTicker.C:
            // Clear the cache so we can hit the original server
            cachedTransport.Clear()

        case <-terminateChannel:
            cacheClearTicker.Stop()
            reqTicker.Stop()
            return

        case <-reqTicker.C:

            resp, err := client.Do(req)

            if err != nil {
                log.Printf("An error occurred.... %v", err)
                continue
            }

            buf, err := ioutil.ReadAll(resp.Body)

            if err != nil {
                log.Printf("An error occurred.... %v", err)
                continue
            }

            fmt.Printf("The body of the response is \"%s\" \n\n", string(buf))
        }
    }
}

cacheTransport 中 RoundTrip 函數實現讀取緩存中的reponse

func (c *cacheTransport) RoundTrip(r *http.Request) (*http.Response, error) {

    // Check if we have the response cached..
    // If yes, we don't have to hit the server
    // We just return it as is from the cache store.
    if val, err := c.Get(r); err == nil {
        fmt.Println("Fetching the response from the cache")
        return cachedResponse([]byte(val), r)
    }

    // Ok, we don't have the response cached, the store was probably cleared.
    // Make the request to the server.
    resp, err := c.originalTransport.RoundTrip(r)

    if err != nil {
        return nil, err
    }

    // Get the body of the response so we can save it in the cache for the next request.
    buf, err := httputil.DumpResponse(resp, true)

    if err != nil {
        return nil, err
    }

    // Saving it to the cache store
    c.Set(r, string(buf))

    fmt.Println("Fetching the data from the real source")
    return resp, nil
}

運行結果

clipboard.png

links:

相關文章
相關標籤/搜索