目录

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. CentOS7的udev的绑定规则

    客户一套RAC环境是华为的存储,共享盘是/dev/sd*,咋一看还怀疑是没有进行多路径配置,实际和主机工程师是已经配置好的,我们使用upadmin show vlun命令可以查看到: [root@xx ...

  2. 关于行结束符(CR、LF)、回车、换行

    CR(Carriage Return)表示回车 LF(Line Feed)表示换行 Dos和Windows采用回车+换行(CR+LF)表示下一行而UNIX/Linux采用换行符(LF)表示下一行苹果机 ...

  3. .NET 云原生架构师训练营(模块二 基础巩固 HTTP管道与中间件)--学习笔记

    2.3.2 Web API -- HTTP管道与中间件 管道 中间件 ASP.NET Core 中间件:https://docs.microsoft.com/zh-cn/aspnet/core/fun ...

  4. python-命令行参数处理 getopt模块详解

    背景 在写脚本程序的时候需要添加一些额外的参数来实现脚本的附加功能或者增强功能,通常的做法是通过sys.argv[i]直接来获取参数的值,但是这个比较局限,要求参数的输入一定要按照顺序. fileNa ...

  5. CDN缓存的理解

    CDN缓存的理解 CDN即内容分发网络Content Delivery Network,CDN的基本原理是广泛采用各种缓存服务器,将这些缓存服务器分布到用户访问相对集中的地区或网络中,在用户访问网站时 ...

  6. Redhat7更改网易yum源

    说明 之前写了一篇关于Redhat6更换Yum源的文章,时隔已久很多包都变了,正好最近搭建环境需要用到Redhat7.3所以就再记录一下如何更换为国内最新最常用的yum源. 操作步骤 1.卸载系统自带 ...

  7. 导致Redis访问慢的常见操作

    导致Redis访问慢的原因通常有2个方面: 第一,Redis本身性能出现了瓶颈,如:内存使用率过高,并发过大等 第二,存在大KEY,或者客户端访问命令使用不当引起的阻塞 在此,只列举因为的客户端命令使 ...

  8. 解决Spring boot 单元测试,无法读取配置文件问题。

    1.启动类上加上@EnableConfigurationProperties 2.springboot版本springboot 2.X版本在单元测试中读取不到yml配置文件的值这是个大坑,在项目中写单 ...

  9. 项目实战:Qt数据分析处理平台(兼容各国产麒麟系统)(文件域字符串解析,上万文件批量导入,折线图、散点图,正态分布图分析处理导出等)

    若该文为原创文章,转载请注明原文出处本文章博客地址:https://blog.csdn.net/qq21497936/article/details/114710650长期持续带来更多项目与技术分享, ...

  10. 协程与yield表达式

    在函数内,yield语句可以作为表达式使用,出现在赋值运算符的右边,例如: def receiver(): print("Ready to receive") while True ...