MST

星途 面试题库

面试题:Go的context在处理嵌套调用时如何避免资源泄漏

在一个复杂的分布式系统的Go代码中,存在多层嵌套的函数调用,每个函数可能会启动新的goroutine并使用context来管理请求。请分析在这种场景下可能出现资源泄漏的情况有哪些,以及如何通过合理使用context的WithCancel、WithTimeout等函数来避免资源泄漏。请结合具体的业务场景(比如一个涉及数据库查询、RPC调用等多层操作的场景)进行说明。
44.2万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

可能出现资源泄漏的情况

  1. goroutine未正确结束
    • 在多层嵌套函数调用中,如果内层函数启动了goroutine,但外层函数提前返回,而该goroutine没有收到结束信号,它可能会一直运行,占用系统资源。例如,在数据库查询场景中,一个函数启动goroutine来执行数据库查询,但在查询结果返回前,外层函数因为其他原因提前返回,这个goroutine可能持续占用数据库连接资源。
    • 假设在一个处理用户订单的业务场景中,订单处理函数调用内层函数来检查库存,内层函数启动goroutine与库存服务进行RPC调用。如果订单处理函数在库存检查未完成时因订单过期等原因提前返回,库存检查的goroutine可能继续运行,占用与库存服务的连接资源。
  2. 未释放上下文相关资源
    • 如果没有正确取消context,相关的资源(如数据库连接、RPC连接等)可能不会被及时释放。例如,在使用context控制数据库事务时,如果没有在合适时机取消context,数据库事务可能一直处于打开状态,占用数据库资源。
    • 在一个电商系统的商品详情查询场景中,通过RPC调用多个微服务获取商品的详细信息。如果context没有正确取消,即使某个RPC调用超时,相关的微服务连接可能不会及时关闭,导致资源浪费。

如何通过context避免资源泄漏

  1. 使用WithCancel
    • 场景:在一个订单处理系统中,订单处理函数调用内层函数进行支付操作,支付操作启动goroutine与支付服务进行交互。
    • 代码示例
package main

import (
    "context"
    "fmt"
    "time"
)

func pay(ctx context.Context) {
    select {
    case <-ctx.Done():
        fmt.Println("Payment cancelled")
        return
    default:
        // 模拟支付操作
        time.Sleep(2 * time.Second)
        fmt.Println("Payment completed")
    }
}

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

    go pay(ctx)

    // 模拟订单处理的其他逻辑
    time.Sleep(1 * time.Second)
    // 如果订单处理逻辑提前结束,调用cancel取消goroutine
    cancel()
}
  • 解释:在processOrder函数中,创建了一个可取消的上下文ctx和取消函数cancel。当订单处理逻辑提前结束时,调用cancel函数,pay函数中的ctx.Done()通道会收到信号,从而结束支付操作的goroutine,避免资源泄漏。
  1. 使用WithTimeout
    • 场景:在商品详情查询中,需要通过RPC调用多个微服务获取商品图片、描述等信息。假设每个RPC调用都有一个合理的超时时间。
    • 代码示例
package main

import (
    "context"
    "fmt"
    "time"
)

func getProductImage(ctx context.Context) {
    select {
    case <-ctx.Done():
        fmt.Println("Image retrieval cancelled due to timeout")
        return
    default:
        // 模拟图片获取操作
        time.Sleep(3 * time.Second)
        fmt.Println("Product image retrieved")
    }
}

func getProductDetails() {
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel()

    go getProductImage(ctx)

    // 模拟其他商品详情获取逻辑
    time.Sleep(2 * time.Second)
}
  • 解释:在getProductDetails函数中,创建了一个带有2秒超时的上下文ctx和取消函数cancel。如果getProductImage函数在2秒内未完成,ctx.Done()通道会收到信号,从而取消图片获取操作的goroutine,避免长时间占用与图片服务的连接资源。