MST

星途 面试题库

面试题:Go语言中context的深入理解与应用

请阐述Go语言中context包的设计目的、核心接口及其作用。假设你正在开发一个微服务,其中涉及多个嵌套的函数调用,如何使用context来实现请求的取消、超时控制以及传递请求范围的数据?请给出具体的代码示例。
20.4万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

Go语言中context包的设计目的

context包的设计目的主要是在多个goroutine构成的工作流中,传递截止时间、取消信号等上下文信息,以确保在程序结束或发生特定情况时,所有相关的goroutine能被安全、高效地取消或结束,同时还能在goroutine之间传递请求范围的数据。

核心接口及其作用

  1. Context接口
    • 是所有上下文的基接口,定义了四个方法:
      • Deadline方法返回当前上下文的截止时间,oktrue表示设置了截止时间,当超过这个时间,上下文应该被取消。
      • Done方法返回一个通道,当上下文被取消或者超时时,该通道会被关闭。用于通知相关的goroutine停止工作。
      • Err方法返回上下文被取消的原因,如果Done通道未关闭,返回nil;如果Done通道已关闭,根据不同的取消原因返回相应的错误(如context.Canceled表示被手动取消,context.DeadlineExceeded表示超时)。
      • Value方法用于从上下文中获取键值对数据,主要用于在goroutine之间传递请求范围的数据。
  2. CancelFunc类型
    • 是一个函数类型,调用该函数可以取消对应的上下文。通常通过context.WithCancelcontext.WithTimeoutcontext.WithDeadline函数创建上下文时返回。

在微服务中使用context实现相关功能及代码示例

  1. 请求取消
package main

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

func worker(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("Worker received cancel signal, exiting...")
            return
        default:
            fmt.Println("Worker is working...")
            time.Sleep(1 * time.Second)
        }
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    go worker(ctx)

    time.Sleep(3 * time.Second)
    cancel()
    time.Sleep(1 * time.Second)
}
  1. 超时控制
package main

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

func worker(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("Worker received cancel signal (timeout), exiting...")
            return
        default:
            fmt.Println("Worker is working...")
            time.Sleep(1 * time.Second)
        }
    }
}

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

    go worker(ctx)

    time.Sleep(5 * time.Second)
}
  1. 传递请求范围的数据
package main

import (
    "context"
    "fmt"
)

func processRequest(ctx context.Context) {
    value := ctx.Value("key")
    if value != nil {
        fmt.Printf("Received value from context: %v\n", value)
    }
}

func main() {
    ctx := context.WithValue(context.Background(), "key", "value")
    processRequest(ctx)
}

在实际微服务开发中,对于嵌套的函数调用,可以将context对象作为参数在函数间传递,从而在整个调用链中实现取消、超时控制以及数据传递。例如:

package main

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

func innerFunction(ctx context.Context) {
    select {
    case <-ctx.Done():
        fmt.Println("Inner function received cancel signal, exiting...")
        return
    default:
        fmt.Println("Inner function is working...")
        time.Sleep(1 * time.Second)
    }
}

func outerFunction(ctx context.Context) {
    innerFunction(ctx)
}

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

    outerFunction(ctx)
}