目录

Welcome to YARP - 1.认识YARP并搭建反向代理服务

Welcome to YARP - 2.配置功能

Welcome to YARP - 3.负载均衡

Welcome to YARP - 4.限流

Welcome to YARP - 5.身份验证和授权

Welcome to YARP - 6.压缩、缓存

Welcome to YARP - 7.目标健康检查

Welcome to YARP - 8.分布式跟踪

哈哈哈,第一篇文章还说,只规划了8篇文章,写到配置功能的时候发现东西还是挺多的,还是拆分成小章节来说吧。目前成了10篇了—_—。写之前觉得配置功能应该没什么东西可讲,结果写着写着都想讲一嘴。然后就越写越多,大家看的时候可以选择性的跳过。

介绍

如果有同学不知道YARP是什么,YARP有哪些功能的同学请移步第一篇文章Welcome to YARP - 1.认识YARP并搭建反向代理服务。接下来这篇文章主要讲解YARP的配置功能,包含:配置文件配置提供程序配置筛选器

配置文件(Configuration Files)

YARP可以使用 Microsoft.Extensions 中的 IConfiguration 从文件加载路由和群集的配置。这就说明了不只是JSON文件,任何支持 IConfiguration 源都有效。你可以基于IConfiguration扩展出XMLYAML等格式的配置。而且如果源文件发生更改,配置也将更新,无需重新启动代理服务。

1.加载配置

要从 IConfiguration 加载代理配置,请在启动中添加以下代码(LoadFromConfig)

builder.Services.AddReverseProxy()//添加ReverseProxy相关服务到DI
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));//从配置文件中加载ReverseProxy的设置

LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"))这段代码会把配置文件中ReverseProxy节点下的路由(Routes)配置和集群(Clusters)配置都加载到程序中。

2.多个配置源

从 1.1 开始,YARP 支持从多个源加载代理配置。LoadFromConfig 可以多次调用,引用不同的 IConfiguration 节点,也可以与不同的配置源(如InMemory)结合使用。路由可以引用来自其他源的群集。

请注意,对于给定的路由或集群,不支持合并来自不同源的节点配置。

思考1:YARP是怎么做到可以多配置源或加载不同的配置源呢?(只是使用,可略过此部分)
源码解读:

这时候我们就要扒一扒他的源码了

看源码也是为了看优秀的代码怎么写,学习其设计,其次就是看源码的技巧,怎么一层一层的去解剖、去找你想要的东西。

我们先从入口开始:

builder.Services.AddReverseProxy()//添加ReverseProxy相关服务到DI
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));//从配置文件中加载ReverseProxy的设置

接下来我们在看一下 AddReverseProxy 这个扩展方法

	/// <summary>
/// Adds ReverseProxy's services to Dependency Injection.
/// </summary>
public static IReverseProxyBuilder AddReverseProxy(this IServiceCollection services)
{
var builder = new ReverseProxyBuilder(services);
builder
.AddConfigBuilder()
.AddRuntimeStateManagers()
.AddConfigManager()//关键就在这里
.AddSessionAffinityPolicies()
.AddActiveHealthChecks()
.AddPassiveHealthCheck()
.AddLoadBalancingPolicies()
.AddHttpSysDelegation()
.AddDestinationResolver()
.AddProxy(); services.TryAddSingleton<ProxyEndpointFactory>(); services.AddDataProtection();
services.AddAuthorization();
services.AddCors();
services.AddRouting(); return builder;
}

我们看到有一个 AddConfigManager ,这个就是注入 ProxyConfigManager 的,关键代码就在这个类里,我们直接看这个类,代码太多了,我们只看关键性的代码片段

internal sealed class ProxyConfigManager : EndpointDataSource, IProxyStateLookup, IDisposable
{
private static readonly IReadOnlyDictionary<string, ClusterConfig> _emptyClusterDictionary = new ReadOnlyDictionary<string, ClusterConfig>(new Dictionary<string, ClusterConfig>()); private readonly object _syncRoot = new();
private readonly ILogger<ProxyConfigManager> _logger;
private readonly IProxyConfigProvider[] _providers;//关键接口
}

