Welcome to YARP - 2.2 配置功能 - 配置提供者(Configuration Providers)
目录
Welcome to YARP - 1.认识YARP并搭建反向代理服务
- 2.1 - 配置文件(Configuration Files)
- 2.2 - 配置提供者(Configuration Providers)
- 2.3 - 配置过滤器(Configuration Filters)
哈哈哈,第一篇文章还说,只规划了8篇文章,写到配置功能的时候发现东西还是挺多的,还是拆分成小章节来说吧。目前成了10篇了—_—。写之前觉得配置功能应该没什么东西可讲,结果写着写着都想讲一嘴。然后就越写越多,大家看的时候可以选择性的跳过。
介绍
如果有同学不知道YARP
是什么,YARP
有哪些功能的同学请移步第一篇文章[Welcome to YARP - 1.认识YARP并搭建反向代理服务]( Welcome to YARP - 1.认识 YARP 之- 反向代理 - coding-y - 博客园 (cnblogs.com) )。接下来这篇文章主要讲解YARP
的配置功能的第二小章节:配置提供程序
配置提供者(Configuration Providers)
我们之前两篇的示例中,都是通过配置文件去加载代理所需要的配置。当然,我们也可以选择以编程方式加载代理配置 。 我们可以通过提供几个实现 IProxyConfigProvider
和 IProxyConfig
的类来做到这一点,上一篇文章中我们通过源码解读的方式,也提到过这两个接口,这里就不多说了。
结构
这个上篇文章也提到过,这里就简单带过一下吧:
IProxyConfigProvider
有一个 GetConfig()
返回 IProxyConfig
实例的方法。IProxyConfig
具有当前路由和集群的列表,以及一个用于信息过期,并通知YARP
重新加载配置的令牌IChangeToken
,这将导致 GetConfig()
会被再次调用。
内存中配置(In Memory Config)
通过 InMemoryConfigProvider
调用 LoadFromMemory
实现 IProxyConfigProvider
并启用直接在代码中指定路由和群集
services.AddReverseProxy().LoadFromMemory(routes, clusters);
若要稍后更新配置,请从服务容器解析 , InMemoryConfigProvider
并使用新的路由和集群列表进行调用 Update
httpContext.RequestServices.GetRequiredService<InMemoryConfigProvider>().Update(routes, clusters);
生命周期
启动
IProxyConfigProvider
应该在 DI
容器中注册为单一实例。在启动时,代理将解析此实例并调用 GetConfig()
。在第一次调用时,提供者可以选择:
- 如果提供程序由于任何原因无法生成有效的代理配置,则引发异常。这将阻止应用程序启动。
- 加载配置时同步阻止。这将阻止应用程序启动,直到有效的路由数据可用。
- 或者,它可能选择在后台加载配置时返回空
IProxyConfig
实例。提供程序需要在配置可用时触发。IChangeToken
原子性(Atomicity)
提供给代理的配置对象和集合应该是只读的,并且在通过GetConfig()
将它们移交给代理后不会进行修改。
/// <summary>
/// 表示代理配置数据的快照。这些属性可能被多次访问,不应被修改。
/// </summary>
public interface IProxyConfig
{
private static readonly ConditionalWeakTable<IProxyConfig, string> _revisionIdsTable = new();
/// <summary>
/// A unique identifier for this revision of the configuration.
/// </summary>
string RevisionId => _revisionIdsTable.GetValue(this, static _ => Guid.NewGuid().ToString());
/// <summary>
/// Routes matching requests to clusters.
/// </summary>
IReadOnlyList<RouteConfig> Routes { get; }
/// <summary>
/// Cluster information for where to proxy requests to.
/// </summary>
IReadOnlyList<ClusterConfig> Clusters { get; }
/// <summary>
/// A notification that triggers when this snapshot expires.
/// </summary>
IChangeToken ChangeToken { get; }
}
可以看到GetConfig()
加载过来的路由和集群列表都是只读的集合。
重新加载(Reload)
如果IChangeToken
支持ActiveChangeCallbacks
,那么一旦代理处理了初始配置,它就会使用此令牌注册回调。如果提供程序不支持回调,则HasChanged
将每5分钟轮询一次。
当提供程序想要向代理提供新配置时,它应该:
- 在后台加载该配置。
- 路由和集群对象是不可变的,因此必须为任何新数据创建新实例。
- 可以重用未更改路由和集群的对象,也可以创建新实例 - 将通过差异来检测更改。
- (可选)使用 [
IConfigValidator
]( Interface IConfigValidator (microsoft.github.io) ) 验证配置,然后才IChangeToken
从先前IProxyConfig
实例发出新数据可用的信号。代理将再次调用GetConfig()
以检索新数据。
重新加载配置与第一次加载配置时存在重要差异:
- 新配置将与当前配置不同,并且只会更新修改的路由或集群。更新将以原子方式应用,并且仅影响新请求,而不会影响当前正在进行的请求。
- 重新加载过程中的任何错误都将被记录和禁止。应用程序将继续使用上次已知的正确配置。
- 如果
GetConfig()
抛出代,理将无法侦听将来的更改,因为IChangeToken
是一次性的。
一旦验证并应用了新配置,代理将使用新的IChangeToken
注册回调。请注意,如果有多个连续的重新加载信号,代理可能会跳过一些,并在准备好后立即加载下一个可用的配置。每个IProxyConfig
都包含完整的配置状态,因此不会丢失任何内容。
上述描述的是刷新配置相关的内容,具体代码参考
ListenForConfigChanges()
,可以结合代码一起看。
多配置源
参考前两篇文章的描述。
代码示例:
using Yarp.ReverseProxy.Configuration;
using Yarp.ReverseProxy.Model;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddReverseProxy()//添加ReverseProxy相关服务到DI
.LoadFromMemory(GetRoutes(), GetClusters());//从内存中加载配置
var app = builder.Build();
app.MapGet("/reload", (HttpContext httpContext) =>
{
httpContext.RequestServices.GetRequiredService<InMemoryConfigProvider>().Update(GetRoutes(), GetClusters("https://www.baidu.com"));
});
app.MapReverseProxy();
app.Run();
static RouteConfig[] GetRoutes()
{
return new[]
{
new RouteConfig()
{
RouteId = "route" + Random.Shared.Next(),
ClusterId = "cluster1",
Match = new RouteMatch
{
Path = "{**catch-all}"
}
}
};
}
static ClusterConfig[] GetClusters(string? address = null)
{
address ??= "https://cn.bing.com";
return new[]
{
new ClusterConfig()
{
ClusterId = "cluster1",
Destinations = new Dictionary<string, DestinationConfig>(StringComparer.OrdinalIgnoreCase)
{
{ "destination1", new DestinationConfig() { Address = address } }
}
}
};
}
上述代码中我们通过内存去加载代理所需要的配置,而且使用了刷新配置功能,当我们访问这个站点时(http://localhost:5270)代理的是必应的站点,当我们去请求reload方法时(http://localhost:5270/reload)他会更改代理配置,从而代理百度的站点。完整代码已上传GitHub,在 YARP.ConfigurationProvider.Memory 文件夹下。
总结
这一章我们介绍并实践了配置提供者,通过内存来提供配置,通过代码来实现配置加载和更新。
当然我们还可以自定义配置提供者只要实现上述两个接口就可以。你可以实现基于 Redis
的和 数据库 的 配置提供者,感兴趣的小伙伴可以研究一下,这两种实现起来应该还是比较简单的(等有时间了,我会写个demo示例,先把规划的文章写完了再去搞这个)。
这篇文章就到这里,下一篇我们继续介绍配置过滤器(Configuration Filters)