面试题答案
一键面试思路
- 创建带取消功能的context:在发起服务调用链时,创建一个
context.Context
并绑定一个取消函数cancel
。这样在需要时,可以通过调用cancel
函数来取消整个调用链。 - 传递context:将创建的
context
传递到每个子服务调用中,确保每个子服务都能感知到取消信号。 - 设置超时:使用
context.WithTimeout
函数为整个调用链或每个子服务设置超时时间。当超时发生时,自动触发取消操作。 - 资源释放:在每个子服务内部,监听
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("所有子服务执行完毕")
}
在上述代码中:
context.WithTimeout
创建了一个带有3秒超时的context
,同时返回取消函数cancel
。subService1
和subService2
内部通过select
语句监听ctx.Done()
信号。一旦接收到取消信号,打印释放资源的信息并返回错误。main
函数中按顺序调用子服务,并在函数结束时通过defer cancel()
确保即使发生错误,也能及时取消所有操作并释放资源。如果某个子服务执行超时,context
会触发取消,后续子服务会接收到取消信号并释放资源。