IProxyConfigProvider 接口是获取配置的关键,他里面只有一个 返回类型为 IProxyConfigGetConfig 方法。IProxyConfig 里面定义了当前路由和集群的列表,以及一个用于信息过期,并需要通知YARP 重新加载配置的令牌IChangeToken ,而他们就是获取配置的核心,我们甚至可以实现IProxyConfigProvider接口去自定义一个配置提供者,比如LoadFromRedis()LoadFromSqlServer()完全可以的(等有时间了,我会写个demo示例,先把规划的文章写完了再去搞这个)。

/// <summary>
/// A data source for proxy route and cluster information.
/// </summary>
public interface IProxyConfigProvider
{
/// <summary>
/// Returns the current route and cluster data.
/// </summary>
/// <returns></returns>
IProxyConfig GetConfig();
}

ProxyConfigManager类就是定义了 IProxyConfigProvider[] 数组去获取多个 IProxyConfigProvider 实例,然后在调用 GetConfig() 方法去获取每个实例的配置。具体实现在这个类的 InitialLoadAsync方法里,代码片段如下:

internal async Task<EndpointDataSource> InitialLoadAsync()
{
try
{
var routes = new List<RouteConfig>();
var clusters = new List<ClusterConfig>(); var resolvedConfigs = new List<(int Index, IProxyConfigProvider Provider, ValueTask<IProxyConfig> Config)>(_providers.Length); //这里循环_providers去调用LoadConfigAsync方法去拿配置,LoadConfigAsync方法内部就是调用的provider.GetConfig()方法获取配置,只不过内部还做了一写其他操作(配置校验等)
for (var i = 0; i < _providers.Length; i++)
{
var provider = _providers[i];
var configLoadTask = LoadConfigAsync(provider, cancellationToken: default);
resolvedConfigs.Add((i, provider, configLoadTask));
} foreach (var (i, provider, configLoadTask) in resolvedConfigs)
{
var config = await configLoadTask;
_configs[i] = new ConfigState(provider, config);
routes.AddRange(config.Routes ?? Array.Empty<RouteConfig>());
clusters.AddRange(config.Clusters ?? Array.Empty<ClusterConfig>());
} return this;
}

然后我们在验证下,既然官方说了1.1支持的多配置那么1.0版本这里是不是就不是数组呢?答案是肯定的:

好了现在已经知道是通过多个IProxyConfigProvider 循环去调用每个实例的GetConfig方法拿配置了。那么IProxyConfigProvider是接口,他的实例从哪来?哈哈哈,其实这是一个很蠢的问题。那不就是通过 LoadFromXXX 来注入不同的 ConfigProvider 吗?本着既然写了就刨到底的态度,我们还是看一眼吧,哈哈哈

LoadFromConfig方法是通过配置文件获取配置,注入的是ConfigurationConfigProvider ,而这个类则是继承了IProxyConfigProvider 接口:

	/// <summary>
/// Loads routes and endpoints from config.
/// </summary>
public static IReverseProxyBuilder LoadFromConfig(this IReverseProxyBuilder builder, IConfiguration config)
{
if (config is null)
{
throw new ArgumentNullException(nameof(config));
} builder.Services.AddSingleton<IProxyConfigProvider>(sp =>
{
// This is required because we're capturing the configuration via a closure
return new ConfigurationConfigProvider(sp.GetRequiredService<ILogger<ConfigurationConfigProvider>>(), config);
}); return builder;
} internal sealed class ConfigurationConfigProvider : IProxyConfigProvider, IDisposable
{
public IProxyConfig GetConfig()
{
// First time load
if (_snapshot is null)
{
_subscription = ChangeToken.OnChange(_configuration.GetReloadToken, UpdateSnapshot);
UpdateSnapshot();
} return _snapshot;
}
}

LoadFromMemory 也是同样的,注入的是InMemoryConfigProvider

	/// <summary>
/// Adds an InMemoryConfigProvider
/// </summary>
public static IReverseProxyBuilder LoadFromMemory(this IReverseProxyBuilder builder, IReadOnlyList<RouteConfig> routes, IReadOnlyList<ClusterConfig> clusters)
{
builder.Services.AddSingleton(new InMemoryConfigProvider(routes, clusters));
builder.Services.AddSingleton<IProxyConfigProvider>(s => s.GetRequiredService<InMemoryConfigProvider>());
return builder;
} public sealed class InMemoryConfigProvider : IProxyConfigProvider
{
// Marked as volatile so that updates are atomic
private volatile InMemoryConfig _config; /// <summary>
/// Implementation of the IProxyConfigProvider.GetConfig method to supply the current snapshot of configuration
/// </summary>
/// <returns>An immutable snapshot of the current configuration state</returns>
public IProxyConfig GetConfig() => _config;
}

说的有点多了,其实是一个很简单的功能(对于使用者来说),快速入手可以忽略这部分。

3.配置约定(可略过)

基于文件的配置通过 IProxyConfigProvider 实现在应用程序启动时和每次配置更改时进行转换,动态映射到 Yarp.ReverseProxy.Configuration 命名空间中的类型。

说人话就是:Yarp.ReverseProxy.Configuration这个命名空间下有很多关于YARP[配置的类]( Namespace Yarp.ReverseProxy.Configuration (microsoft.github.io) ),如ActiveHealthCheckConfig.csClusterConfig.cs等等,然后这些类都是通过IProxyConfigProvider进行映射的,我们拿到配置后会对这些类的属性进行配置映射。

4.配置结构

LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"))这段代码会把配置文件中ReverseProxy节点下的 Routes 配置节和 Clusters 配置节都加载到程序中。

例:

{
"ReverseProxy": {
"Routes": {
"route1" : {
"ClusterId": "cluster1",
"Match": {
"Path": "{**catch-all}",
"Hosts" : [ "www.aaaaa.com", "www.bbbbb.com"],
},
}
},
"Clusters": {
"cluster1": {
"Destinations": {
"cluster1/destination1": {
"Address": "https://example.com/"
}
}
}
}
}
}

5.Routes

路由配置最小集:

​ 路由节点是路由匹配及其相关配置的无序集合。路径至少需要以下字段:

  • (RouteId)路由 ID - 唯一名称 (示例中的route1

  • (ClusterId)群集 ID - 指 集群(Clusters) 节点中 某个集群的名称 (示例中的cluster1

  • (Match)匹配 - 包含 Hosts 数组或 Path 模式字符串。PathASP.NET Core 路由模板,可以按ASP.NET Core 路由模板文档进行定义。

    路由匹配中越具体的路由匹配优先级越高。也可以使用 Order 字段实现显式排序,值越低优先级越高。

其他配置:

当然你还可以在路由节点下配置以下内容

  • AuthorizationPolicy

    • 要应用于此路由的授权策略的名称。如果未设置,则仅应用回退策略。设置为 Default 以启用应用程序默认策略的授权。设置为 Anonymous 以禁用此路由的所有授权检查。
  • Headers
    • 要匹配的标头(未指定)为任意
  • CorsPolicy
    • 要应用于此路由的CorsPolicy 的名称。如果未设置,则不会自动匹配 cors 预检请求的路由。设置为 Default 以启用具有默认策略的 cors。设置为 Disable 以拒绝此路由的 cors 请求。

还有很多支持的配置这里就不一 一列举了,可以查看官方的 RouteConfig 文档。

思考2:令我好奇的是,我查看了官方的 RouteConfig 文档竟然没有发现关于针对路由限流的?难道限流不是在这里配置?方便的是每页的API文档上有对应的源码链接,点看查看,果然是由限流配置的。只不过要.NET 7.0才支持这个功能。

然后我又打开限流的文档查看,发现文档上也是有配置的。哈哈哈,这里算是来了一波剧透了吧。

6.Clusters

群集节点是命名群集的无序集合。集群主要包含命名目标及地址的集合,其中任何一个都被认为能够处理给定路由的请求。代理将根据路由和群集配置处理请求,以便选择目标。

说人话:YARP会根据路由节点的集群标识(ClusterId)去集群节点中匹配相应的集群目标地址(Address)

相关其他字段配置,请参阅 ClusterConfig

7.所有属性配置(可以暂时略过,后续在具体功能模块会拿出来单独讲)

{
// 代理服务的URL,必须和下面路由定义的Url区分开
"Urls": "http://localhost:5000;https://localhost:5001",
"Logging": {
"LogLevel": {
"Default": "Information",
// 取消注释以对运行时和代理隐藏诊断消息
// "Microsoft": "Warning",
// "Yarp" : "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ReverseProxy": {
// Routes 告诉代理要转发的请求
"Routes": {
"minimumroute" : {
// 匹配任何内容并将其路由到www.example.com
"ClusterId": "minimumcluster",
"Match": {
"Path": "{**catch-all}"
}
},
"allrouteprops" : {
// 匹配 /something/* 并路由到“allclusterrops”
"ClusterId": "allclusterprops", // 其中一个群集的名称
"Order" : 100, // 数字越低,优先级越高
"MaxRequestBodySize" : 1000000, // 以字节为单位。服务器请求体大小的限制(默认为30MB)。设置为-1可禁用。
"Authorization Policy" : "Anonymous", // 策略名称 "Default", "Anonymous"
"CorsPolicy" : "Default", // 要应用到此路由的CorsPolicy的名称 "Default", "Disable"
"Match": {
"Path": "/something/{**remainder}", // 这里的Path是 ASP.NET Core语法
"Hosts" : [ "www.aaaaa.com", "www.bbbbb.com"], // 要匹配的主机名(未指定)为任意
"Methods" : [ "GET", "PUT" ], // 匹配的HTTP方法, uspecified 是所有
"Headers": [ // 要匹配的标头(未指定)为任意
{
"Name": "MyCustomHeader", // 标头的名称
"Values": [ "value1", "value2", "another value" ],
"Mode": "ExactHeader", // "ExactHeader(完全匹配)","HeaderPrefix(前缀匹配)", "Exists" , "Contains", "NotContains"
"IsCaseSensitive": true
}
],
"QueryParameters": [ // 要匹配的查询参数(未指定)为任意
{
"Name": "MyQueryParameter", // 查询参数名称
"Values": [ "value1", "value2", "another value" ],
"Mode": "Exact", // "Exact" "Prefix", "Exists" , "Contains", "NotContains"
"IsCaseSensitive": true
}
]
},
"MetaData" : { // 可由自定义扩展使用的键值对列表
"MyName" : "MyValue"
},
"Transforms" : [ // 转换列表。有关更多详细信息,请参阅Transforms文章
{
"RequestHeader": "MyHeader",
"Set": "MyValue",
}
]
}
},
// Clusters 告诉代理在哪里以及如何转发请求
"Clusters": {
"minimumcluster": {
"Destinations": {
"example.com": {
"Address": "http://www.example.com/"
}
}
},
"allclusterprops": {
"Destinations": {
"first_destination": {
"Address": "https://contoso.com"
},
"another_destination": {
"Address": "https://10.20.30.40",
"Health" : "https://10.20.30.40:12345/test" // 覆盖活动的运行状况检查
}
},
"LoadBalancingPolicy" : "PowerOfTwoChoices", // "PowerOfTwoChoices", "FirstAlphabetical", "Random", "RoundRobin", "LeastRequests"
"SessionAffinity": {
"Enabled": true, // 默认 'false'
"Policy": "Cookie", // 默认值"Cookie", 可选 "CustomHeader"
"FailurePolicy": "Redistribute", // 默认值"Redistribute", 可选 "Return503Error"
"Settings" : {
"CustomHeaderName": "MySessionHeaderName" // 默认为 'X-Yarp-Proxy-Affinity`
}
},
"HealthCheck": {
"Active": { // 进行API调用以验证运行状况
"Enabled": "true",
"Interval": "00:00:10",
"Timeout": "00:00:10",
"Policy": "ConsecutiveFailures",
"Path": "/api/health" // 要查询运行状况状态的API终结点
},
"Passive": { // 基于HTTP响应代码禁用目标
"Enabled": true, // 默认值 false
"Policy" : "TransportFailureRateHealthPolicy",
"ReactivationPeriod" : "00:00:10" // 10s
}
},
"HttpClient" : { // 用于关联指定的HttpClient实例的配置
"SSLProtocols" : "Tls13",
"DangerousAcceptAnyServerCertificate" : false,
"MaxConnectionsPerServer" : 1024,
"EnableMultipleHttp2Connections" : true,
"RequestHeaderEncoding" : "Latin1" // 如何解释标头值中的非ASCII字符
},
"HttpRequest" : { // 将请求发送到目的地的选项
"ActivityTimeout" : "00:02:00",
"Version" : "2",
"VersionPolicy" : "RequestVersionOrLower",
"AllowResponseBuffering" : "false"
},
"MetaData" : {
"TransportFailureRateHealthPolicy.RateLimit": "0.5", // 被动健康策略使用
"MyKey" : "MyValue"
}
}
}
}
}

总结

至此配置文件部分结束,说的有点多了,对于使用者而言 我们知道怎么用的,支持哪些配置源,各个配置代表什么就可以了。但是本着学习的态度结果罗嗦了一堆,大家看的时候可以选择性忽略,能忽略的部分已经做了标记。

本章节没有代码示例

下个小章节我们快速介绍下:Configuration Providers(多种方式提供配置)

Welcome to YARP - 2.1配置功能 - 配置文件的更多相关文章

  1. WCF学习之旅—WCF4.0中的简化配置功能(十五)

    六 WCF4.0中的简化配置功能 WCF4.0为了简化服务配置,提供了默认的终结点.绑定和服务行为.也就是说,在开发WCF服务程序的时候,即使我们不提供显示的 服务终结点,WCF框架也能为我们的服务提 ...

  2. SpringBoot配置(1) 配置文件application&yml

    SpringBoot配置(1) 配置文件application&yml 一.配置文件 1.1 配置文件 SpringBoot使用一个全局的配置文件,配置文件名是固定的. application ...

  3. centos LAMP第三部分php,mysql配置 php配置文件 配置php的error_log 配置php的open_basedir 安装php的扩展模块 phpize mysql配置第二十一节课

    centos   LAMP第三部分php,mysql配置 php配置文件   配置php的error_log  配置php的open_basedir 安装php的扩展模块 phpize  mysql配 ...

  4. 2_1.springboot2.x配置之配置文件解析

    1.配置文件 1.Spring Boot使用一个全局的配置文件:•application.properties.application.yml 2.配置文件放在src/main/resources目录 ...

  5. 服务器IP配置功能实现小结

    1. 服务器网卡配置文件 /etc/sysconfig/network/ifcfg-***(eth0) linux-f1s9:/etc/sysconfig/network # cat ifcfg-et ...

  6. Rest Client(Rest接口调试工具,有保存功配置功能) chrome浏览器插件

    Rest Client(Rest接口调试工具,有保存功配置功能) chrome浏览器插件 下载地址 插件的操作很简单,下面是一些简单的实例. 1.安装 在谷歌应用商城搜索postman,如下图1-1所 ...

  7. qmake的配置功能(Configuration Features)

    Configuration Features qmake can be set up with extra configuration features that are specified in f ...

  8. Abp扩展之【配置功能】

    Abp的扩展功能非常强大,配合持久化可以很方便的配置系统.租户.用户的配置,关于ABP的配置请参考: http://www.cnblogs.com/farb/p/ABPSettingManagemen ...

  9. Spring boot 自动配置自定义配置文件

    示例如下: 1.   新建 Maven 项目 properties 2.   pom.xml <project xmlns="http://maven.apache.org/POM/4 ...

  10. USB转串口参数配置功能

    当使用USB转串口芯片时,在部分场合下需要修改芯片内部的USB参数以满足其应用需要.常见如: 系统下使用多个USB转串口芯片,区分使用各芯片. 修改厂商ID.产品ID.厂商字符串,使用客户自有ID和信 ...

随机推荐

  1. Java - CodeForces - 469C

    题目: 现在有一个容器,里面有n个物品,编号为1-n,现在小q可以进行一些操作,每次取出任意两个数,可以把这两个数的编号相加,相减,相乘,再把结果放回容器.问最后小q能否在n-1次操作后使得容器里的唯 ...

  2. 零基础入门学习Java课堂笔记 ——day02

    流程控制 1.Scanner对象 来源:java.util.Scanner 我们可以通过Scanner类来获取用户的输入输出 用户交互Scanner 通过new关键词创建Scanner对象 Scann ...

  3. 打开PDB报错ORA-30013

    多租户架构,之前还在做运维的时期接触也不多.遇到多租户问题,第一反应是有些发虚的. 但实际很多问题很简单,也容易解决.本文就是一个例子. 问题:RAC节点2打开所有PDB时,报错ORA-30013. ...

  4. 24.3 向量化异常VEH--《Windows核心编程》

    Windows 提供了向量化异常处理(vectored excepation handing,VEH)机制.程序可以注册一个函数,每当异常发送或者一个未处理异常脱离标准SEH的控制时,这个函数就会被调 ...

  5. js 实现call和apply方法,超详细思路分析

    壹 ❀ 引 我在 五种绑定策略彻底弄懂this 一文中,我们提到call,apply,bind属于显示绑定,这三个方法都能直接修改this指向.其中call与apply比较特殊,它们在修改this的同 ...

  6. NEMU PA 3-3 实验报告

    一.实验目的 在上一章PA3-2中,我们实现了分段机制,将48位的虚拟地址vaddr转换成了laddr.为什么不是paddr呢?这就要说到这一章要完成的东西:**分页机制 **. 从80386开始,计 ...

  7. NC15479 最短路

    题目链接 题目 题目描述 企鹅国中有 \(N\) 座城市,编号从 \(1\) 到 \(N\) . 对于任意的两座城市 \(i\) 和 \(j\),企鹅们可以花费 \((i\,\,xor\,\, j)* ...

  8. NC15033 小G有一个大树

    题目链接 题目 题目描述 小G想要把自己家院子里的橘子树搬到家门口(QAQ..就当小G是大力水手吧) 可是小G是个平衡性灰常灰常差的人,他想找到一个这个橘子树的平衡点. 怎么描述这棵树呢...就把它看 ...

  9. 从零开始手写 mybatis (三)jdbc pool 从零实现数据库连接池

    前景回顾 第一节 从零开始手写 mybatis(一)MVP 版本 中我们实现了一个最基本的可以运行的 mybatis. 第二节 从零开始手写 mybatis(二)mybatis interceptor ...

  10. Vue实现简单图书管理例子

    以下内容整理自网络. 说明 本例主要涵盖以下知识点: 数据绑定 条件与循环 计算属性 监听器 过滤器 常见数组和对象操作 vue生命周期 示例演示 代码 <!DOCTYPE html> & ...