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.
}),
})