在开发过程中,我们可能经常遇到需要异步处理的任务,主协程可能不会等待异步任务的完成就结束,传递给异步任务的是主协程的context,主协程退出导致context被cancel, 影响异步任务。
如果简单的给异步任务传递context.Background,可能导致一些value缺失,如trace信息。因此,需要一种no cancelable的context,能继承主协程中context的所有value,同时不继承其cancel机制,我们可以通过以下方法来实现。
// PropagateContext 繁衍一个context,取消oldCtx中的cancel机制,且继承oldCtx中的所有value。
// 对于做一些异步操作,且不希望老context中的超时或cancel机制影响操作
// 如果oldCtx中没有traceId,会随机生成一个。
func PropagateContext(oldCtx context.Context) (newCtx context.Context) {
if oldCtx == nil {
oldCtx = context.Background()
}
traceId, ok := oldCtx.Value(keyTraceID).(string)
if traceId == "" || !ok {
traceId = fmt.Sprintf("trace%s", util.GetRandString(27))
oldCtx = context.WithValue(oldCtx, keyTraceID, traceId)
}
return contextWithoutCancel{oldCtx}
}
type contextWithoutCancel struct{ context.Context }
func (contextWithoutCancel) Deadline() (deadline time.Time, ok bool) { return }
func (contextWithoutCancel) Done() <-chan struct{} { return nil }
func (contextWithoutCancel) Err() error { return nil }