当前位置:网站首页>Go standard library context package: data, cancellation signal, deadline and other related operations between a single request and multiple goroutines and the request domain
Go standard library context package: data, cancellation signal, deadline and other related operations between a single request and multiple goroutines and the request domain
2022-06-29 21:35:00 【Learn programming notes】
Go Standard library Context package : Single request multiple goroutine Between and request domain data 、 Cancel signal 、 Deadline and other related operations
| Golang
stay Go http Bag Server in , Each request has a corresponding goroutine To deal with . Request processing functions usually start additional goroutine Used to access back-end services , Such as databases and RPC service . Used to process a request goroutine You usually need to access some data specific to the request , For example, the identity authentication information of the end user 、 Verify the relevant token、 Deadline for requests . When a request is cancelled or timed out , All that is used to process the request goroutine You should quit quickly , Then the system can release these goroutine Occupied resources .
Why Context
Basic example
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
// Initial example
func worker() {
for {
fmt.Println("worker")
time.Sleep(time.Second)
}
// How to receive external command to exit
wg.Done()
}
func main() {
wg.Add(1)
go worker()
// How to achieve the end gracefully goroutine
wg.Wait()
fmt.Println("over")
}
Global variable mode
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
var exit bool
// The problem of global variable mode :
// 1. Using global variables is not easy to unify when calling across packages
// 2. If worker Start again in goroutine, It's not easy to control .
func worker() {
for {
fmt.Println("worker")
time.Sleep(time.Second)
if exit {
break
}
}
wg.Done()
}
func main() {
wg.Add(1)
go worker()
time.Sleep(time.Second * 3) // sleep3 Seconds to prevent the program from quitting too quickly
exit = true // Modify the global variable implementation sub goroutine The exit of
wg.Wait()
fmt.Println("over")
}
Way of access
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
// The problem of pipeline mode :
// 1. Using global variables is not easy to standardize and unify when calling across packages , Need to maintain a shared channel
func worker(exitChan chan struct{
}) {
LOOP:
for {
fmt.Println("worker")
time.Sleep(time.Second)
select {
case <-exitChan: // Waiting to be informed
break LOOP
default:
}
}
wg.Done()
}
func main() {
var exitChan = make(chan struct{
})
wg.Add(1)
go worker(exitChan)
time.Sleep(time.Second * 3) // sleep3 Seconds to prevent the program from quitting too quickly
exitChan <- struct{
}{
} // Giver goroutine Send an exit signal
close(exitChan)
wg.Wait()
fmt.Println("over")
}
The official version of the plan
package main
import (
"context"
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func worker(ctx context.Context) {
LOOP:
for {
fmt.Println("worker")
time.Sleep(time.Second)
select {
case <-ctx.Done(): // Waiting for the superior to inform
break LOOP
default:
}
}
wg.Done()
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
wg.Add(1)
go worker(ctx)
time.Sleep(time.Second * 3)
cancel() // Notifier goroutine end
wg.Wait()
fmt.Println("over")
}
space in between goroutine Open another goroutine when , Only need to ctx Just pass it in :
package main
import (
"context"
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func worker(ctx context.Context) {
go worker2(ctx)
LOOP:
for {
fmt.Println("worker")
time.Sleep(time.Second)
select {
case <-ctx.Done(): // Waiting for the superior to inform
break LOOP
default:
}
}
wg.Done()
}
func worker2(ctx context.Context) {
LOOP:
for {
fmt.Println("worker2")
time.Sleep(time.Second)
select {
case <-ctx.Done(): // Waiting for the superior to inform
break LOOP
default:
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
wg.Add(1)
go worker(ctx)
time.Sleep(time.Second * 3)
cancel() // Notifier goroutine end
wg.Wait()
fmt.Println("over")
}
Context First time to know
Go1.7 Added a new standard library context, It defines the Context type , Designed to simplify For multiple goroutine Between and request domain data 、 Cancel signal 、 Deadline and other related operations , These operations may involve multiple API call .
The incoming request to the server should create a context , Outgoing calls to the server should accept the context . The chain of function calls between them must pass context , Or you can use WithCancel、WithDeadline、WithTimeout or WithValue The derived context created . When a context is cancelled , All contexts it derives from are also removed .
Context Interface
context.Context It's an interface , This interface defines four methods to be implemented . The specific signature is as follows :
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{
}
Err() error
Value(key interface{
}) interface{
}
}
among :
DeadlineMethod needs to return the currentContextTime cancelled , That's the deadline to finish the work (deadline);DoneMethod needs to return aChannel, This Channel Closes after the current work is completed or the context is cancelled , Multiple callsDoneMethod will return the same Channel;ErrMethod returns the currentContextReason for closure , It will onlyDoneBack to Channel Non empty value will be returned when it is closed ;- If at present
ContextIt will return when cancelledCancelederror ; - If at present
ContextTimeout will returnDeadlineExceedederror ;
- If at present
ValueMethod fromContextValue corresponding to return key in , For the same context , Multiple callsValueAnd pass in the sameKeyWill return the same result , This method is only used to pass data across API Data between processes and request domain ;
Background() and TODO()
Go Built in two functions :Background() and TODO(), Each of these functions returns an implementation Context Interface background and todo. Our code starts with these two built-in context objects as the top level partent context, Derive more sub context objects .
Background() It is mainly used for main function 、 Initialization and test code , As Context The top level of this tree structure Context, That's the root Context.
TODO(), It doesn't know the specific usage scenarios yet , If we don't know what to use Context When , You can use this .
background and todo It's all... In essence emptyCtx Type of structure , It's a non cancellable , No deadline set , It doesn't carry any value Context.
With Series of functions
Besides ,context The package also defines four With Series of functions .
WithCancel
WithCancel The function signature of is as follows :
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
WithCancel Return with new Done Copy of the parent node of the channel . When the call returns cancel Function or when closing the parent context Done Channel time , Will close... That returns the context Done passageway , Whatever happens first .
Canceling this context frees the resources associated with it , So the code should be called as soon as the operation that runs in this context is completed cancel.
func gen(ctx context.Context) <-chan int {
dst := make(chan int)
n := 1
go func() {
for {
select {
case <-ctx.Done():
return // return End this goroutine, Prevent leakage
case dst <- n:
n++
}
}
}()
return dst
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // When we get the integer we need, we call it cancel
for n := range gen(ctx) {
fmt.Println(n)
if n == 5 {
break
}
}
}
In the sample code above ,gen Function in a separate goroutine Generate integers in and send them to the return channel . gen After using the generated integer, the caller of needs to cancel the context , lest gen Inside the startup goroutine Leakage .
WithDeadline
WithDeadline The function signature of is as follows :
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
Returns a copy of the parent context , And will deadline Adjust to no later than d. If the parent context is deadline It's earlier than d, be WithDeadline(parent, d) Semantically equivalent to the parent context . When the deadline expires , When the call returns cancel Function time , Or when the parent context Done When the channel is closed , Returns the context of Done The passage will be closed , The first thing that happens .
Canceling this context frees the resources associated with it , So the code should be called as soon as the operation that runs in this context is completed cancel.
func main() {
d := time.Now().Add(50 * 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("overslept")
case <-ctx.Done():
fmt.Println(ctx.Err())
}
}
In the above code , Defined a 50 Expired in milliseconds deadline, Then we call context.WithDeadline(context.Background(), d) Get a context (ctx) And a cancellation function (cancel), Then use a select Keep the main program waiting : wait for 1 Print in seconds overslept Quit or wait ctx Exit after expiration .
In the example code above , because ctx 50 It will expire in milliseconds , therefore ctx.Done() Will receive first context Expiration notice , And it will print ctx.Err() The content of .
WithTimeout
WithTimeout The function signature of is as follows :
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
WithTimeout return WithDeadline(parent, time.Now().Add(timeout)).
Canceling this context frees the resources associated with it , So the code should be called as soon as the operation that runs in this context is completed cancel, Usually used for database or network connection timeout control . Specific examples are as follows :
package main
import (
"context"
"fmt"
"sync"
"time"
)
// context.WithTimeout
var wg sync.WaitGroup
func 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")
}
WithValue
WithValue Function to connect the data of the request scope with Context Objects build relationships . The statement is as follows :
func WithValue(parent Context, key, val interface{
}) Context
WithValue Returns a copy of the parent node , with key The associated value is val.
Only on API Use context values to pass data from the request domain to the process , Instead of using it to pass optional arguments to a function .
The keys provided must be comparable , And it shouldn't be string Type or any other built-in type , To avoid using context conflicts between packages .WithValue Users of should define their own types for keys . To avoid being assigned to interface{} When it comes to distribution , Context keys usually have specific types struct{}. perhaps , The static type of the exported context key variable should be a pointer or interface .
package main
import (
"context"
"fmt"
"sync"
"time"
)
// context.WithValue
type TraceCode string
var wg sync.WaitGroup
func worker(ctx context.Context) {
key := TraceCode("TRACE_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, trace 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 done!")
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("TRACE_CODE"), "12512312234")
wg.Add(1)
go worker(ctx)
time.Sleep(time.Second * 5)
cancel() // Notifier goroutine end
wg.Wait()
fmt.Println("over")
}
Use Context Precautions for
- It is recommended to display and pass in the form of parameters Context
- With Context Function method as parameter , Should put the Context As the first parameter .
- Pass to a function method Context When , Do not pass nil, If you don't know what to deliver , Just use context.TODO()
- Context Of Value The relevant method should pass the necessary data of the request domain , Should not be used to pass optional parameters
- Context It's thread safe , You can rest assured in more than one goroutine In the transfer
Example of client timeout cancellation
Call the server API How to realize the timeout control on the client ?
server End
// context_timeout/server/main.go
package main
import (
"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(":8000", nil)
if err != nil {
panic(err)
}
}
client End
// context_timeout/client/main.go
package main
import (
"context"
"fmt"
"io/ioutil"
"net/http"
"sync"
"time"
)
// client
type respData struct {
resp *http.Response
err error
}
func doCall(ctx context.Context) {
transport := http.Transport{
// Frequent requests can define global client Object and enable long links
// Requests don't use short links frequently
DisableKeepAlives: true, }
client := http.Client{
Transport: &transport,
}
respChan := make(chan *respData, 1)
req, err := http.NewRequest("GET", "http://127.0.0.1:8000/", nil)
if err != nil {
fmt.Printf("new requestg failed, err:%v\n", 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("client.do resp:%v, err:%v\n", resp, err)
rd := &respData{
resp: resp,
err: err,
}
respChan <- rd
wg.Done()
}()
select {
case <-ctx.Done():
//transport.CancelRequest(req)
fmt.Println("call api timeout")
case result := <-respChan:
fmt.Println("call server api success")
if result.err != nil {
fmt.Printf("call server api failed, 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)
}
Reference link :https://www.liwenzhou.com/posts/Go/go_context/
边栏推荐
- 【摸鱼神器】UI库秒变低代码工具——表单篇(一)设计
- Bs-gx-018 student examination system based on SSM
- 管理人员应具备的基本素质
- 【ROS进阶篇】第三讲 ROS文件系统与分布式通信
- Alibaba product details API interface (item_get- get product details interface), Alibaba API interface
- Final review [microcomputer principle]
- tmux设置
- Change detection and batch update
- 唯品会关键词搜索API接口(item_search-按关键字搜索唯品会商品API接口),唯品会API接口
- Yolov6 training your own data record +yolov5 comparison test
猜你喜欢

Advances in computational imaging

Explain PBR texture maps

Water polo chart - using dynamic ripples to show percentages

yolov6训练自己的数据记录+yolov5对比测试

PostgreSQL每周新闻—6月22日

Application of VoIP push in overseas audio and video services
![Navigation exercises [microcomputer principles] [exercises]](/img/79/8311a409113331e72f650a83351b46.png)
Navigation exercises [microcomputer principles] [exercises]

verilog实现DDS波形发生器模块,可实现频率、相位可调,三种波形

Gstreamer应用开发实战指南(五)

ads131a04 ADC verilog实现及仿真
随机推荐
报表交付工程师
VHDL电话计费系统设计
Recruit | DBA Data Engineer every week with an annual salary of 35+. Dream of Kyushu and bright stars!
Application of VoIP push in overseas audio and video services
ads131a04 ADC verilog实现及仿真
Bs-gx-018 student examination system based on SSM
[cloud native practice] kubesphere practice - Multi tenant system practice
A. Beat The Odds
双目立体视觉摄像头的标定、矫正、世界坐标计算(opencv)
LeetCode 1. Sum of two numbers
Alibaba keyword search commodity API interface (item_search- commodity search interface by keyword), Alibaba Search API interface
Bs-gx-017 online examination management system based on SSM
Matlab adds noise / disturbance to data
varnish 503 no backend connection – varnish健康检查
[advanced ROS chapter] lesson 2 custom header and source file encapsulation
量子机器学习的基础和应用:一个简明文献综述
About Effect Size
Visual analysis and display effect of summer data
导航 习题【微机原理】【习题】
HAproxy + Keepalive实现LDAP代理服务