基于用户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
相关产品推荐
相关产品推荐

