Usage Scenarios
There are three main usage scenarios for Context
- Passing timeout information, which is most used.
- Passing signals, used for message notification, handling multi-process communication
- Passing data, commonly used in the framework layer trace-id, metadata
Let’s take an example of etcd watch to get a better understanding.
|
|
First the child ctx and cancel functions are generated based on the parent ctx passed in as an argument. Then watch passes in child ctx, and if parent ctx is cascaded by cancel, child ctx is cascaded by cancel, rch is closed by etcd, and then the for loop goes to the select logic, at which point child ctx is cancelled. So <-ctx.Done() takes effect and the watch function returns
The context makes it possible for multiple goroutines to collaborate and manage timeouts, which greatly simplifies development work. This is the beauty of Go
Principle
Context is an interface
Deadlinectx returns this value if it is closed at a certain point in time. Otherwise ok is falseDonereturns a channel that will be closed if it times out or is cancelled, enabling message communication.ErrReturns an error if the current ctx has timed out or been cancelled.ValueReturns a value based on a key, similar to a dictionary.
Current implementations are emptyCtx , valueCtx , cancelCtx , timerCtx . Child Context can be generated based on a Parent.

After multiple derivations, ctx is a multinomial tree-like structure. When ctx-1 is canceled, the whole tree with ctx-1 as root is cascaded and canceled, but the original root, ctx2 ctx3, is not affected.
|
|
First detect the done channel, if someone is listening, then close it, then all goroutines waiting for this ctx will receive the message.
Then it iterates through the children map, canceling all children in turn, similar to the prior order traversal of a tree. Finally, removeFromParent removes itself from the parent node.
A few questions
prints Ctx
Using WithCancel as an example, you can see that child also references parent, and with the propagateCancel function, parent also references child (when parent is of type cancelCtx).
|
|
If ctx is printed at this point, the String() method will be called recursively, and the key/value will be printed. If the value is non-thread-safe at this point, such as map, it will raise a concurrent read and write panic.
This case is an implementation of the http standard library server.go:2906 line of code that saves the http server to the ctx.
|
|
The final call to the business layer code passes the ctx to the user.
|
|
If you print ctx at this point, you will print the http srv structure, which contains the map. If you are interested, you can do an experiment and take the ab pressure test to easily reproduce it.
|
|

Also note that go has since made a partial fix for this, which has somewhat solved the problem. But also remember not to print ctx.
Key/Value type is not safe
Strongly do not use Context to pass too much data, here you can see that key / value types are interface{} , compile-time can not determine the type, run-time need to assert, there are performance and security issues
closes the underlying connection
The Context timeout triggers the http pool to close the underlying connection, resulting in frequent connection rebuilding.
The problem is that if the application layer reads it and then drops it, the connection is still available, but if the OS tcp stack handles the useless data, it closes directly. grpc doesn’t have this problem because it is multiplexed and each request is a virtual stream, so if it times out, it just closes the stream, not the underlying tcp connection
Doubly linked list
When Context is derived from more layers, it forms a doubly linked list, and key / value fetching is likely to degenerate into an O(N) operation, which is very slow
|
|
Whenever a key / value is added a new valueCtx is generated, and when queried, if the current ctx does not have a key, the c.Context is queried recursively.
timeout in advance
When the call stack is deep, it is easy to generate this situation when multiple people cooperate. In fact, still do not understand how ctx cancel works, asynchronous go out of the business logic needs to be based on context.Background() and then derive child ctx, otherwise it will return early timeout.
Another point that is easy to ignore is that by default grpc will pass through the timeout, for example, if the entry A service calls B and the timeout is set to 2s, if B uses the same Context to call downstream C, then the timeout will be subtracted from B’s own processing time. If the link is long, it is likely to time out by the time it reaches the G service
Passing the timeout can release the resources earlier, otherwise the backend is still processing the request after the entry timeout.
Custom Ctx
The reason why custom Context is very unconventional is that it is handled differently in the source code.
|
|
As you can see from the source code, there are two ways for parent to refer to child, the official cancelCtx type is saved with map. But the unofficial one needs to open goroutine to monitor it. The business code is already full of goroutines, so using them unchecked will only increase the burden on the system.
Suggestions for use
Finally, to summarize a few principles of context usage.
- Don’t use
WithValueto carry business data except at the framework level, this type isinterface{}, which cannot be determined at compile time, andasserthas overhead at runtime. If you do carry it, use thread-safe data - Be sure not to print
Context, especially if it’s derived from thehttpstandard library, who knows what’s in it Contextis usually passed as the first argument to a function, but if the life cycle ofContextis equivalent to that of a structure, it’s fine to treat it as a structure member- Don’t customize user-level
Contextif possible, unless the benefits are huge - Asynchronous goroutine logic uses
Contextto be clear about who is still holding it and whether it will time out early, especially when calling rpc, db, redis. - The derived child ctx must be used with defer cancel() to free up resources.