The following column golang tutorial will introduce you to the usage scenarios of Context in golang. I hope it will be helpful to friends in need!
Context usage scenarios in golang
context has been included in the standard library since Go1.7. Its main use, in one sentence, is to control the life cycle of goroutine. When a computing task is taken over by a goroutine, and we want to abort the goroutine's computing task for some reason (timeout, or forced exit), then this Context is used.
This article mainly discusses some usage scenarios of context in golang:
Scenario 1: RPC call
There are 4 on the main goroutine RPC and RPC2/3/4 are requested in parallel. Here we hope that after the RPC2 request fails, an error will be returned directly and RPC3/4 will stop continuing to calculate. At this time, Context is used.
The specific implementation of this is as follows.
package main import ( "context" "sync" "github.com/pkg/errors" ) func Rpc(ctx context.Context, url string) error { result := make(chan int) err := make(chan error) go func() { // 进行RPC调用,并且返回是否成功,成功通过result传递成功信息,错误通过error传递错误信息 isSuccess := true if isSuccess { result <- 1 } else { err <- errors.New("some error happen") } }() select { case <- ctx.Done(): // 其他RPC调用调用失败 return ctx.Err() case e := <- err: // 本RPC调用失败,返回错误信息 return e case <- result: // 本RPC调用成功,不返回错误信息 return nil } } func main() { ctx, cancel := context.WithCancel(context.Background()) // RPC1调用 err := Rpc(ctx, "http://rpc_1_url") if err != nil { return } wg := sync.WaitGroup{} // RPC2调用 wg.Add(1) go func(){ defer wg.Done() err := Rpc(ctx, "http://rpc_2_url") if err != nil { cancel() } }() // RPC3调用 wg.Add(1) go func(){ defer wg.Done() err := Rpc(ctx, "http://rpc_3_url") if err != nil { cancel() } }() // RPC4调用 wg.Add(1) go func(){ defer wg.Done() err := Rpc(ctx, "http://rpc_4_url") if err != nil { cancel() } }() wg.Wait() }
Of course I use waitGroup here to ensure that the main function does not exit until all RPC calls are completed.
In the Rpc function, the first parameter is a CancelContext. This Context is a microphone. When creating the CancelContext, a listener (ctx) and microphone (cancel function) are returned. ). All goroutines hold this listener (ctx). When the main goroutine wants to tell all goroutines that it is about to end, it tells all goroutines the end information through the cancel function. Of course, all goroutines need to have built-in logic to handle the listener end signal (ctx->Done()). We can look inside the Rpc function and use a select to determine which one of ctx's done and the current rpc call ends first.
This waitGroup and one of the RPC calls notify all RPC logic. In fact, a package has already done it for us. errorGroup. For specific usage of this errorGroup package, please see the test example of this package.
Some people may worry that our cancel() here will be called multiple times. The cancel call in the context package is idempotent. You can call it multiple times with confidence.
We might as well take a look here. The Rpc function here is actually a "blocking" request in our example. If this request is implemented using http.Get or http.Post, in fact, The Goroutine of the Rpc function has ended, but the actual http.Get inside has not ended. Therefore, you need to understand that the function here is best to be "non-blocking", such as http.Do, and then it can be interrupted in some way. For example, like this example in this article Cancel http.Request using Context:
func httpRequest( ctx context.Context, client *http.Client, req *http.Request, respChan chan []byte, errChan chan error ) { req = req.WithContext(ctx) tr := &http.Transport{} client.Transport = tr go func() { resp, err := client.Do(req) if err != nil { errChan <- err } if resp != nil { defer resp.Body.Close() respData, err := ioutil.ReadAll(resp.Body) if err != nil { errChan <- err } respChan <- respData } else { errChan <- errors.New("HTTP request failed") } }() for { select { case <-ctx.Done(): tr.CancelRequest(req) errChan <- errors.New("HTTP request cancelled") return case <-errChan: tr.CancelRequest(req) return } } }
It uses http.Client.Do, and then when receiving ctx.Done, it ends by calling transport.CancelRequest.
We can also refer to net/dail/DialContext
In other words, if you want the package you implement to be "stoppable/controllable", then in the function you implement, it is best to Can receive a Context function and handle Context.Done.
Scenario 2: PipeLine
The pipeline model is an assembly line model. Several workers on the assembly line have n products and assemble them one by one. In fact, the implementation of the pipeline model has nothing to do with Context. We can also use chan to implement the pipeline model without context. But for the control of the entire pipeline, Context needs to be used. The example in this article Pipeline Patterns in Go is a very good illustration. Here is a rough description of this code.
There are three pipeline workers in runSimplePipeline. lineListSource is responsible for dividing the parameters one by one for transmission, lineParser is responsible for processing the string into int64, and sink determines whether the data is available based on the specific value. All their return values basically have two chans, one for passing data and one for passing errors. (
We can see that in the specific functions of these three workers, switch is used to handle case
func lineParser(ctx context.Context, base int, in <-chan string) ( <-chan int64, <-chan error, error) { ... go func() { defer close(out) defer close(errc) for line := range in { n, err := strconv.ParseInt(line, base, 64) if err != nil { errc <- err return } select { case out <- n: case <-ctx.Done(): return } } }() return out, errc, nil }
Scenario 3: Timeout request
When we send an RPC request, we often want to impose a timeout limit on this request. When an RPC request exceeds 10 seconds, it will be automatically disconnected. Of course, we can also achieve this function by using CancelContext (start a new goroutine, this goroutine holds the cancel function, and when the time is up, the cancel function is called).
Given that this requirement is very common, the context package also implements this requirement: timerCtx. The specific instantiation methods are WithDeadline and WithTimeout.
The specific logic in timerCtx is to call ctx.cancel through time.AfterFunc.
Official example:
package main import ( "context" "fmt" "time" ) func main() { ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) defer cancel() select { case <-time.After(1 * time.Second): fmt.Println("overslept") case <-ctx.Done(): fmt.Println(ctx.Err()) // prints "context deadline exceeded" } }
It is also a common method to add timeout in the http client
uri := "https://httpbin.org/delay/3" req, err := http.NewRequest("GET", uri, nil) if err != nil { log.Fatalf("http.NewRequest() failed with '%s'\n", err) } ctx, _ := context.WithTimeout(context.Background(), time.Millisecond*100) req = req.WithContext(ctx) resp, err := http.DefaultClient.Do(req) if err != nil { log.Fatalf("http.DefaultClient.Do() failed with:\n'%s'\n", err) } defer resp.Body.Close()
How to set a timeout on the http server?
package main import ( "net/http" "time" ) func test(w http.ResponseWriter, r *http.Request) { time.Sleep(20 * time.Second) w.Write([]byte("test")) } func main() { http.HandleFunc("/", test) timeoutHandler := http.TimeoutHandler(http.DefaultServeMux, 5 * time.Second, "timeout") http.ListenAndServe(":8080", timeoutHandler) }
我们看看TimeoutHandler的内部,本质上也是通过context.WithTimeout来做处理。
func (h *timeoutHandler) ServeHTTP(w ResponseWriter, r *Request) { ... ctx, cancelCtx = context.WithTimeout(r.Context(), h.dt) defer cancelCtx() ... go func() { ... h.handler.ServeHTTP(tw, r) }() select { ... case <p><strong>场景四:HTTP服务器的request互相传递数据</strong></p><p>context还提供了valueCtx的数据结构。</p><p>这个valueCtx最经常使用的场景就是在一个http服务器中,在request中传递一个特定值,比如有一个中间件,做cookie验证,然后把验证后的用户名存放在request中。</p><p>我们可以看到,官方的request里面是包含了Context的,并且提供了WithContext的方法进行context的替换。</p><pre class='brush:php;toolbar:false;'>package main import ( "net/http" "context" ) type FooKey string var UserName = FooKey("user-name") var UserId = FooKey("user-id") func foo(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := context.WithValue(r.Context(), UserId, "1") ctx2 := context.WithValue(ctx, UserName, "yejianfeng") next(w, r.WithContext(ctx2)) } } func GetUserName(context context.Context) string { if ret, ok := context.Value(UserName).(string); ok { return ret } return "" } func GetUserId(context context.Context) string { if ret, ok := context.Value(UserId).(string); ok { return ret } return "" } func test(w http.ResponseWriter, r *http.Request) { w.Write([]byte("welcome: ")) w.Write([]byte(GetUserId(r.Context()))) w.Write([]byte(" ")) w.Write([]byte(GetUserName(r.Context()))) } func main() { http.Handle("/", foo(test)) http.ListenAndServe(":8080", nil) }
在使用ValueCtx的时候需要注意一点,这里的key不应该设置成为普通的String或者Int类型,为了防止不同的中间件对这个key的覆盖。最好的情况是每个中间件使用一个自定义的key类型,比如这里的FooKey,而且获取Value的逻辑尽量也抽取出来作为一个函数,放在这个middleware的同包中。这样,就会有效避免不同包设置相同的key的冲突问题了。
The above is the detailed content of Do you know what are the usage scenarios of Context in golang?. For more information, please follow other related articles on the PHP Chinese website!

Golangisidealforperformance-criticalapplicationsandconcurrentprogramming,whilePythonexcelsindatascience,rapidprototyping,andversatility.1)Forhigh-performanceneeds,chooseGolangduetoitsefficiencyandconcurrencyfeatures.2)Fordata-drivenprojects,Pythonisp

Golang achieves efficient concurrency through goroutine and channel: 1.goroutine is a lightweight thread, started with the go keyword; 2.channel is used for secure communication between goroutines to avoid race conditions; 3. The usage example shows basic and advanced usage; 4. Common errors include deadlocks and data competition, which can be detected by gorun-race; 5. Performance optimization suggests reducing the use of channel, reasonably setting the number of goroutines, and using sync.Pool to manage memory.

Golang is more suitable for system programming and high concurrency applications, while Python is more suitable for data science and rapid development. 1) Golang is developed by Google, statically typing, emphasizing simplicity and efficiency, and is suitable for high concurrency scenarios. 2) Python is created by Guidovan Rossum, dynamically typed, concise syntax, wide application, suitable for beginners and data processing.

