国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【Golang】golang中http請求的context傳遞到異步任務(wù)的坑

這篇具有很好參考價值的文章主要介紹了【Golang】golang中http請求的context傳遞到異步任務(wù)的坑。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

前言

在golang中,context.Context可以用來用來設(shè)置截止日期、同步信號,傳遞請求相關(guān)值的結(jié)構(gòu)體。 與 goroutine 有比較密切的關(guān)系。

在web程序中,每個Request都需要開啟一個goroutine做一些事情,這些goroutine又可能會開啟其他的 goroutine去訪問后端資源,比如數(shù)據(jù)庫、RPC服務(wù)等,它們需要訪問一些共享的資源,比如用戶身份信息、認(rèn)證token、請求截止時間等 這時候可以通過Context,來跟蹤這些goroutine,并且通過Context來控制它們, 這就是Go語言為我們提供的Context,中文可以理解為“上下文”。

簡單看一下Context結(jié)構(gòu):

type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key interface{}) interface{}
}
  • Deadline方法是獲取設(shè)置的截止時間的意思,第一個返回值是截止時間,到了這個時間點,Context會自動發(fā)起取消請求; 第二個返回值ok==false時表示沒有設(shè)置截止時間,如果需要取消的話,需要調(diào)用取消函數(shù)(CancleFunc)進行取消。
  • Done方法返回一個只讀的chan,類型為struct{},在goroutine中,如果該方法返回的chan可以讀取,則意味著parent context已經(jīng)發(fā)起了取消請求, 我們通過Done方法收到這個信號后,就應(yīng)該做清理操作,然后退出goroutine,釋放資源。之后,Err 方法會返回一個錯誤,告知為什么 Context 被取消。
  • Err方法返回取消的錯誤原因,Context被取消的原因。
  • Value方法獲取該Context上綁定的值,是一個鍵值對,通過一個Key才可以獲取對應(yīng)的值,這個值一般是線程安全的。

常用的

// 傳遞一個父Context作為參數(shù),返回子Context,以及一個取消函數(shù)用來取消Context。
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
// 和WithCancel差不多,它會多傳遞一個截止時間參數(shù),意味著到了這個時間點,會自動取消Context,
// 當(dāng)然我們也可以不等到這個時候,可以提前通過取消函數(shù)進行取消。
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)

// WithTimeout和WithDeadline基本上一樣,這個表示是超時自動取消,是多少時間后自動取消Context的意思
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)

//WithValue函數(shù)和取消Context無關(guān),它是為了生成一個綁定了一個鍵值對數(shù)據(jù)的Context,
// 綁定的數(shù)據(jù)可以通過Context.Value方法訪問到,這是我們實際用經(jīng)常要用到的技巧,一般我們想要通過上下文來傳遞數(shù)據(jù)時,可以通過這個方法,
// 如我們需要tarce追蹤系統(tǒng)調(diào)用棧的時候。
func WithValue(parent Context, key, val interface{}) Context

一、HTTP請求的Context傳遞到異步任務(wù)的坑

看下面例子:我們將http的context傳遞到goroutine 中:

package main

import (
	"context"
	"fmt"
	"net/http"
	"time"
)

func IndexHandler(resp http.ResponseWriter, req *http.Request) {
	ctx := req.Context()
	go func(ctx context.Context) {
		for {
			select {
			case <-ctx.Done():
				fmt.Println("gorountine off,the err is: ", ctx.Err())
				return
			default:
				fmt.Println(333)
			}
		}
	}(ctx)

	time.Sleep(1000)
	resp.Write([]byte{1})
}
func main() {

	http.HandleFunc("/test1", IndexHandler)
	http.ListenAndServe("127.0.0.1:8080", nil)
}

結(jié)果:
【Golang】golang中http請求的context傳遞到異步任務(wù)的坑
從上面結(jié)果來看,在http請求返回之后,傳入gorountine的context被cancel掉了,如果不巧,你在gorountine中進行一些http調(diào)用或者rpc調(diào)用傳入了這個context,那么對應(yīng)的請求也將會被cancel掉。因此,在http請求中異步任務(wù)出去時,如果這個異步任務(wù)中需要進行一些rpc類請求,那么就不要直接使用或者繼承http的context,否則將會被cancel。

糾其原因:http請求再結(jié)束后,將會cancel掉這個context,所以異步出去的請求中收到的context是被cancel掉的。

