MST

星途 面试题库

面试题:Go语言Context在复杂业务场景下的应用

假设你正在开发一个微服务,该微服务需要调用多个下游服务,每个下游服务的调用都在独立的Goroutine中执行。如果其中一个调用超时,你需要取消所有正在执行的Goroutine。请描述如何使用Context来设计和实现这样的功能,并分析可能遇到的问题及解决方案。
18.1万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

使用Context设计与实现

  1. 创建顶层Context:在调用微服务的入口处,创建一个带有取消功能的顶层Context,例如使用context.WithCancelcontext.WithTimeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
  1. 传递Context:将这个Context传递给每个在Goroutine中执行的下游服务调用函数。
var wg sync.WaitGroup
for _, service := range downstreamServices {
    wg.Add(1)
    go func(svc Service) {
        defer wg.Done()
        err := svc.Call(ctx)
        if err != nil {
            // 处理错误
        }
    }(service)
}
  1. 在下游服务调用中检查Context:在每个下游服务调用函数内部,定期检查Context的取消信号。例如,在进行HTTP请求时,可以将Context传递给HTTP客户端。
func (s *Service) Call(ctx context.Context) error {
    req, err := http.NewRequestWithContext(ctx, "GET", s.url, nil)
    if err != nil {
        return err
    }
    resp, err := s.client.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    // 处理响应
    return nil
}
  1. 超时处理:如果顶层Context设置了超时,当超时发生时,所有传递了该Context的Goroutine会收到取消信号,它们在检查Context时会提前结束操作。

可能遇到的问题及解决方案

  1. 资源清理
    • 问题:Goroutine在收到取消信号后,可能没有正确清理资源,如文件描述符、数据库连接等。
    • 解决方案:在Goroutine结束前,确保调用相应的资源清理函数。例如,对于文件操作,使用defer file.Close();对于数据库连接,使用defer db.Close()
  2. 同步问题
    • 问题:当一个Goroutine超时取消后,其他Goroutine可能还在执行中,导致数据不一致或状态混乱。
    • 解决方案:使用sync.WaitGroup来等待所有Goroutine完成,并且在取消时确保所有Goroutine都能正确响应取消信号。在Goroutine内部,在接收到取消信号后,尽快结束业务逻辑,并通过wg.Done()通知等待组。
  3. 嵌套Context
    • 问题:如果在下游服务调用中又创建了新的Context,并且没有正确传递取消信号,可能导致子Context不会被取消。
    • 解决方案:始终使用从上层传递下来的Context,如果需要创建新的Context,确保使用WithCancelWithTimeout等函数基于上层Context创建,以保证取消信号能正确传递。例如childCtx, childCancel := context.WithCancel(parentCtx)