Golang is better than Python in terms of performance and scalability. 1) Golang's compilation-type characteristics and efficient concurrency model make it perform well in high concurrency scenarios. 2) Python, as an interpreted language, executes slowly, but can optimize performance through tools such as Cython.

Go language has unique advantages in concurrent programming, performance, learning curve, etc.: 1. Concurrent programming is realized through goroutine and channel, which is lightweight and efficient. 2. The compilation speed is fast and the operation performance is close to that of C language. 3. The grammar is concise, the learning curve is smooth, and the ecosystem is rich.

The main differences between Golang and Python are concurrency models, type systems, performance and execution speed. 1. Golang uses the CSP model, which is suitable for high concurrent tasks; Python relies on multi-threading and GIL, which is suitable for I/O-intensive tasks. 2. Golang is a static type, and Python is a dynamic type. 3. Golang compiled language execution speed is fast, and Python interpreted language development is fast.

Golang is usually slower than C, but Golang has more advantages in concurrent programming and development efficiency: 1) Golang's garbage collection and concurrency model makes it perform well in high concurrency scenarios; 2) C obtains higher performance through manual memory management and hardware optimization, but has higher development complexity.

Golang is widely used in cloud computing and DevOps, and its advantages lie in simplicity, efficiency and concurrent programming capabilities. 1) In cloud computing, Golang efficiently handles concurrent requests through goroutine and channel mechanisms. 2) In DevOps, Golang's fast compilation and cross-platform features make it the first choice for automation tools.


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

mPDF
mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),

VSCode Windows 64-bit Download
A free and powerful IDE editor launched by Microsoft

EditPlus Chinese cracked version
Small size, syntax highlighting, does not support code prompt function

MantisBT
Mantis is an easy-to-deploy web-based defect tracking tool designed to aid in product defect tracking. It requires PHP, MySQL and a web server. Check out our demo and hosting services.

SublimeText3 Chinese version
Chinese version, very easy to use