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

基于用户Claims动态切换YARP集群目标地址的实现方案咨询

基于用户Claims动态切换YARP集群目标地址的实现方案

核心结论

ConfigFilters是单例模式,无法获取请求上下文里的用户Claims,直接用它走不通。最优方案是通过自定义中间件+YARP集群选择逻辑实现,授权时推送地址到单例ConfigFilters是不合理的,会引发线程安全和上下文污染问题。

具体实现步骤

1. 自定义中间件提取Claims并查询缓存

中间件能拿到当前请求的HttpContext,从中提取用户Claims作为缓存键,查询缓存得到目标地址后存入HttpContext.Items,供后续YARP逻辑调用:

public class DynamicClusterMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ICacheService _cacheService;

    public DynamicClusterMiddleware(RequestDelegate next, ICacheService cacheService)
    {
        _next = next;
        _cacheService = cacheService;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var cacheKey = context.User.FindFirst("YourClaimType")?.Value;
        if (!string.IsNullOrEmpty(cacheKey))
        {
            var targetAddress = await _cacheService.GetTargetAddressAsync(cacheKey);
            if (!string.IsNullOrEmpty(targetAddress))
            {
                context.Items["DynamicTargetAddress"] = targetAddress;
            }
        }

        await _next(context);
    }
}

public static class DynamicClusterMiddlewareExtensions
{
    public static IApplicationBuilder UseDynamicCluster(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<DynamicClusterMiddleware>();
    }
}

2. 实现自定义集群选择器

通过IClusterSelector接口,在YARP选择集群的阶段,从HttpContext.Items取出目标地址,动态匹配或创建集群配置:

public class DynamicClusterSelector : IClusterSelector
{
    private readonly IProxyConfigProvider _proxyConfigProvider;
    private readonly ILogger<DynamicClusterSelector> _logger;

    public DynamicClusterSelector(IProxyConfigProvider proxyConfigProvider, ILogger<DynamicClusterSelector> logger)
    {
        _proxyConfigProvider = proxyConfigProvider;
        _logger = logger;
    }

    public ValueTask<ClusterState?> SelectClusterAsync(HttpContext context, RouteConfig route, CancellationToken cancellationToken)
    {
        if (context.Items.TryGetValue("DynamicTargetAddress", out var targetObj) && targetObj is string targetAddress)
        {
            var config = _proxyConfigProvider.GetConfig();
            // 先找现有集群里是否有对应地址的目标
            var cluster = config.Clusters.FirstOrDefault(c => c.Destinations.Values.Any(d => d.Address == targetAddress));
            
            if (cluster == null)
            {
                // 没有的话动态创建集群(频繁创建建议加缓存,避免重复实例)
                cluster = new ClusterState($"dynamic-cluster-{Guid.NewGuid()}")
                {
                    Destinations = new Dictionary<string, DestinationState>(StringComparer.OrdinalIgnoreCase)
                    {
                        { "dynamic-dest", new DestinationState { Address = targetAddress } }
                    }
                };
            }
            return new ValueTask<ClusterState?>(cluster);
        }

        // 没有动态地址时, fallback到默认集群选择逻辑
        return new ValueTask<ClusterState?>(null);
    }
}

3. 注册服务与中间件

在Program.cs中完成服务注册,注意中间件的顺序要放在UseAuthorization之后、UseReverseProxy之前:

var builder = WebApplication.CreateBuilder(args);

// 注册你的缓存服务实现
builder.Services.AddSingleton<ICacheService, YourCacheServiceImpl>();

// 注册自定义集群选择器
builder.Services.AddSingleton<IClusterSelector, DynamicClusterSelector>();

// 添加YARP代理
builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));

var app = builder.Build();

app.UseRouting();
app.UseAuthorization();
// 启用自定义中间件
app.UseDynamicCluster();
app.UseReverseProxy();

app.Run();

为什么不推荐授权时推送地址到ConfigFilters?

  • ConfigFilters是单例,多请求并发时会出现线程安全问题,不同用户的目标地址会互相覆盖。
  • ConfigFilters的设计目标是全局配置过滤,不是处理请求级别的动态逻辑,强行注入请求上下文会破坏其设计初衷,后续维护成本极高。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 18:12:34