Welcome to YARP - 3 负载均衡 (Load Balancing)
目录
Welcome to YARP - 1.认识YARP并搭建反向代理服务
- 2.1 - 配置文件(Configuration Files)
- 2.2 - 配置提供者(Configuration Providers)
- 2.3 - 配置过滤器(Configuration Filters)
介绍
负载均衡(Load Balancing)是一种用于分发网络流量或工作负载的技术,旨在确保多个服务器或资源之间的负载均衡分布,以提高性能、可用性和可伸缩性。负载均衡通常用于网络服务器、Web服务器、应用程序服务器和其他计算资源,以分散请求并优化资源利用。
在YARP
中,每当有多个正常运行的目标(服务)可用时,YARP
会决定将哪一个用于给定请求。YARP
附带内置负载均衡算法,当然你也可以自定义负载均衡算法(本文也会涉及)。
接下来带大家一步一步的去配置和使用YARP
的负载均衡功能。
负载均衡配置
服务和中间件注册
负载均衡策略通过该方法 AddLoadBalancingPolicies()
在 DI
容器中注册,该方法由 AddReverseProxy()
自动调用。
中间件添加 UseLoadBalancing()
,默认情况下包含在 MapReverseProxy
的无参数方法中。
集群配置
用于确定目标的算法可以通过设置 . LoadBalancingPolicy
如果未指定策略,则将使用 :PowerOfTwoChoices
(随机找两个,然后把请求分配给最少的):
配置示例
"ReverseProxy": {
"Routes": {
"route1": {
"ClusterId": "cluster1",
"Match": {
"Path": "{**catch-all}"
}
}
},
"Clusters": {
"cluster1": {
"LoadBalancingPolicy": "RoundRobin",
"Destinations": {
"cluster1/destination1": {
"Address": "https://www.baidu.com/"
},
"cluster1/destination2": {
"Address": "https://cn.bing.com/"
}
}
}
}
}
代码示例
var clusters = new[]
{
new ClusterConfig()
{
ClusterId = "cluster1",
LoadBalancingPolicy = LoadBalancingPolicies.RoundRobin,
Destinations = new Dictionary<string, Destination>(StringComparer.OrdinalIgnoreCase)
{
{ "destination1", new DestinationConfig() { Address = "https://localhost:10000" } },
{ "destination2", new DestinationConfig() { Address = "https://localhost:10010" } }
}
}
};
效果展示:
可以看到相同的地址会按顺序循环选择目标服务。这就是 RoundRobin
策列的效果。
内置策略
YARP
附带以下内置策略:
-
FirstAlphabetical
选择按字母顺序排列的第一个可用目标,而不考虑负载。这对于双目标故障转移系统非常有用。
-
PowerOfTwoChoices
(默认策略)选择两个随机目标,然后选择请求分配最少的目标。这避免了
LeastRequest
的开销,也避免了Random
选择繁忙目标的最坏情况。 -
RoundRobin
通过按顺序循环选择目的地。
-
LeastRequests
选择请求分配最少的目标。这需要检查所有目标。
自定义策略
ILoadBalancingPolicy
负责从可用的健康目标列表中选择目标。
可以在 DI
中提供自定义实现。
// 实现 ILoadBalancingPolicy 接口
public sealed class LastLoadBalancingPolicy : ILoadBalancingPolicy
{
public string Name => "Last";
public DestinationState? PickDestination(HttpContext context, ClusterState cluster, IReadOnlyList<DestinationState> availableDestinations)
{
return availableDestinations[^1];
}
}
// 注册服务到 DI
services.AddSingleton<ILoadBalancingPolicy, LastLoadBalancingPolicy>();
// 设置 cluster 的 LoadBalancingPolicy 属性为我们上述定义好的名称(Last)
// 可在代码里设置 也可以在 配置文件里设置,取决于你使用什么配置提供者
cluster.LoadBalancingPolicy = "Last";
此策略会一直选择可用目标的最后一个,这里就不做演示了,这里只是告诉大家如何去自定义策略。
粘性会话
粘性会话 也被称为"会话持久性"或"会话粘性",是一种在Web应用程序负载均衡中的会话管理技术。它用于确保来自同一客户端的多个请求在负载均衡环境下被路由到同一个后端服务器,以保持用户会话的连续性。
通常,Web应用程序在负载均衡环境中有多个后端服务器,负载均衡器用于将客户端请求分发到这些服务器上。**在某些情况下,对于特定应用程序,需要确保来自同一客户端的请求在处理过程中被路由到同一台后端服务器,以便维护用户会话状态。 **
例如:
-
对于需要用户身份验证的应用程序,粘性会话可以确保用户登录后的会话状态在同一服务器上保持一致。这对于管理用户身份验证和权限非常重要。
-
瞬态缓存(例如内存中),其中第一个请求将数据从较慢的持久存储中提取到快速的本地缓存中,而其他请求仅使用缓存的数据,从而提高吞吐量。
配置
服务和中间件注册
会话关联服务由 AddReverseProxy()
自动在 DI 容器中注册(AddSessionAffinityPolicies
)。默认情况下,中间件 UseSessionAffinity()
包含在无参数的 MapReverseProxy
方法中。如果要自定义代理管道,请在添加 UseLoadBalancing()
.
app.MapReverseProxy(proxyPipeline =>
{
proxyPipeline.UseSessionAffinity();
proxyPipeline.UseLoadBalancing();
});
注意:某些会话关联实现依赖于数据保护,这将需要对多个代理实例等方案进行额外配置。有关详细信息,请参阅密钥保护,本文不再涉及。
集群配置
"ReverseProxy": {
"Clusters": {
"<cluster-name>": {
"SessionAffinity": {
"Enabled": "(true|false)", // defaults to 'false'
"Policy": "(HashCookie|ArrCookie|Cookie|CustomHeader)", // defaults to 'HashCookie'
"FailurePolicy": "(Redistribute|Return503Error)", // defaults to 'Redistribute'
"AffinityKeyName": "Key1",
"Cookie": {
"Domain": "localhost",
"Expiration": "03:00:00",
"HttpOnly": true,
"IsEssential": true,
"MaxAge": "1.00:00:00",
"Path": "mypath",
"SameSite": "Strict",
"SecurePolicy": "Always"
}
}
}
}
Cookie 配置
用于配置与 HashCookie、ArrCookie 和 Cookie 策略一起使用的 cookie 的属性可以使用 SessionAffinityCookieConfig
进行配置。属性可以是如上所示的 JSON 配置,也可以是如下所示的代码:
new ClusterConfig
{
ClusterId = "cluster1",
SessionAffinity = new SessionAffinityConfig
{
Enabled = true,
FailurePolicy = "Return503Error",
Policy = "HashCookie",
AffinityKeyName = "Key1",
Cookie = new SessionAffinityCookieConfig
{
Domain = "mydomain",
Expiration = TimeSpan.FromHours(3),
HttpOnly = true,
IsEssential = true,
MaxAge = TimeSpan.FromDays(1),
Path = "mypath",
SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict,
SecurePolicy = Microsoft.AspNetCore.Http.CookieSecurePolicy.SameAsRequest
}
}
}
请求管道
粘性会话机制由服务(如上所述)和以下两个中间件实现:
SessionAffinityMiddleware
- 协调请求的关联解决过程。首先,它调用ClusterConfig.SessionAffinity.policy
属性,为给定群集指定的策略。然后,它检查策略返回的关联解析状态,并在出现故障时调用ClusterConfig.SessionAffinity.FailurePolicy
上的故障处理策略。
它必须在负载平衡器之前添加到管道中。
public static IReverseProxyApplicationBuilder UseSessionAffinity(this IReverseProxyApplicationBuilder builder)
{
builder.UseMiddleware<SessionAffinityMiddleware>();//SessionAffinityMiddleware
return builder;
}
AffinitizeTransform
- 如果为请求建立了新的关联,则在响应上设置键。否则,如果请求遵循现有相关性,则不执行任何操作。这将自动添加为响应转换。
public static IReverseProxyBuilder AddSessionAffinityPolicies(this IReverseProxyBuilder builder)
{
builder.Services.TryAddEnumerable(new[] {
ServiceDescriptor.Singleton<IAffinityFailurePolicy, RedistributeAffinityFailurePolicy>(),
ServiceDescriptor.Singleton<IAffinityFailurePolicy, Return503ErrorAffinityFailurePolicy>()
});
builder.Services.TryAddEnumerable(new[] {
ServiceDescriptor.Singleton<ISessionAffinityPolicy, CookieSessionAffinityPolicy>(),
ServiceDescriptor.Singleton<ISessionAffinityPolicy, HashCookieSessionAffinityPolicy>(),
ServiceDescriptor.Singleton<ISessionAffinityPolicy, ArrCookieSessionAffinityPolicy>(),
ServiceDescriptor.Singleton<ISessionAffinityPolicy, CustomHeaderSessionAffinityPolicy>()
});
builder.AddTransforms<AffinitizeTransformProvider>();//AffinitizeTransform
return builder;
}
相关代码已经贴上去,详细的处理过程可以自己去了解一下。
请注意,粘性会话也可能会引入一些挑战,例如单点故障问题和服务器不均衡。因此,在选择使用粘性会话时,需要仔细考虑应用程序的需求和负载均衡策略。不是所有应用程序都需要粘性会话,而有时可以使用其他方法来管理会话状态。选择是否使用粘性会话通常取决于特定的业务需求。
扩展知识
与本章节的内容关系不大
在其他文章的评论里我们了解到YARP
的性能,这是一个园友贴出来的性能对比图。
看到了这两个(nginx
和 haproxy
)我们想到了 k8s
的 Ingress
。k8s 的 Ingress Controller
常用的两款软件就是 Nginx
和 Haproxy
。 而YARP
有一个k8s的扩展包就是对k8s的支持, 用于监视 kubernetes
入口对象并将 yarp
配置为指向服务 ips 。对于.NET 并且上了 k8s 的团队 而言,YARP
也是一个不错的选择。如何使用请参考: 如何在 Docker Desktop (KinD) 的 Kubernetes 中使用 YARP 作为入口控制器 Windows
我曾经待过的一个公司就规划了基于YARP并结合我们自己的配置中心或另启一个
Gateway
项目专门做网关,而且还要和 K8S 的 Ingress Controller 打通,所有的配置都可以在 配置中心 或者Gateway
项目中进行操作,不仅运维方便,而且不熟悉k8s
的开发也可以操作。
总结
本章我们使用YARP对服务进行了负载均衡配置,而且有不同的策略供我们选择,可以达到故障转移,优化资源等效果。本章源码已上传GitHub
在 YARP.LoadBalancing 文件夹下
下篇文章我们继续讲如何使用YARP
的 限流 功能