下面來看下源代碼:
ListenAndServe–>Server:Server方法中有一個大的for循環(huán),這個for循環(huán)中,針對每個請求,都會起一個協(xié)程進行處理。
【Golang】golang中http請求的context傳遞到異步任務(wù)的坑
serve方法處理一個連接中的請求,并在一個請求serverHandler{c.server}.ServeHTTP(w, w.req)結(jié)束后cancel掉對應(yīng)的context:

// Serve a new connection.
func (c *conn) serve(ctx context.Context) {
	c.remoteAddr = c.rwc.RemoteAddr().String()
	ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())
	defer func() {
		if err := recover(); err != nil && err != ErrAbortHandler {
			const size = 64 << 10
			buf := make([]byte, size)
			buf = buf[:runtime.Stack(buf, false)]
			c.server.logf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
		}
		if !c.hijacked() {
			c.close()
			c.setState(c.rwc, StateClosed, runHooks)
		}
	}()

	if tlsConn, ok := c.rwc.(*tls.Conn); ok {
		if d := c.server.ReadTimeout; d != 0 {
			c.rwc.SetReadDeadline(time.Now().Add(d))
		}
		if d := c.server.WriteTimeout; d != 0 {
			c.rwc.SetWriteDeadline(time.Now().Add(d))
		}
		if err := tlsConn.Handshake(); err != nil {
			// If the handshake failed due to the client not speaking
			// TLS, assume they're speaking plaintext HTTP and write a
			// 400 response on the TLS conn's underlying net.Conn.
			if re, ok := err.(tls.RecordHeaderError); ok && re.Conn != nil && tlsRecordHeaderLooksLikeHTTP(re.RecordHeader) {
				io.WriteString(re.Conn, "HTTP/1.0 400 Bad Request\r\n\r\nClient sent an HTTP request to an HTTPS server.\n")
				re.Conn.Close()
				return
			}
			c.server.logf("http: TLS handshake error from %s: %v", c.rwc.RemoteAddr(), err)
			return
		}
		c.tlsState = new(tls.ConnectionState)
		*c.tlsState = tlsConn.ConnectionState()
		if proto := c.tlsState.NegotiatedProtocol; validNextProto(proto) {
			if fn := c.server.TLSNextProto[proto]; fn != nil {
				h := initALPNRequest{ctx, tlsConn, serverHandler{c.server}}
				// Mark freshly created HTTP/2 as active and prevent any server state hooks
				// from being run on these connections. This prevents closeIdleConns from
				// closing such connections. See issue https://golang.org/issue/39776.
				c.setState(c.rwc, StateActive, skipHooks)
				fn(c.server, tlsConn, h)
			}
			return
		}
	}

	// HTTP/1.x from here on.

	ctx, cancelCtx := context.WithCancel(ctx)
	c.cancelCtx = cancelCtx
	defer cancelCtx()

	c.r = &connReader{conn: c}
	c.bufr = newBufioReader(c.r)
	c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)

	for {
		// 從連接中讀取請求
		w, err := c.readRequest(ctx)
		if c.r.remain != c.server.initialReadLimitSize() {
			// If we read any bytes off the wire, we're active.
			c.setState(c.rwc, StateActive, runHooks)
		}
		.....
		.....
		// Expect 100 Continue support
		req := w.req
		if req.expectsContinue() {
			if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {
				// Wrap the Body reader with one that replies on the connection
				req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
				w.canWriteContinue.setTrue()
			}
		} else if req.Header.get("Expect") != "" {
			w.sendExpectationFailed()
			return
		}

		c.curReq.Store(w)
		
		// 啟動協(xié)程后臺讀取連接
		if requestBodyRemains(req.Body) {
			registerOnHitEOF(req.Body, w.conn.r.startBackgroundRead)
		} else {
			w.conn.r.startBackgroundRead() 
		}

		// HTTP cannot have multiple simultaneous active requests.[*]
		// Until the server replies to this request, it can't read another,
		// so we might as well run the handler in this goroutine.
		// [*] Not strictly true: HTTP pipelining. We could let them all process
		// in parallel even if their responses need to be serialized.
		// But we're not going to implement HTTP pipelining because it
		// was never deployed in the wild and the answer is HTTP/2.
		serverHandler{c.server}.ServeHTTP(w, w.req)
		/**
		* 重點在這兒,處理完請求后將會調(diào)用w.cancelCtx()方法cancel掉context
		**/
		w.cancelCtx()
		if c.hijacked() {
			return
		}
		w.finishRequest()
		if !w.shouldReuseConnection() {
			if w.requestBodyLimitHit || w.closedRequestBodyEarly() {
				c.closeWriteAndWait()
			}
			return
		}
		c.setState(c.rwc, StateIdle, runHooks)
		c.curReq.Store((*response)(nil))

		if !w.conn.server.doKeepAlives() {
			// We're in shutdown mode. We might've replied
			// to the user without "Connection: close" and
			// they might think they can send another
			// request, but such is life with HTTP/1.1.
			return
		}

		if d := c.server.idleTimeout(); d != 0 {
			c.rwc.SetReadDeadline(time.Now().Add(d))
			if _, err := c.bufr.Peek(4); err != nil {
				return
			}
		}
		c.rwc.SetReadDeadline(time.Time{})
	}
}

