Go net/rpc 本身不支持限速,需通过包装 net.Listener 实现连接频次限制,或包装 net.Conn 在 Read() 中按 RPC 帧限速;精确 QPS 限速应改用 gRPC + 拦截器。
net/rpc 本身不支持限速,必须自己加中间层Go 标准库的 net/rpc 是一个轻量级、无状态的 RPC 框架,它不提供任何请求频率控制能力。限速逻辑不能写在服务端方法内部(那样会污染业务),也不能依赖 HTTP 中间件(因为 net/rpc 默认走 TCP,不经过 HTTP 栈)。真正可行的方式是在连接建立后、请求分发前插入限速器。
net.Listener 包装器实现连接级限速最干净的做法是包装 net.Listener,在 Accept() 返回连接前做速率判断。这样能控制新连接建立频率,适合防御突发连接洪峰。注意:这不是“每秒请求数”(QPS)限速,而是“每秒新建连接数”限制。
golang.org/x/time/rate.Limiter 配合 net.Listener 实现Accept() 中阻塞太久,建议设置超时或使用非阻塞 TryAccept 类逻辑type RateLimitedListener struct {
net.Listener
limiter *rate.Limiter
}
func (l *RateLimitedListener) Accept() (net.Conn, error)
{
if !l.limiter.Allow() {
return nil, errors.New("connection rejected: rate limit exceeded")
}
return l.Listener.Accept()
}
rpc.ServeConn 前拦截并限速单连接内请求如果要控制单个 TCP 连接内的请求处理速度(比如防止一个恶意客户端复用连接狂发请求),就得在 rpc.ServeConn 调用前包装 net.Conn。核心是重写 Read() 方法,在每次读到完整 RPC 帧(含 header + body)后再触发一次限速检查。
net/rpc 使用自定义二进制协议,每个请求以 4 字节长度头开始,需先读头再读体net.Conn 并在 Read() 中做 limiter.WaitN(ctx, n) 可控但复杂jsonrpc2 或 gRPC 等带上下文和中间件能力的框架grpc-middleware 实现 QPS 限速如果你的场景真需要精确到每秒 N 次方法调用的限速(比如 User.Create 接口限 100 QPS),net/rpc 不是合适选择。gRPC 天然支持拦截器,配合 go.uber.org/ratelimit 或 golang.org/x/time/rate 很容易实现。
status.Errorf(codes.ResourceExhausted, "...")
limiter.Allow() 要放在每次 Recv() 后而非连接建立时func rateLimitInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
if !limiter.Allow() {
return nil, status.Errorf(codes.ResourceExhausted, "rate limit exceeded")
}
return handler(ctx, req)
}
真正难的不是写限速代码,而是确定限速维度和阈值——同一接口对内部系统和外部 API 的容忍度可能差十倍;而误把健康检查探针当成恶意请求限掉,这种事在灰度期特别常见。