Welcome to YARP - 3 负载均衡 (Load Balancing)

目录

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.分布式跟踪

介绍

负载均衡(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" } }
        }
    }
};

效果展示:

GIF.gif

可以看到相同的地址会按顺序循环选择目标服务。这就是 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应用程序在负载均衡环境中有多个后端服务器,负载均衡器用于将客户端请求分发到这些服务器上。**在某些情况下,对于特定应用程序,需要确保来自同一客户端的请求在处理过程中被路由到同一台后端服务器,以便维护用户会话状态。 **

例如:

  1. 对于需要用户身份验证的应用程序,粘性会话可以确保用户登录后的会话状态在同一服务器上保持一致。这对于管理用户身份验证和权限非常重要。

  2. 瞬态缓存(例如内存中),其中第一个请求将数据从较慢的持久存储中提取到快速的本地缓存中,而其他请求仅使用缓存的数据,从而提高吞吐量。

配置

服务和中间件注册

会话关联服务由 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"
            }
        }
    }
}

用于配置与 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
        }
    }    
}
请求管道

粘性会话机制由服务(如上所述)和以下两个中间件实现:

  1. SessionAffinityMiddleware - 协调请求的关联解决过程。首先,它调用ClusterConfig.SessionAffinity.policy 属性,为给定群集指定的策略。然后,它检查策略返回的关联解析状态,并在出现故障时调用ClusterConfig.SessionAffinity.FailurePolicy上的故障处理策略。

它必须在负载平衡器之前添加到管道中。

public static IReverseProxyApplicationBuilder UseSessionAffinity(this IReverseProxyApplicationBuilder builder)
    {
        builder.UseMiddleware<SessionAffinityMiddleware>();//SessionAffinityMiddleware
        return builder;
    }
  1. 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的性能,这是一个园友贴出来的性能对比图。img

看到了这两个(nginxhaproxy)我们想到了 k8s Ingress。k8s 的 Ingress Controller常用的两款软件就是 NginxHaproxy。 而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限流 功能

posted @ 2023-11-04 15:31  coding-y  阅读(545)  评论(0编辑  收藏  举报