一、背景
在golang使用redis的时候,会发现redis的函数调用中都需要传入一个变量:context。
虽然这是redis-go的函数规定的,但是为什么需要传入这个变量?
除此之外,查看一些开源项目中,也经常会在一些连接,数据库操作中看见相关的参数传递。
二、为什么需要 context.Context
假设你要写一个 查询用户订单 的 API,逻辑很简单:
- 验证用户身份;
- 去数据库查订单;
- 调用物流服务拿到快递状态。
正常情况下没问题,但现实可能是这样的:
- 用户点了查询,等了一会儿,觉得太慢,直接关掉了 App;
- 服务器还在后台拼命查数据库、请求第三方接口;
- 等处理完了,结果发现用户早就不需要了。
这种情况带来的问题:
- 浪费资源:CPU、内存、数据库连接都被无效任务占着;
- 拖垮性能:一旦并发多了,系统可能被这些“僵尸请求”压垮。
所以,Go 给我们准备了一个“传话筒”——context。
只要上游任务取消了,消息会层层往下传,下游一看:哦,原来不用干了,那我立刻停掉。
这就是 context 的意义。
三 context的优点
-
超时控制 (Timeout) :你可以设置一个上下文,当操作超过特定时间后自动取消。例如,如果数据库查询耗时过长,可以通过
context自动取消,避免资源阻塞。 -
取消信号 (Cancellation) :当一个请求被取消时(比如用户关闭了网页),你可以通过
context向所有相关的下游操作(如数据库查询、网络请求)发送取消信号,让它们立即停止工作,释放资源。 -
请求范围的数据传递 (Request-scoped Data) :
context可以在函数调用链中传递一些请求特有的数据,例如请求 ID、认证信息等,而无需在每个函数签名中显式地传递这些参数。 -
可追踪性 (Traceability) :在分布式系统中,
context常用于传递跟踪 ID(Trace ID),以便将一个请求在不同服务间的调用串联起来,便于问题排查。
package main
import (
"context"
"fmt"
"time"
"github.com/redis/go-redis/v9"
)
func main() {
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
// 设置一个 1 秒的超时
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
val, err := rdb.Get(ctx, "user:1001").Result()
if err != nil {
fmt.Println("查询失败:", err)
return
}
fmt.Println("查询结果:", val)
}