MST

星途 面试题库

面试题:Go 中 Go Context 在简单请求链中的应用

假设你正在开发一个简单的 HTTP 服务,该服务需要调用两个下游 API。请使用 Go Context 来管理这个请求链的生命周期,确保在客户端取消请求或整个操作超时时,所有正在进行的 API 调用都能被正确取消。请写出关键代码片段并简要说明思路。
12.0万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
package main

import (
    "context"
    "fmt"
    "net/http"
    "time"
)

// 模拟调用下游 API1
func callAPI1(ctx context.Context) error {
    select {
    case <-time.After(2 * time.Second):
        fmt.Println("API1 call completed")
        return nil
    case <-ctx.Done():
        fmt.Println("API1 call cancelled")
        return ctx.Err()
    }
}

// 模拟调用下游 API2
func callAPI2(ctx context.Context) error {
    select {
    case <-time.After(3 * time.Second):
        fmt.Println("API2 call completed")
        return nil
    case <-ctx.Done():
        fmt.Println("API2 call cancelled")
        return ctx.Err()
    }
}

func httpHandler(w http.ResponseWriter, r *http.Request) {
    // 创建带有超时的 context
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    // 创建一个 channel 用于接收 API 调用结果
    resultCh := make(chan error, 2)

    // 启动 goroutine 调用 API1
    go func() {
        resultCh <- callAPI1(ctx)
    }()

    // 启动 goroutine 调用 API2
    go func() {
        resultCh <- callAPI2(ctx)
    }()

    // 等待所有 API 调用完成或 context 取消
    for i := 0; i < 2; i++ {
        select {
        case err := <-resultCh:
            if err != nil {
                fmt.Println("API call failed:", err)
                w.WriteHeader(http.StatusInternalServerError)
                return
            }
        case <-ctx.Done():
            fmt.Println("Request cancelled or timed out")
            w.WriteHeader(http.StatusRequestTimeout)
            return
        }
    }

    fmt.Println("All API calls completed successfully")
    w.WriteHeader(http.StatusOK)
}

func main() {
    http.HandleFunc("/", httpHandler)
    fmt.Println("Server is listening on :8080")
    http.ListenAndServe(":8080", nil)
}

思路说明

  1. 创建带有超时的 context:使用 context.WithTimeout 创建一个带有 5 秒超时的 context。当客户端取消请求(例如关闭连接)或整个操作超时,该 context 会被取消。
  2. 在 API 调用中使用 context:在 callAPI1callAPI2 函数中,通过 select 语句监听 ctx.Done() 通道。如果该通道接收到数据,说明 context 被取消,此时取消正在进行的 API 调用并返回错误。
  3. 并发调用 API:在 httpHandler 函数中,通过 goroutine 并发调用 callAPI1callAPI2,并将结果发送到 resultCh 通道。
  4. 等待结果或 context 取消:通过 for 循环和 select 语句等待所有 API 调用完成或 context 取消。如果任何一个 API 调用失败或 context 取消,立即返回相应的 HTTP 状态码。