当前位置:网站首页>Understand the context in go language in an article

Understand the context in go language in an article

2022-07-04 15:15:00 1024 questions

Catalog

0 Pre knowledge sync.WaitGroup

1 brief introduction

2 context.Context introduce

3 context Other commonly used functions of the package

3.1 context.Background and context.TODO

3.2 context.WithCancel and

3.3 context.WithTimeout

3.4 context.WithDeadline

3.5 context.WithValue

4 example : Request browser timeout

5 Context Where are the bags used

6 Summary

0 Pre knowledge sync.WaitGroup

sync.WaitGroup Is to wait for the end of a group of collaborative processes . It implements a structure similar to task queue , You can add tasks to the queue , Remove the task from the queue after the task is completed , If all the tasks in the queue are not completed , The queue triggers a block to prevent the program from continuing . sync.WaitGroup Only 3 A way ,Add(),Done(),Wait() .

among Done() yes Add(-1) Another name for , Use Add() Add count ,Done() Subtract a count , Count is not 0, Blocking Wait() Operation of .

Example :

package mainimport (   "fmt"   "sync"   "time")var group sync.WaitGroupfunc sayHello() {   for i := 0; i < 5; i++ {      fmt.Println("hello......")      time.Sleep(time.Second)   }   // Thread end -1   group.Done()}func sayHi() {   // Thread end -1   defer group.Done()   for i := 0; i < 5; i++ {      fmt.Println("hi......")      time.Sleep(time.Second)   }}func main() {   //+2   group.Add(2)   fmt.Println("main Blocking ...")   go sayHello()   fmt.Println("main Persistent obstruction ...")   go sayHi()   // Thread waiting   group.Wait()   fmt.Println("main It seems that the jam is over ...")}

effect :

1 brief introduction

stay Go Server , Each incoming request is in its own goroutine In dealing with . Request handlers usually start additional goroutine To access the backend , For example, databases and RPC service . A group of that processes requests goroutine You usually need to access request specific values , For example, the identity of the end user 、 Authorization token and request deadline . When the request is cancelled or exceeded , Handle all of the requests goroutine We should all quit quickly , So that systems can recycle any resources they are using .

So , Developed a context package , The value of the requested range can be easily 、 Cancellation signal and deadline span API The boundary is passed to all involved in processing the request goroutine.

Context Carry a deadline 、 A cancellation signal and other jumps API Boundary value . Contextual methods can be used by multiple gor Routine calls .

Incoming requests to the server should create a context , Outgoing calls to the server should accept a context . The function call chain between them must propagate Context, You can choose to replace it with WithCancel、WithDeadline、WithTimeout or WithValue Created derivation Context. When a context is cancelled , All contexts derived from it are also cancelled .

WithCancel、WithDeadline and WithTimeout Function adoption Context( Father ) And returns the derived Context( Son ) and CancelFunc. call CancelFunc Will cancel the subitems and their subitems , Delete the reference of parent item to child item , And stop any associated timers . call CancelFunc Failure will leak children and their children , Until the parent item is canceled or the timer is triggered .go vet The tool checks whether CancelFuncs.

Procedures that use context should follow the following rules , To maintain consistent interfaces across packages , And enable static analysis tools to check context propagation :

Don't store context in structure type ; contrary , take Context Explicitly pass it to every function that needs it .

Context It should be the first parameter , Usually named ctx:

func DoSomething(ctx context.Context, arg Arg) error { // ... Use ctx ... }

Even if the function allows , Don't pass on nil Context . If you're not sure which one to use Context, Please deliver context.TODO.

Use context values only for transfer processes and API Request scope data , It is not used to pass optional parameters to functions .

same Context Can be passed to in different goroutine Functions running in ; Context for multiple goroutine It's safe to use at the same time .

2 context.Context introduce // The context carries the deadline 、 The values of the cancellation signal and request range are API The boundaries of . Its method is safe to use multiple at the same time goroutine.type Context interface {    // Done Returns a channel closed when the context is canceled or timed out .    Done() <-chan struct{}​    // Err It means that Done Why cancel this context after the channel is closed .    Err() error​    // Deadline Returns the time when the context will be canceled ( If any ).    Deadline() (deadline time.Time, ok bool)​    // Value Return and key Related values , If not, return nil.    Value(key interface{}) interface{}}

The Done Method returns a channel , This channel acts as a cancellation signal representing the function of operation Context: When the channel is closed , Functions should give up their work and return .

The Err Method returns an error , instructions Context Reason for cancellation .

One Context For multiple goroutine It's safe to use at the same time . Code can pass a single Context Give any number of goroutines And cancel it Context To all goroutine Signal .

The Deadline Methods allow functions to determine whether they should start working , You can also use the deadline to set I/O Timeout for operation .

Value Allows a Context Carry the data of the requested range . The data must be secure , In order to more goroutine Use at the same time .

3 context Other commonly used functions of the package 3.1 context.Background and context.TODO

Background Any Context The root of the tree , It will never be cancelled :

//Background Returns an empty Context. It will never be cancelled , There is no deadline , There is no value . Background Usually used for main、init and tests, And as the top-level context of the incoming request .  func Background() Context

Pass to a function method Context When , Do not pass nil, If you don't know what to deliver , Just use context.TODO()

3.2 context.WithCancel and

WithCancelt Returns derived Context value , You can compare with your father Context Cancel faster . When the request handler returns , Usually, the content. When using multiple copies ,WithCancel It is also useful for cancelling redundant requests .

// WithCancel Return a copy of the parent process , Of the parent process Done The channel is closed as soon as possible .  close Done Or call cancel.func WithCancel(parent Context) (ctx Context, cancel CancelFunc)// CancelFunc Cancel a context .type CancelFunc func()

Example :

package mainimport (   "context"   "fmt")func play(ctx context.Context) <-chan int {   dist := make(chan int)   n := 1   // Anonymous functions towards dist Add elements to   go func() {      for {         select {         //ctx This... Will not be executed when it is empty         case <-ctx.Done():            return // return End this goroutine, Prevent leakage            // towards dist Add elements to         case dist <- n:            n++         }     }   }()   return dist}func main() {   // Return empty context   ctx, cancel := context.WithCancel(context.Background())   defer cancel() // call cancel   for n := range play(ctx) {      fmt.Println(n)      if n == 5 {         break     }   }}

Expand :go in select Usage of

```select The usage and switch The language is very similar , from select Start a new selection block , Each selection is made by case To describe . And switch Statement than , select There are more restrictions , One of the biggest restrictions is that each case There must be a IO operation , The general structure is as follows :``` goselect {   case <-chan1:      // If chan1 Successfully read the data , Then go ahead with the case Processing statements   case chan2 <- 1:      // If we succeed in chan2 Write data , Then go ahead with the case Processing statements   default:      // If none of the above works , entering default Processing flow }``` In a select In the sentence ,Go The language evaluates each statement sent and received from beginning to end in order . If any of these statements can continue ( That is, it is not blocked ), Then choose any one of those executable statements to use . If no statement can be executed ( That is, all channels are blocked ), So there are two possibilities :- If given default sentence , Then it will carry out default sentence , At the same time, the execution of the program will start from select Restore... In the statement after the statement .- without default sentence , that select The statement will be blocked , Until at least one communication can go on .```3.3 context.WithTimeout

WithTimeout Returns derived Context value ,WithTimeout Used to set the deadline for requests to the back-end server :

//WithTimeout Return a copy of the parent process , Of the parent process Done Parents whose channels were immediately closed . close “ complete ”、 call “ Cancel ” Or timeout ends . new //Context Of Deadline It's faster now +timeout And father's Deadline, If anything .  If the timer is still running , be cancel Function to release its resources .func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)// CancelFunc Cancel a context .type CancelFunc func()

Example :

package mainimport (   "context"   "fmt"   "sync"   "time")var wg sync.WaitGroupfunc worker(ctx context.Context) {    LOOP:   for {      fmt.Println("db connecting ...")      time.Sleep(time.Millisecond * 10) // Suppose it takes time to connect to the database normally 10 millisecond      select {      case <-ctx.Done(): // 50 Called automatically in milliseconds         break LOOP      default:     }   }   fmt.Println("worker done!")   wg.Done()}func main() {   // Set up a 50 A millisecond timeout   ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50)   wg.Add(1)   go worker(ctx)   time.Sleep(time.Second * 5)   cancel() // Notifier goroutine end   wg.Wait()   fmt.Println("over")}

Execution results :

3.4 context.WithDeadlinefunc WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {   if parent == nil {      panic("cannot create context from nil parent")   }   if cur, ok := parent.Deadline(); ok && cur.Before(d) {      // The current deadline is already ahead of the new deadline      return WithCancel(parent)   }   c := &timerCtx{      cancelCtx: newCancelCtx(parent),      deadline:  d,   }   propagateCancel(parent, c)   dur := time.Until(d)   if dur <= 0 {      c.cancel(true, DeadlineExceeded) // The deadline has passed      return c, func() { c.cancel(false, Canceled) }   }   c.mu.Lock()   defer c.mu.Unlock()   if c.err == nil {      c.timer = time.AfterFunc(dur, func() {         c.cancel(true, DeadlineExceeded)     })   }   return c, func() { c.cancel(true, Canceled) }}

Example :

package mainimport (   "context"   "fmt"   "time")func main() {   d := time.Now().Add(500 * time.Millisecond)   ctx, cancel := context.WithDeadline(context.Background(), d)   // Even though ctx Will expire , But in any case it's called cancel Functions are good practices .   // If you don't do that , It may make the context and its parent class live longer than necessary .   defer cancel()   select {   case <-time.After(1 * time.Second):      fmt.Println("over")   case <-ctx.Done():      fmt.Println(ctx.Err())   }}

Execution results :

3.5 context.WithValue

WithValue Provides a way to compare the value of the request range with Context The method of Association :

//WithValue Return a copy of the parent element , Its Value Method returns val for key.func WithValue(parent Context, key interface{}, val interface{}) Context

Learn how to use context The best way to package is through a working example .

Example :

package mainimport (   "context"   "fmt"   "sync"   "time")type TraceCode stringvar wg sync.WaitGroupfunc worker(ctx context.Context) {   key := TraceCode("KEY_CODE")   traceCode, ok := ctx.Value(key).(string) // In the child goroutine In order to get trace code   if !ok {      fmt.Println("invalid trace code")   }    LOOP:   for {      fmt.Printf("worker,code:%s\n", traceCode)      time.Sleep(time.Millisecond * 10) // Suppose it takes time to connect to the database normally 10 millisecond      select {      case <-ctx.Done(): // 50 Called automatically in milliseconds         break LOOP      default:     }   }   fmt.Println("worker is over!")   wg.Done()}​func main() {   // Set up a 50 A millisecond timeout   ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50)   // Set in the entrance of the system trace code Pass it on to the subsequent startup goroutine Realize log data aggregation   ctx = context.WithValue(ctx, TraceCode("KEY_CODE"), "12512312234")   wg.Add(1)   go worker(ctx)   time.Sleep(time.Second * 5)   cancel() // Notifier goroutine end   wg.Wait()   fmt.Println("over")}

Execution results :

4 example : Request browser timeout

server End :

package mainimport (   "fmt"   "math/rand"   "net/http"   "time")// server End , Random slow response func indexHandler(w http.ResponseWriter, r *http.Request) {   number := rand.Intn(2)   if number == 0 {      time.Sleep(time.Second * 10) // Time consuming 10 Slow response in seconds      fmt.Fprintf(w, "slow response")      return   }   fmt.Fprint(w, "quick response")}func main() {   http.HandleFunc("/", indexHandler)   err := http.ListenAndServe(":9999", nil)   if err != nil {      panic(err)   }}

client End :

package mainimport (   "context"   "fmt"   "io/ioutil"   "net/http"   "sync"   "time")// client ​type respData struct {   resp *http.Response   err  error}func doCall(ctx context.Context) {   // http A long connection   transport := http.Transport{DisableKeepAlives: true}   client := http.Client{Transport: &transport}​   respChan := make(chan *respData, 1)   req, err := http.NewRequest("GET", "http://127.0.0.1:9999/", nil)   if err != nil {      fmt.Println(err)      return   }   req = req.WithContext(ctx) // Use the... With timeout ctx Create a new client request   var wg sync.WaitGroup   wg.Add(1)   defer wg.Wait()   go func() {      resp, err := client.Do(req)      fmt.Printf("resp:%v, err:%v\n", resp, err)      rd := &respData{         resp: resp,         err:  err,     }      respChan <- rd      wg.Done()   }()   select {   case <-ctx.Done():      fmt.Println("timeout...")   case result := <-respChan:      fmt.Println("success....")      if result.err != nil {         fmt.Printf("err:%v\n", result.err)         return     }      defer result.resp.Body.Close()      data, _ := ioutil.ReadAll(result.resp.Body)      fmt.Printf("resp:%v\n", string(data))   }}​func main() {   // Define a 100 A millisecond timeout   ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*100)   defer cancel() // call cancel Releaser goroutine resources   doCall(ctx)}5 Context Where are the bags used

Many server frameworks provide packages and types for hosting request scope values . We can define “Context” New implementation of interface , The code and needs of using the existing framework “Context” Build a bridge between the codes of parameters .

6 Summary

In Google , requirement Go The programmer will “Context” Parameters are passed as the first parameter of each function on the call path between incoming and outgoing requests . This allows many different teams to develop Go The code can interoperate well . It provides simple control over timeouts and cancellations , And ensure that key values such as security credentials can be transmitted correctly Go Program .

Want to build on “Context” The server framework on should provide “Context” To connect their packages to those that need “Context” Package of parameters . then , Their client libraries will accept “Context”. By establishing a common interface for requesting scope data and cancellation ,“ Context ” Make it easier for package developers to share code that creates scalable services .

This is about an article to understand Go In language Context This is the end of the article , More about Go Context Please search the previous articles of software development network or continue to browse the relevant articles below. I hope you will support software development network more in the future !


原网站

版权声明
本文为[1024 questions]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/185/202207041348059015.html