当前位置:网站首页>Go context basic introduction

Go context basic introduction

2022-07-04 09:39:00 Dynamic for a while, reconstructing the crematorium

stay Go1.7 Introduced in context, contain goroutine Running state 、 Environmental Science 、 Information about the scene . And gradually become concurrency control 、 Standard practice of timeout control .

Why would there be context?

stay Go Of server Usually, each request will start several goroutine Work at the same time ( Some go to the database to get data , Some call the downstream interface to obtain relevant data ). And these goroutine You need to share the basic data of this request , For example, log in token、 The maximum timeout for processing requests, etc .

So we can Go server As a co process model . But there is no such model context There may be some problems . For example, during business peak , The response of downstream services is slow , The current system request has no timeout control , Then there will be more and more coordination processes waiting for downstream Services , Finally, memory usage soared , Even lead to service unavailability accidents .

For this accident , In fact, simply setting the maximum downstream processing time can be avoided . and context It is very conducive to this setting .

context Can be in a group goroutine Pass shared values in 、 Cancel message 、deadline etc. .

Local global variables

stay 《 The code of 》 It points out several disadvantages of global variables

  • Inadvertent modification
  • name conflict
  • Strange alias
  • Concurrency issues
  • The initialization sequence cannot be guaranteed
  • Hindering code reuse
  • It interferes with modularity

Personally, I think the most serious problem of global variables should be coupling . Others are relatively easy to say

But global variables also have the advantage of improving the scope of data .

So many languages design variables that are not so global . such as Java Medium ThreadLocal, Global within the thread , Avoid thread conflicts . Another example Go Medium context

about context Directly reflect this point is WithValue, And for WithTimeout、WithCancel In fact, it is also a global control variable

Basic use

There are mainly two ways to create context

  • context.Backgroud(): Context default , All other contexts derive from him . Usually used for main function 、 initialization 、 Test or top-level context
  • context.TODO(): When you are not sure which context to use

Both are actually type emptyCtx int

In practice, we still need to pass the following four With Method to derive

// WithCancel Return a copy of the parent process , And there is a new Done channel. When returned cancel Function called or parent context Done When the channel is closed , Returns the context of Done The passage will be closed , Whichever occurs first .
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)

// WithDeadline Returns a copy of the parent context , The deadline is adjusted to be no later than d. If the deadline of the parent context is earlier than d, WithDeadline(parent, d) Semantically equivalent to parent.
//  When the deadline expires 、 Call returned cancel Function or parent context Done When the channel is closed , Returns the context of Done The passage will be closed , Whichever comes first .
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
    
	return WithDeadline(parent, time.Now().Add(timeout))
}

// WithValue Return a copy of the parent element , Where the value associated with the key is val.
// Context values are only used to pass processes and api Data within the scope of the request , Instead of passing optional parameters to functions .
// The keys provided must be comparable , Should not be string Type or any other built-in type , To avoid using context Conflicts between packages . Use WithValue Users of should define their own key types . In the interface {} assignment , To avoid distribution , Context keys usually have specific types struct{}. in addition , The static type of the exported context key variable should be a pointer or interface .
func WithValue(parent Context, key, val interface{
    }) Context

Transfer shared data

Often used for some “ Global variables ”. For example, the following code sends the request reqID, Because after that, the code basically needs to use , So you can use context

Personal opinions are actually unnecessary context Transfer shared data . If the code is functionally cohesive, it can be abstracted into structure , Use shared data as a structure . If there are not many transmission links , You can also send parameters directly . If it is “ overall situation ” Shared variables , That can also be done through chan Or other ways to share , Of course context It is also one of other methods .

func TestContextPassValue(t *testing.T) {
    
	handler := withRequestID(http.HandlerFunc(handle))
	http.ListenAndServe("/", handler)
}

const reqKey = 0

func withRequestID(next http.Handler) http.Handler {
    
	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
    
		// fetch request id from header
		rid := req.Header.Get("X-Request-ID")
		ctx := context.WithValue(req.Context(), reqKey, rid)
		req = req.WithContext(ctx)
		next.ServeHTTP(rw, req)
	})
}

func handle(rw http.ResponseWriter, req *http.Request) {
    
	reqID := req.Context().Value(reqKey)
    ...
}

Cancel goroutine

Someone once said context The real solution is to cancel . The three uses mentioned in this article , There are often other and even better ways to transfer shared data ; prevent goroutine Let the cat out of the , In fact, it is also a kind of cancellation goroutine To achieve . But cancel the function of downstream Services , only context It can be solved in the simplest way .

prevent goroutine Let the cat out of the

Suppose we are working on a chat software , It has real-time scanning of user folders to protect “ Security ” The function of .

func imApp() {
    
	close:=make(chan struct{
    })
	go scanFiles()
	
	select {
    
	case <-close:
		return
	}
}

func scanFiles() {
    
	// scan user file
}

At this time, the user informs us that app close , Then maybe app Other functions are turned off , But scanning files goroutine It's been running .

At this time, in order to prevent this kind of situation , have access to context To prevent this infinite operation goroutine Let the cat out of the .

func imApp() {
    
	close := make(chan struct{
    })

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	go scanFiles(ctx)

	select {
    
	case <-close:
		return
	}
}

func scanFiles(ctx context.Context) {
    
	select {
    
	case <-ctx.Done():
		return
	default:
		// scan user file partly
	}
}

This can be cancelled at most , I scanned it again .

Use with caution context

context Although very good , Perfect for writing server, Especially for cancellation . But if it is abused everywhere , It may spread like a virus . also context It is actually a linked list implementation , The efficiency is relatively low .

Ref

  1. https://zhuanlan.zhihu.com/p/68792989
  2. https://www.cnblogs.com/qcrao-2018/p/11007503.html
  3. https://www.zhihu.com/question/269905592/answer/364438511
  4. https://faiface.github.io/post/context-should-go-away-go2/
原网站

版权声明
本文为[Dynamic for a while, reconstructing the crematorium]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/185/202207040908111375.html