当前位置:网站首页>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】
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.WaitGroupsync.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 introductionstay 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 .
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 andWithCancelt 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.WithTimeoutWithTimeout 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.WithValueWithValue 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 timeoutserver 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 SummaryIn 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 !
边栏推荐
猜你喜欢
The per capita savings of major cities in China have been released. Have you reached the standard?
go-zero微服务实战系列(九、极致优化秒杀性能)
音视频技术开发周刊 | 252
Intelligent customer service track: Netease Qiyu and Weier technology play different ways
[differential privacy and data adaptability] differential privacy code implementation series (XIV)
Quick introduction to automatic control principle + understanding
LeetCode 1200 最小絕對差[排序] HERODING的LeetCode之路
[local differential privacy and random response code implementation] differential privacy code implementation series (13)
Redis哨兵模式实现一主二从三哨兵
深度学习 网络正则化
随机推荐
[Dalian University of technology] information sharing of postgraduate entrance examination and re examination
十六进制
Redis 發布和訂閱
On the implementation plan of MySQL explain
力扣刷题01(反转链表+滑动窗口+LRU缓存机制)
Ffprobe common commands
这几年爆火的智能物联网(AIoT),到底前景如何?
MYSQL索引优化
[learning notes] matroid
Numpy notes
智能客服赛道:网易七鱼、微洱科技打法迥异
谈SaaS下如何迅速部署应用软件
.Net 应用考虑x64生成
MP3是如何诞生的?
MySQL学习笔记——数据类型(数值类型)
PXE网络
Go zero micro service practical series (IX. ultimate optimization of seckill performance)
Decimal, exponential
PLC Analog input analog conversion FC s_ ITR (CoDeSys platform)
Quelles sont les perspectives de l'Internet intelligent des objets (aiot) qui a explosé ces dernières années?