MST

星途 面试题库

面试题:Go中context在跨服务调用链路追踪时的资源管理

在Go语言跨服务调用链路追踪场景下,context不仅用于传递链路信息,还需考虑资源管理。当一个服务调用链因为某个子服务超时而中断时,如何通过context有效地释放相关资源,防止内存泄漏和资源浪费?请详细描述思路和实现方式。
47.5万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

思路

  1. 创建带取消功能的context:在发起服务调用链时,创建一个 context.Context 并绑定一个取消函数 cancel。这样在需要时,可以通过调用 cancel 函数来取消整个调用链。
  2. 传递context:将创建的 context 传递到每个子服务调用中,确保每个子服务都能感知到取消信号。
  3. 设置超时:使用 context.WithTimeout 函数为整个调用链或每个子服务设置超时时间。当超时发生时,自动触发取消操作。
  4. 资源释放:在每个子服务内部,监听 context 的取消信号。一旦接收到取消信号,立即停止正在进行的操作并释放相关资源。

实现方式

package main

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

// 模拟子服务1
func subService1(ctx context.Context) error {
    select {
    case <-ctx.Done():
        // 释放子服务1相关资源
        fmt.Println("subService1: 收到取消信号,释放资源")
        return ctx.Err()
    case <-time.After(2 * time.Second):
        fmt.Println("subService1: 执行完毕")
        return nil
    }
}

// 模拟子服务2
func subService2(ctx context.Context) error {
    select {
    case <-ctx.Done():
        // 释放子服务2相关资源
        fmt.Println("subService2: 收到取消信号,释放资源")
        return ctx.Err()
    case <-time.After(2 * time.Second):
        fmt.Println("subService2: 执行完毕")
        return nil
    }
}

func main() {
    // 创建带超时的context
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel()

    // 执行子服务调用链
    err := subService1(ctx)
    if err != nil {
        fmt.Println("subService1 执行失败:", err)
        return
    }

    err = subService2(ctx)
    if err != nil {
        fmt.Println("subService2 执行失败:", err)
        return
    }

    fmt.Println("所有子服务执行完毕")
}

在上述代码中:

  1. context.WithTimeout 创建了一个带有3秒超时的 context,同时返回取消函数 cancel
  2. subService1subService2 内部通过 select 语句监听 ctx.Done() 信号。一旦接收到取消信号,打印释放资源的信息并返回错误。
  3. main 函数中按顺序调用子服务,并在函数结束时通过 defer cancel() 确保即使发生错误,也能及时取消所有操作并释放资源。如果某个子服务执行超时,context 会触发取消,后续子服务会接收到取消信号并释放资源。