
YARP 路由与集群扩展性设计解析从 Metadata 字符串字典到结构化扩展体系【免费下载链接】reverse-proxyA toolkit for developing high-performance HTTP reverse proxy applications.项目地址: https://gitcode.com/GitHub_Trending/re/reverse-proxy本文整理自 docs/designs/route-extensibility.md 归档设计讨论。该文档属于历史设计记录文件头部标注了 [!CAUTION] Archived design discussions. Information may be outdated and inaccurate.描述的是 YARP 早期关于路由Route与集群Cluster扩展机制的提案与思考。文中方案在当前仓库源码中属于尚未落地/演进中的设计方向阅读时请结合当前 RouteConfig.cs 与 ClusterConfig.cs 的实际情况对照理解。导读YARPYet Another Reverse Proxy允许通过配置定义路由与集群但早期的扩展手段只有一个Metadata属性且其类型被固定为Dictionarystring, string。对于 A/B 测试、后端服务鉴权等需要在配置中存放结构化数据并在运行时以强类型对象读取的场景这个设计显得捉襟见肘。本篇将围绕 YARP 路由/集群扩展性的设计提案梳理问题背景、目标场景、核心方案Extensions 集合 工厂注册机制以及配置热更新与第三方配置系统集成等关键议题并结合当前仓库源码解释其背后的运行模型。一、问题背景为什么Metadata不够用1.1 现状唯一的扩展通道是字符串字典在 YARP 当前的设计中路由和集群都通过一个键值对字典承载用户自定义数据路由侧RouteConfig.Metadata声明为IReadOnlyDictionarystring, string?见 RouteConfig.cs集群侧ClusterConfig.Metadata类型一致见 ClusterConfig.cs。由于值是string任何结构化数据对象、数组、嵌套配置都必须先序列化成字符串塞进去再用的时候再解析回来。设计文档指出这直接限制了 A/B 测试、与后端服务器鉴权非透传式等真实场景——它们需要在配置中保存一整块结构并在运行时从 route/cluster 对象上直接取用。1.2 另一个驱动因素预置扩展的互不踩脚设计文档引用了 YARP 的预置扩展议题#1714如果要为 YARP 提供一批开箱即用的扩展就必须保证每个扩展在 route/cluster 上都有自己的独立配置数据区且互不干扰。共享一个字符串字典显然做不到这种隔离。二、为什么这个问题值得关注A/B 测试的典型诉求设计文档给出的经典例子是 A/B 测试一条路由需要把流量按比例分发到多个集群比例与集群集合都来自配置。理想配置形态大致如下{ ReverseProxy: { Routes: { route1: { ClusterId: ignore, Match: { Path: {**catch-all} }, Extensions: { A-B: [ { ClusterId: c1, Load: 0.4 }, { ClusterId: c2, Load: 0.6 }, { ClusterId: experimental, Load: 0.01 }, { ClusterId: TelemetrySample, Load: 0.01 } ] } } } } }注意几个细节ClusterId被置为ignore因为真正的转发目标由扩展在运行时决定而不是由路由静态指定配置里出现了一个新的Extensions节点键名A-B代表一种扩展值是一组{ ClusterId, Load }结构体——这正是任意结构化数据诉求的具体体现同一节点下未来还可以挂其他扩展各自携带自己的数据互不冲突。三、方案需求Requirements设计文档对扩展机制提出了四条硬性需求多扩展并存YARP 上可以挂多个扩展每个扩展拥有自己的配置状态任意结构化数据route/cluster 的配置中允许承载任意数据YARP 不应规定扩展配置的数据结构即不强加 schema运行时强类型访问中间件能够直接从 route/cluster 对象上以对象形态拿到这些数据支持远程配置在分布式配置服务器的场景下这些数据要能够被远程化传递。四、核心提案Proposal4.1 在 Route/Cluster 上增加 Extensions 集合提案的核心是给 Route 和 Cluster 各增加一个Extensions集合模式仿照 ASP.NET Core 的 HTTP Features 体系使用IReadOnlyDictionaryType, object以扩展类型为键。这样中间件在运行时可以根据类型精确取出对应对象且结果天然强类型。在代理中间件中的使用形态如下public void Configure(IApplicationBuilder app, IProxyStateLookup lookup) { app.UseRouting(); app.UseEndpoints(endpoints { // 自定义代理管道可以增删改代理步骤 endpoints.MapReverseProxy(proxyPipeline { // 自定义中间件定义见下 proxyPipeline.Use((context, next) { var proxyFeature context.Features.GetIReverseProxyFeature(); var abstate proxyFeature.Route.Extensions[typeof(ABState)]; var newClusterName abstate.SelectSlice(new Random().NextDouble()); if (lookup?.TryGetCluster(newClusterName, out var cluster)) { context.ReassignProxyRequest(cluster); } return next(); }); proxyPipeline.UseSessionAffinity(); proxyPipeline.UseLoadBalancing(); }); }); }这段示例代码包含了三个关键运行机制都能在当前仓库中找到对应实现IReverseProxyFeature当前请求的代理状态载体暴露RouteRouteModel、ClusterClusterModel以及可用/已代理目标等属性定义见 IReverseProxyFeature.cslookup.TryGetCluster(...)通过 IProxyStateLookup 按名称查询集群状态ClusterStatecontext.ReassignProxyRequest(cluster)运行时把当前请求改派到另一个集群实现在 HttpContextFeaturesExtensions.cs。该扩展既提供仅替换集群的重载也提供同时替换路由RouteModel与集群的重载且更新后会重建IReverseProxyFeature。这一机制在 LimitsMiddleware.cs 的注释中也有印证——限制类中间件位于管道中应用可在其生效前调用ReassignProxyRequest把请求切到别的路由。从源码结构看Extensions的运行时载体应与RouteModel/ClusterModel类似作为配置驱动的不可变数据随模型整体原子替换详见下文热更新一节。4.2 用工厂机制把配置键映射到强类型对象仅加一个字典还不够——YARP 需要知道配置里的键名对应哪个类型从而在解析IConfiguration时构造出强类型对象。提案给出的注册方式services.AddReverseProxy() .LoadFromConfig(Configuration.GetSection(ReverseProxy)) .AddRouteExtension(A-B, (section, _, _) new ABState(section));集群侧有类似机制。提案设想的注册签名大致是static IReverseProxyBuilder AddRouteExtension(this IReverseProxyBuilder builder, string sectionName, FuncIConfigurationSection, RouteConfig, ExtensionType, ExtensionType factory)其中工厂依次接收该扩展对应的IConfigurationSection配置片段正在被扩展的 route 对象已存在的扩展实例——这是为配置热更新准备的见下文 4.3。当配置被解析时YARP 根据配置键名调用对应工厂把返回对象放入 route/cluster 的 Extensions 集合。4.3 配置热更新下的实例稳定性这一节是提案中最体现工程严谨性的部分。YARP 常规配置在变更时会做diff merge对比新旧配置必要时创建新对象。扩展数据必须复用同一套机制并且遵循以下原则配置更新后若某个扩展的旧实例存在则把它传给工厂工厂自行决定是复用旧实例还是把旧数据拷贝进新实例以反映配置变化实例必须稳定如果复用会影响正在处理中的请求就不能原地修改旧实例。由于扩展类型是用户自定义的YARP 无法强制规定对象内部如何变更只能把稳定性责任交给工厂实现。这个稳定实例的要求与当前仓库的运行模型完全吻合RouteState持有volatile RouteModelRouteState.csClusterState持有volatile ClusterModelClusterState.cs配置变化时通过整体替换不可变模型而非原地修改来保证线程安全——RouteModel、ClusterModel的注释都明确要求所有成员保持不可变需要变化时整体替换实例。因此扩展对象若作为模型的一部分同样应以新建/替换而非原地改的方式演进。4.4 自定义配置提供方Custom Config Provider使用自定义配置提供方时有两种途径填充 Extensions由提供方直接填充Extensions 集合提供方实现一个IConfigurationSection然后走上面的工厂机制。4.5 与第三方配置系统的集成IConfigurationSection桥接当 YARP 与 K8s、Service Fabric 等第三方配置系统集成时这些系统通常有自己的自定义属性表达方式其中一部分会被 YARP 用来定义 route/cluster。提案建议集成方提供一个IConfigurationSection实现把第三方持久化格式映射到 YARP 的配置视图理由IConfigurationSection本质上就是名称/值查找 API与配置系统常用的 YAML、JSON 格式能较好地对应实现负担不大。从当前仓库看这一思路与 Kubernetes.Controller 的实践方向一致——其 YarpParser.cs、YarpIngressContext.cs 等转换器负责把 K8s Ingress/Service 对象翻译为 YARP 的 route/cluster 配置印证了第三方系统格式 ↔ YARP 配置模型之间确实需要一层映射适配。4.6 分布式配置服务器扩展数据的远程化设计文档进一步提出对应议题 #1710可以把中央 YARP 配置提供方模式正式化让多个 YARP 代理实例绑定到同一个中央配置源从而一次推送、多实例生效提升可扩展性。要支撑该场景需要将 IConfiguration 数据序列化后传给各代理实例扩展配置数据自然包含在内随配置一起远程分发。五、设计要点归纳关注点设计方案对应仓库现状/机制数据承载Route/Cluster 增加Extensions: IReadOnlyDictionaryType, object按类型存取当前仅Metadata字符串字典见 RouteConfig.cs、ClusterConfig.cs配置映射AddRouteExtension(A-B, factory)把配置键注册到工厂工厂机制属提案内容尚未在源码中体现运行时访问中间件经IReverseProxyFeature.Route/Cluster取扩展对象配合ReassignProxyRequest改派见 IReverseProxyFeature.cs、HttpContextFeaturesExtensions.cs热更新diff merge 工厂接收旧实例实例必须稳定、不破坏在途请求模型整体原子替换RouteState/ClusterState中volatile模型引用见 RouteState.cs、ClusterState.cs第三方配置集成方实现IConfigurationSection桥接格式差异K8s 集成已有转换层佐证src/Kubernetes.Controller/Converters/分布式序列化 IConfiguration 数据中央提供方推送多实例属提案方向议题 #1710六、结语Route/Cluster 扩展性是 YARP 从配置文件驱动的反向代理走向可编程、可扩展的应用平台的关键设计议题以IReadOnlyDictionaryType, object替代单一字符串字典配以工厂注册、diff merge 热更新、IConfigurationSection桥接与分布式序列化构成了完整的技术蓝图。需要再次提醒的是本文内容来自归档设计讨论docs/designs/route-extensibility.md部分细节可能已与当前版本演进不同——若要在真实项目中使用类似能力请以当前 RouteConfig.cs、ClusterConfig.cs 与 RouteModel.cs、ClusterModel.cs 的实际 API 为准但其中关于扩展隔离、强类型访问、实例稳定、格式桥接的设计原则对理解 YARP 的配置与运行模型仍有重要参考价值。【免费下载链接】reverse-proxyA toolkit for developing high-performance HTTP reverse proxy applications.项目地址: https://gitcode.com/GitHub_Trending/re/reverse-proxy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考