面试题答案
一键面试数据结构本身的优化
- 精简携带数据:
- context主要用于携带请求范围的数据,如请求ID、认证信息等。只在context中传递真正必要的数据,避免放入大量不必要的缓存数据等。例如,如果只是为了在整个请求链路中追踪请求ID,那么context中只放入请求ID即可,而不是放入整个用户的详细信息。
- 示例:
type RequestIDKey struct{} reqID := "123456" ctx := context.WithValue(context.Background(), RequestIDKey{}, reqID)
- 避免重复创建:
- 尽量复用已有的context实例,避免在每次函数调用时都重新创建一个全新的context。如果只是需要传递一个额外的值,可以基于已有的context使用
context.WithValue
。例如在中间件中,可能会基于传入的ctx
添加一些值后再传递给下一个处理函数。 - 示例:
func middleware(ctx context.Context) context.Context { newCtx := context.WithValue(ctx, "key", "value") return newCtx }
- 尽量复用已有的context实例,避免在每次函数调用时都重新创建一个全新的context。如果只是需要传递一个额外的值,可以基于已有的context使用
传递过程中的优化策略
- 尽早传递:
- 在函数调用链的起始处就将context传递下去,避免在中间函数层层传递过程中临时创建和传递。这样可以确保所有需要context的函数在一开始就能获取到完整的上下文信息,减少由于延迟传递导致的不必要的上下文创建和数据传递。
- 示例:
func main() { ctx := context.Background() result, err := topLevelFunction(ctx) if err!= nil { // 处理错误 } // 处理result } func topLevelFunction(ctx context.Context) (string, error) { // 调用下层函数并传递ctx return middleLevelFunction(ctx) } func middleLevelFunction(ctx context.Context) (string, error) { // 调用更下层函数并传递ctx return bottomLevelFunction(ctx) } func bottomLevelFunction(ctx context.Context) (string, error) { // 使用ctx return "result", nil }
- 减少不必要传递:
- 如果某个函数不需要使用context中的任何数据,就不要传递context。这样可以减少函数参数列表的长度,也减少了不必要的context传递开销。例如一些简单的工具函数,如计算两个整数和的函数,通常不需要context。
- 示例:
func add(a, b int) int { return a + b }
- 使用context取消机制合理释放资源:
- 在高并发场景下,当请求取消时,通过context的取消机制及时释放相关资源。例如在启动一个goroutine处理任务时,将context传递进去,当上层context取消时,该goroutine能及时感知并停止工作,释放资源。
- 示例:
func worker(ctx context.Context) { for { select { case <-ctx.Done(): return default: // 执行任务 } } } func main() { ctx, cancel := context.WithCancel(context.Background()) go worker(ctx) // 一段时间后取消 cancel() }