You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为gRPC Unary调用添加请求限流 实现单用户每秒最多10次请求

gRPC Unary 调用单用户限流实现方案

你调研的通过自定义 Interceptor 实现限流的方案是完全可行的,以下是可直接落地的实现逻辑:

实现核心逻辑

需要两个核心模块配合:

  • 从 gRPC 请求元数据中获取唯一用户标识(通常是请求头里的用户ID、Token解析后的用户ID等)
  • 为每个用户维护独立的请求时间窗口与计数,超过阈值直接返回限流响应

完整示例代码

using Grpc.Core;
using Grpc.Core.Interceptors;
using System.Collections.Concurrent;

public class LimitClientRequestsInterceptor : Interceptor
{
    // 存储每个用户的请求计数与时间窗口起点,Key为用户唯一标识,Value为(请求数, 窗口开始时间)
    private static readonly ConcurrentDictionary<string, (int Count, DateTime WindowStart)> _userRequestLimit = new();
    // 限流阈值:10次/秒
    private const int _maxRequestsPerSecond = 10;
    private const int _windowSeconds = 1;

    public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
        TRequest request,
        ServerCallContext context,
        UnaryServerMethod<TRequest, TResponse> continuation)
    {
        // 1. 从请求头中获取用户唯一标识,此处以自定义头x-user-id为例,可根据实际业务替换为Token解析等逻辑
        var userId = context.RequestHeaders.Get("x-user-id")?.Value;
        if (string.IsNullOrEmpty(userId))
        {
            // 无用户标识的情况可自行处理,比如统一按匿名用户限流,或者直接拒绝请求
            throw new RpcException(new Status(StatusCode.PermissionDenied, "缺少用户标识"));
        }

        // 2. 限流校验逻辑
        var now = DateTime.UtcNow;
        var userLimitInfo = _userRequestLimit.AddOrUpdate(
            key: userId,
            addValueFactory: (id) => (1, now),
            updateValueFactory: (id, oldInfo) =>
            {
                // 当前时间超出上一个窗口,重置计数与窗口时间
                if (now.Subtract(oldInfo.WindowStart).TotalSeconds >= _windowSeconds)
                {
                    return (1, now);
                }
                // 窗口内计数+1
                return (oldInfo.Count + 1, oldInfo.WindowStart);
            });

        // 3. 超出限流阈值返回资源耗尽错误
        if (userLimitInfo.Count > _maxRequestsPerSecond)
        {
            throw new RpcException(new Status(StatusCode.ResourceExhausted, "请求频率过高,请稍后重试"));
        }

        // 4. 限流校验通过,继续执行后续逻辑
        return await continuation(request, context);
    }
}

启用限流拦截器

在服务端的Program.cs中注册拦截器即可全局生效:

builder.Services.AddGrpc(options =>
{
    options.Interceptors.Add<LimitClientRequestsInterceptor>();
});

注意事项

  • 上述代码为单机部署场景的实现,如果是多节点分布式部署,需要将限流计数存储到Redis等集中式缓存中,避免每个节点单独计数导致总阈值超出
  • 可以根据业务需求调整限流规则,比如为不同等级的用户配置不同的请求阈值
  • 建议客户端识别ResourceExhausted状态码,配合退避重试逻辑优化体验

内容的提问来源于stack exchange,提问作者Gabriel Ribeiro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 08:15:00