1. 程式人生 > 其它 >go http header 大小寫指定_解析 Go context 通過 HTTP 的傳播

go http header 大小寫指定_解析 Go context 通過 HTTP 的傳播

技術標籤:go http header 大小寫指定

Go 1.7 引入了一個內建的上下文(context)型別。在系統中,可以使用 context 傳遞請求範圍的元資料,例如不同函式,執行緒甚至程序之間的請求 ID。Go 將 context 引入標準庫的初衷是以統一同一程序內的 context 傳播。因此整個庫和框架可以使用標準 context,同時可以避免程式碼碎片化。在引入該包之前,每個框架都在使用它們自己的 context 型別,並且沒有兩個 context 彼此相容。在這種情況下傳播當前 context 就要編寫醜陋的膠水程式碼。

儘管引入公共上下文傳播機制對於統一同一程序內的 context 傳遞很有用,但 Go 上下文包不提供任何串聯 context 的支援。如上所述,在網路系統中,上下文應該在不同程序之間線上路上傳播。例如,在多服務體系結構中,請求通過多個程序(幾個微服務,訊息佇列,資料庫),直到完成使用者請求。能夠在程序之間傳播 context 對於底層應用的的 context 協作是非常重要的。

如果要通過 HTTP 傳播當前 context,則需要自己序列化 context。同樣在接收端,你需要解析傳入的請求並將值放入當前 context 中。假設,我們希望在上下文中傳播請求 ID:

package request

import "context"
// WithID 將當前 request Id 放入 context 中 .
func WithID(ctx context.Context, id string) context.Context {
    return context.WithValue(ctx, contextIDKey, id)
}
// IDFromContext 從 context 中取出 request Id.
func IDFromContext(ctx context.Context) string {
    v := ctx.Value(contextIDKey)
    if v == nil {
        return ""
    }
    return v.(string)
}

type contextIDType struct{}
var contextIDKey = &contextIDType{}
// ..

WithID 允許我們讀取請求 ID,IDFromContext 允許我們將請求 ID 放在給定的 context 中。一旦我們想要跨越執行緒傳播 context,我們就需要進行手動操作以將 context 置於同一條線上。同時,將其從傳播線路上解析到接收端的 context。

在 HTTP 上,我們可以將請求 ID 轉儲為 header。大多數 context 元資料可以作為 header 傳播。一些傳輸層可能不提供 header 或 header 可能不滿足傳播資料的要求(例如,由於大小限制和缺乏加密)。在這種情況下,由實現來決定如何傳播 context。

HTTP 傳播

沒有一種方法能自動將 context 放入 HTTP 請求,反之亦然。由於無法迭代 context 值,因此也無法轉儲整個 context。

const requestIDHeader = "request-id"
// Transport 將請求 context 序列化為請求 header.
type Transport struct {
    // Base 生成 request.
    // 預設使用 http.DefaultTransport.
    Base http.RoundTripper
}
// RoundTrip 將請求 context 轉換成 headers
// 並生成請求 .
func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
    r = cloneReq(r) // per RoundTrip interface enforces
    rid := request.IDFromContext(r.Context())
    if rid != "" {
        r.Header.Add(requestIDHeader, rid)
    }
    base := t.Base
    if base == nil {
        base = http.DefaultTransport
    }
    return base.RoundTrip(r)
}

在上面的 Transport 中,請求 ID(如果存在於請求上下文中)將作為“ request-id ” header 傳播。

類似地,處理程式可以解析傳入的請求以將“ request-id ”放入請求 context 中。

// Handler 將請求 header 反序列化為請求 context.
type Handler struct {
    // Base is the actual handler to call once deserialization
    // 當 context 完成的時候,Base 將會呼叫一次反序列化過程 .
    Base http.Handler
}

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    rid := r.Header.Get(requestIDHeader)
    if rid != "" {
        r = r.WithContext(request.WithID(r.Context(), rid))
    }
    h.Base.ServeHTTP(w, r)
}

為了繼續傳播 context,請確保將當前 context 傳遞給處理程式的傳出請求。傳入 context 將傳播到 https://endpoint。

http.Handle("/", &Handler{
    Base: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        req, _ := http.NewRequest("GET", "https://endpoint", nil)
        // Propagate the incoming context.
        req = req.WithContext(r.Context())
        // 生成 request.
    }),
})