net/rpc标准库的client.call和client.go完全不接收context,因此context.withtimeout无效;应改用grpc或自封装wrapper,或用time.afterfunc手动取消。

Go RPC 调用里 context.WithTimeout 不生效?检查 client 是否支持 context
标准库 net/rpc 的 Client.Call 和 Client.Go **完全不接收 context**,这是最常踩的坑。你传了 context.WithTimeout,但底层压根不读——超时永远不起作用。
实操建议:
- 改用
gRPC(原生支持context)或net/rpc/jsonrpc+ 自封装 wrapper - 若坚持用
net/rpc,只能靠time.AfterFunc手动 cancel +Client.Close()中断连接 - 第三方库如
go-kit或kitex的 RPC 客户端才默认集成context传递
gRPC 中 ctx 传到服务端后为什么 ctx.Done() 没触发?
常见现象:客户端调用时用了 context.WithTimeout,但服务端函数里 select { case 死活不进 —— 很可能服务端没把 <code>ctx 透传给下游依赖。
关键点:
立即学习“go语言免费学习笔记(深入)”;
- gRPC server 端 handler 接收的
ctx是有效的,但你调用数据库、HTTP 客户端、另一个 gRPC 服务时,必须显式把该ctx传下去 -
database/sql的QueryContext、http.Client.Do、grpc.ClientConn.Invoke都要带ctx,漏一个就断链 - 不要在 handler 里起 goroutine 并直接用原始
ctx;要用ctx = ctx显式捕获,避免变量复用导致提前取消
context.WithCancel 和 context.WithTimeout 在 RPC 链路中能混用吗?
能,但顺序和所有权要理清。典型错误是:客户端用 WithTimeout,服务端又用 WithCancel 新建子 context,结果父 context 超时后子 context 还活着。
正确做法:
- 全链路只用一个源头
ctx(通常是 client 发起时创建),所有中间层只做WithValue或WithDeadline衍生,不新建WithCancel - 只有需要主动取消(比如用户点击“停止”)时,才在某一层调用
cancel();此时整个链路都会收到ctx.Done() -
WithTimeout底层就是WithDeadline+ 定时器,它和WithCancel是正交能力,但别在服务端无故覆盖 client 传来的ctx
为什么加了 context,RPC 调用反而变慢甚至卡死?
不是 context 本身慢,而是误用引发阻塞:比如在 select 里只监听 ctx.Done(),却忘了处理业务 channel 的发送/接收,导致 goroutine 泄漏或死锁。
高频问题:
- 用
select等待多个 channel 时,漏写default或非阻塞尝试,让 goroutine 卡在某个分支 - 把
ctx传给同步阻塞函数(如未设 timeout 的time.Sleep、无缓冲 channel 的 send),它不会响应取消 - 在 HTTP handler 或 gRPC 方法里启动大量 goroutine,每个都持有一个 long-lived
ctx,但没做并发控制,压垮服务
真正难的不是加 context,是每一层都得想清楚:这个操作是否可取消?取消后资源怎么清理?channel 关了没?连接关了没?goroutine 退了没?