至此,我們知道,http請求在正常結(jié)束后將會主動cancel掉context。此外,在請求異常時候也會主動cancel掉context(cancel目的就是為了快速失?。?,具體可見w.conn.r.startBackgroundRead() 其中的實現(xiàn)。

在日常開發(fā)中,我們知道有時候會存在客戶端超時情況,和ctx相關(guān)的原因可歸納如下:

  • 服務(wù)端收到的請求的request context被cancel掉。
  • 客戶端本身收到context deadline exceeded錯誤
  • 服務(wù)端業(yè)務(wù)業(yè)務(wù)使用了http的context,但沒有用于做rpc等需要建立連接的任務(wù),那么客戶端即使收到了context canceled的錯誤,服務(wù)端實際上還是在繼續(xù)執(zhí)行業(yè)務(wù)代碼。
  • 服務(wù)端業(yè)務(wù)業(yè)務(wù)使用了http的context,并用于做rpc等需要建立連接的任務(wù),那么客戶端收到context canceled錯誤,并且服務(wù)端也會在對應(yīng)的rpc等建立連接任務(wù)處返回context cancled的錯誤。

最后,如果context cancel掉了,但是業(yè)務(wù)又在繼續(xù)執(zhí)行,有時候并不是我們想要的結(jié)果,因為這會占用資源,因此我們可以主動在業(yè)務(wù)中通過監(jiān)聽context Done的信號來做context canceled的處理,從而可以達到快速失敗,節(jié)約資源的目的。文章來源地址http://www.zghlxwxcb.cn/news/detail-472723.html

到了這里,關(guān)于【Golang】golang中http請求的context傳遞到異步任務(wù)的坑的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • golang 發(fā)起 http 請求,獲取訪問域名的 ip 地址(net, httptrace)

    golang 發(fā)起 http 請求,獲取訪問域名的 ip 地址(net, httptrace)

    今天碰到了個需求,我要知道程序?qū)ν庠L問的 http 請求域名的 ip 地址。 直接查看 golang 的 net/http 包,發(fā)現(xiàn) Response 中并沒有我想要的 ip 信息。 考慮到在 OSI 七層模型中, ip 是網(wǎng)絡(luò)層協(xié)議,而 http 是應(yīng)用層協(xié)議。去翻了下 net 包,發(fā)現(xiàn)了基礎(chǔ)用法。 先提一下,域名訪問服務(wù)器

    2024年02月02日
    瀏覽(26)
  • 【Golang 接口自動化02】使用標(biāo)準(zhǔn)庫net/http發(fā)送Post請求

    【Golang 接口自動化02】使用標(biāo)準(zhǔn)庫net/http發(fā)送Post請求

    目錄 寫在前面 發(fā)送Post請求 示例代碼 源碼分析 Post請求參數(shù)解析 響應(yīng)數(shù)據(jù)解析 驗證 發(fā)送Json/XMl Json請求示例代碼 xml請求示例代碼 總結(jié) 資料獲取方法 上一篇我們介紹了使用? net/http ?發(fā)送get請求,因為考慮到篇幅問題,把Post單獨拎了出來,我們在這一篇一起從源碼來了解一

    2024年02月14日
    瀏覽(30)
  • 【Golang 接口自動化01】使用標(biāo)準(zhǔn)庫net/http發(fā)送Get請求

    【Golang 接口自動化01】使用標(biāo)準(zhǔn)庫net/http發(fā)送Get請求

    目錄 發(fā)送Get請求 響應(yīng)信息 拓展 ?資料獲取方法 使用Golang發(fā)送get請求很容易,我們還是使用 http://httpbin.org 作為服務(wù)端來進行演示。 更多的響應(yīng)內(nèi)容我們查看安裝路徑的net包中Response struct 的信息,里面有詳細(xì)的注釋,參考路徑: C:Gosrcnethttpresponse.go : 用過Python的同學(xué)都知

    2024年02月14日
    瀏覽(55)
  • Golang:淺析Context包

    在golang官方文檔context package - context - Go Packages中是這樣介紹context包的: 在context包中定義了context類型來在不同的Goroutine 之間傳遞上下文,攜帶截止時間、取消信號以及攜帶上下文的系統(tǒng)參數(shù)(k-v)的類型。對服務(wù)器的傳入請求應(yīng)該創(chuàng)建上下文,對服務(wù)器的傳出調(diào)用應(yīng)該接受上

    2024年02月06日
    瀏覽(19)
  • golang Context應(yīng)用舉例

    golang Context應(yīng)用舉例

    ? golang標(biāo)準(zhǔn)庫里Context實際上是一個接口(即一種編程規(guī)范、 一種約定)。 ? 通過查看源碼里的注釋,我們得到如下約定: Done()函數(shù)返回一個只讀管道,且管道里不存放任何元素(struct{}),所以用這個管道就是為了實現(xiàn)阻塞 Deadline()用來記錄到期時間,以及是否到期。 Err()用來

    2024年02月08日
    瀏覽(19)
  • golang中context詳解

    編碼中遇到上下文信息傳遞,并發(fā)信息取消等,記錄下在go語言中context包的使用。 在Go語言中,context包提供了一種在程序中傳遞截止日期、取消信號、請求范圍數(shù)據(jù)和其他元數(shù)據(jù)的方式。context包的核心類型是Context接口,它定義了在執(zhí)行上下文中傳遞的方法。Context接口的主要

    2024年01月21日
    瀏覽(14)
  • golang之context實用記錄

    簡言 WithCancel()函數(shù)接受一個 Context 并返回其子Context和取消函數(shù)cancel 新創(chuàng)建協(xié)程中傳入子Context做參數(shù),且需監(jiān)控子Context的Done通道,若收到消息,則退出 需要新協(xié)程結(jié)束時,在外面調(diào)用 cancel 函數(shù),即會往子Context的Done通道發(fā)送消息 注意:當(dāng) 父Context的 Done() 關(guān)閉的時候,子

    2024年02月09日
    瀏覽(19)
  • Golang 如何基于現(xiàn)有的 context 創(chuàng)建新的 context?

    目錄 基于現(xiàn)有的 context 創(chuàng)建新的 context 現(xiàn)有創(chuàng)建方法的問題 Go 1.21 中的 context.WithoutCancel 函數(shù) Go 版本低于 1.21 該怎么辦? 在 Golang 中,context 包提供了創(chuàng)建和管理上下文的功能。當(dāng)需要基于現(xiàn)有的 context.Context 創(chuàng)建新的 context 時,通常是為了添加額外的控制信息或為了滿足特

    2024年01月17日
    瀏覽(21)
  • 【golang】Context超時控制與原理

    【golang】Context超時控制與原理

    在Go語言圈子中流行著一句話: Never start a goroutine without knowing how it will stop。 翻譯:如果你不知道協(xié)程如何退出,就不要使用它。 在創(chuàng)建協(xié)程時,我們可能還會再創(chuàng)建一些別的子協(xié)程,那么這些協(xié)程的退出就成了問題。在Go1.7之后,Go官方引入了Context來實現(xiàn)協(xié)程的退出。不僅

    2024年01月22日
    瀏覽(19)
  • Golang中context包基礎(chǔ)知識詳解

    目錄 什么是context.Context? 如何構(gòu)造context.Context對象? 衍生Context方法 使用context包需要注意的點 context.Context是Golang標(biāo)準(zhǔn)庫提供的接口(context包對此接口有多種實現(xiàn)),該接口提供了四個抽象法: Deadline方法,返回context.Context被取消的時間點,也就是需要完成任務(wù)的截止時間

    2024年02月02日
    瀏覽(22)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包