这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

YARP文章

YARP文章

1 - YARP 入门

YARP 入门

https://microsoft.github.io/reverse-proxy/articles/getting-started.html

YARP 是一个提供核心代理功能的类库,您可以通过添加或替换模块对其进行定制。YARP 目前以 NuGet 软件包和代码片段的形式提供。我们计划在未来提供一个项目模板和预构建的 exe。

YARP 是在 .NET Core 基础架构之上实现的,可在 Windows、Linux 或 MacOS 上使用。开发工作可通过 SDK 和您最喜欢的编辑器(Microsoft Visual Studio 或 Visual Studio Code)完成。

YARP 2.0.0 支持 ASP.NET Core 6.0 及更新版本。您可以从 https://dotnet.microsoft.com/download/dotnet/ 下载 .NET SDK。

备注:这里我下载了 .NET 6.0 for macOS Arm64 版本并安装

创建新项目

使用以下步骤创建的项目的完整版本可在 Minimal YARP Sample 中找到。有关不使用顶层语句的版本,请参阅基本 YARP 示例。

首先,使用命令行创建一个 “empty” ASP.NET Core 应用程序:

dotnet new web -n MyProxy -f net6.0

完成之后的目录结构如下:

ls -ls

8 -rw-r--r--  1 sky  staff  219 Jan 18 21:25 MyProxy.csproj
8 -rw-r--r--  1 sky  staff  135 Jan 18 21:25 Program.cs
0 drwxr-xr-x  3 sky  staff   96 Jan 18 21:25 Properties
8 -rw-r--r--  1 sky  staff  127 Jan 18 21:25 appsettings.Development.json
8 -rw-r--r--  1 sky  staff  151 Jan 18 21:25 appsettings.json
0 drwxr-xr-x  7 sky  staff  224 Jan 18 21:25 obj

用 vs code 打开这个项目。

修改 MyProxy.csproj 文件,增加内容:

<ItemGroup> 
 <PackageReference Include="Yarp.ReverseProxy" Version="2.0.0" />
</ItemGroup> 

添加 YARP 中间件

更新 Program.cs 以使用 YARP 中间件:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
var app = builder.Build();
app.MapReverseProxy();
app.Run();

配置

YARP 的配置在 appsettings.json 文件中定义。详情请参阅配置文件。

也可以通过编程提供配置。请参阅配置提供程序了解详情。

您可以通过查看 RouteConfig 和 ClusterConfig 进一步了解可用的配置选项。

修改 appsettings.json 文件,内容如下:

{
 "Logging": {
   "LogLevel": {
     "Default": "Information",
     "Microsoft": "Warning",
     "Microsoft.Hosting.Lifetime": "Information"
   }
 },
 "AllowedHosts": "*",
 "ReverseProxy": {
   "Routes": {
     "route1" : {
       "ClusterId": "cluster1",
       "Match": {
         "Path": "{**catch-all}"
       }
     }
   },
   "Clusters": {
     "cluster1": {
       "Destinations": {
         "destination1": {
           "Address": "https://example.com/"
         }
       }
     }
   }
 }
}

运行项目

使用在样本目录中调用的 dotnet run

dotnet run                         
Building...
info: Yarp.ReverseProxy.Configuration.ConfigProvider.ConfigurationConfigProvider[1]
      Loading proxy data from config.
warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
      No XML encryptor configured. Key {2e19e480-715d-4990-a187-831a2dbc72fc} may be persisted to storage in unencrypted form.
warn: Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer[5]
      The application is trying to access the ASP.NET Core developer certificate key. A prompt might appear to ask for permission to access the key. When that happens, select 'Always Allow' to grant 'dotnet' access to the certificate key in the future.
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: https://localhost:7243
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5260
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /Users/sky/work/code/yarp/MyProxy/

这时通过浏览器访问 http://localhost:5260

可以得到和直接访问 https://example.com/ 一样的页面。反向代理成功!

2 - 支持的运行时

YARP 支持的运行时

https://microsoft.github.io/reverse-proxy/articles/runtimes.html

YARP 2.0+ 支持 ASP.NET Core 6.0 及更新版本。您可以从 https://dotnet.microsoft.com/download/dotnet/ 下载 .NET SDK。有关具体的版本支持,请参阅 版本

YARP 正在利用新的 .NET 功能和优化功能。这意味着如果您运行的是以前版本的 ASP.NET,某些功能可能不可用。

相关的 6.0 运行时改进

  • HTTP/3 - 支持入站和出站连接(预览版)。
  • 分布式跟踪 .NET 6.0 内置了可配置的支持,YARP 可利用这些支持启用更多开箱即用的方案。
  • Http.sys Delegation–内核级 ASP.NET Core 6 功能允许将请求转移到不同的进程。
  • UseHttpLogging–包含一个额外的中间件组件,可用于提供有关请求和响应的更多详细信息。
  • 动态 HTTP/2 窗口缩放 - 提高高延迟连接上的 HTTP/2 下载速度。
  • 非验证标头 - 通过使用非验证 HttpClient 标头提高性能。

相关的 7.0 运行时改进

备注:新发布的8.0没有提及。

3 - 配置文件

YARP 配置文件

https://microsoft.github.io/reverse-proxy/articles/config-files.html

简介

反向代理可以使用 Microsoft.Extensions 中的 IConfiguration 抽象从文件加载路由和群集的配置。这里给出的示例使用的是 JSON,但任何 IConfiguration 源文件都可以使用。如果源文件发生变化,也无需重启代理即可更新配置。

加载配置

要从 IConfiguration 加载代理配置,请在 Program.cs 中添加以下代码:

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

// Add the reverse proxy capability to the server
builder.Services.AddReverseProxy()
    // Initialize the reverse proxy from the "ReverseProxy" section of configuration
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));

var app = builder.Build();

// Register the reverse proxy routes
app.MapReverseProxy();

app.Run();

注:有关中间件顺序的详细信息,请参阅此处。

可使用配置过滤器在加载过程中修改配置。

多个配置源

从 1.1 开始,YARP 支持从多个来源加载代理配置。LoadFromConfig 可以多次调用,引用不同的 IConfiguration 部分,也可以与不同的配置源(如 InMemory)相结合。路由可以引用其他来源的群集。注意,不支持为给定路由或群集合并不同来源的部分配置。

services.AddReverseProxy()
    .LoadFromConfig(Configuration.GetSection("ReverseProxy1"))
    .LoadFromConfig(Configuration.GetSection("ReverseProxy2"));

或者

services.AddReverseProxy()
    .LoadFromMemory(routes, clusters)
    .LoadFromConfig(Configuration.GetSection("ReverseProxy"));

配置合约

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

配置结构

配置由上文通过 Configuration.GetSection(“ReverseProxy”) 指定的命名部分组成,并包含路由和群集的子部分。

示例:

{
  "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/"
          }
        }
      }
    }
  }
}

路由

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

  • RouteId - 唯一名称
  • ClusterId - 指群集部分中的条目名称。
  • Match - 包含 Hosts 数组或 Path 模式字符串。Path 是 ASP.NET Core 路由模板,可按此处的说明进行定义。路由匹配基于具有最高优先级的最特定路由,如此处所述。可以使用 order 字段实现显式排序,值越小优先级越高。

可以在每个路由条目上配置 Headers, Authorization, CORS 和其他基于路由的策略。有关其他字段,请参阅 RouteConfig。

代理将应用给定的匹配标准和策略,然后将请求传递给指定的群集。

集群

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

有关其他字段,请参阅 ClusterConfig。

所有配置属性

{
  // 服务器监听的基本 URL,必须独立于下面的路由进行配置
  "Urls": "http://localhost:5000;https://localhost:5001",
  "Logging": {
    "LogLevel": {
      "default": "Information",
      // 取消注释可从运行时和代理中隐藏诊断信息
      // "Microsoft": "Warning",
      // "Yarp" : "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "ReverseProxy": {
    // 路由告诉代理转发哪些请求
    "Routes": {
      "minimumroute" : {
        // 匹配任何内容并将其路由到 www.example.com
        "ClusterId": "minimumcluster",
        "Match": {
          "Path": "{**catch-all}"
        }
      },
      "allrouteprops" : {
        // 匹配 /something/* 和路由到 "allclusterprops"
        "ClusterId": "allclusterprops", // 其中一个群集的名称
        "Order" : 100, // 数字越小优先级越高
        "MaxRequestBodySize" : 1000000, // 以字节为单位。可选择覆盖服务器的限制(默认为 30MB)。设置为 -1 则禁用。
        "AuthorizationPolicy" : "Anonymous", // 策略名称或 "Default","Anonymous"
        "CorsPolicy""Default"// 适用于此路由的 CorsPolicy 名称或 "Default","Disable"
        "Match": {
          "Path": "/something/{**remainder}", // 使用 ASP.NET 语法匹配的路径。
          "Hosts" : [ "www.aaaaa.com", "www.bbbbb.com"], // 要匹配的主机名,未指定的为任意主机名
          "Methods" : [ "GET", "PUT" ], // 匹配的 HTTP 方法,未指定的为所有方法
          "Headers": [// 要匹配的头信息,未指定的为任何头信息
            {
              "名称": "MyCustomHeader", // 标头名称
              "Values": [ "value1", "value2", "another value" ], // 匹配这些值中的任何一个
              "Mode": "ExactHeader", // 或 "HeaderPrefix", "Exists" , "Contains", "NotContains", "NotExists"
              "IsCaseSensitive": true
            }
          ],
          "QueryParameters": [ // 要匹配的查询参数,未指定的为任意参数
            {
              "名称": "MyQueryParameter", // 查询参数名称
              "Values": [ "value1", "value2", "another value" ], // 与这些值中的任意值进行匹配
              "模式": "精确"//或 "前缀","存在","包含","不包含"
              "IsCaseSensitive": true
            }
          ]
        },
        "MetaData"{ // 可由自定义扩展使用的键值对列表
          "MyName""MyValue"
        },
        "Transforms" : [ // 转换列表。有关详细信息,请参阅 "变换 "文章
          {
            "RequestHeader": "MyHeader",
            "Set": "MyValue",
          }
        ]
      }
    },
    // 集群告诉代理转发请求的位置和方式
    "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", // 可选择 "FirstAlphabetical", "Random", "RoundRobin", "LeastRequests" 或 "FirstAlphabetical", "Random", "RoundRobin", "LeastRequests".
        "SessionAffinity": {
          Enabled": true, // 默认为 "false
          "Policy": "Cookie"//默认,或者 "CustomHeader"(自定义头文件
          "FailurePolicy": "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", // Required
            "ReactivationPeriod" : "00:00:10" // 10s
          }
        },
        "HttpClient" : {用于联系目的地的 HttpClient 实例的配置
          "SSLrotocols" : "Tls13",
          "DangerousAcceptAnyServerCertificate" : false,
          "MaxConnectionsPerServer" : 1024,
          "EnableMultipleHttp2Connections" : true,
          "RequestHeaderEncoding" : "Latin1", // 如何解释请求头值中的非 ASCII 字符
          "ResponseHeaderEncoding" : "Latin1" // 如何解释响应头值中的非 ASCII 字符
        },
        "HttpRequest" : { // 向目的地发送请求的选项
          "ActivityTimeout" : "00:02:00",
          "Version" : "2",
          "VersionPolicy" : "RequestVersionOrLower",
          "AllowResponseBuffering" : "false" (允许响应缓冲)。
        },
        "MetaData" : { // 自定义键值对
          "TransportFailureRateHealthPolicy.RateLimit": "0.5", // 被动健康策略使用
          "MyKey" : "MyValue" 
        }
      }
    }
  }
}

更多信息,请参阅日志配置和 HTTP 客户端配置。

4 - 配置提供者

YARP 配置提供者

https://microsoft.github.io/reverse-proxy/articles/config-providers.html

简介

基本 Yarp 示例 显示代理配置是从 appsettings.json 中加载的。不过,代理配置可以通过编程从你选择的源代码中加载。您可以通过提供几个实现 IProxyConfigProviderIProxyConfig 的类来实现这一点。

请参阅 ReverseProxy.Code.Sample 以了解自定义配置提供程序的示例。

可以使用 Configuration Filters 在加载过程中修改配置。

结构体

IProxyConfigProvider 有一个方法 GetConfig(),该方法应返回一个 IProxyConfig 实例。IProxyConfig 包含当前路由和群集的列表,以及一个 IChangeToken 用来在这些信息过时并需要重新加载时通知代理,这将导致再次调用 GetConfig()

路由

路由部分是命名路由的无序集合。路由包含匹配项及其相关配置。路由至少需要以下字段:

  • RouteId - 唯一名称
  • ClusterId - 指群集部分中的条目名称。
  • Match - 包含 Hosts 数组或 Path 模式字符串。Path 是 ASP.NET Core 路由模板,可定义为此处解释

每个路由条目都可以配置 HeadersAuthorizationCORS 和其他基于路由的策略。有关其他字段,请参阅 RouteConfig

代理将应用给定的匹配标准和策略,然后将请求传递给指定的群集。

群集

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

有关其他字段,请参阅 ClusterConfig

内存配置

InMemoryConfigProvider 实现了 IProxyConfigProvider,可通过调用 LoadFromMemory 直接在代码中指定路由和集群。

services.AddReverseProxy().LoadFromMemory(routes, clusters)

要稍后更新配置,请解析服务容器中的 InMemoryConfigProvider 并使用新的路由和集群列表调用 Update

httpContext.RequestServices.GetRequiredService<InMemoryConfigProvider>().Update(routes, clusters)

生命周期

启动

应将 IProxyConfigProvider 作为单例在 DI 容器中注册。启动时,代理将解析该实例并调用 GetConfig()。在第一次调用时,提供程序可以选择

  • 如果提供程序因故无法生成有效的代理配置,则抛出异常。这将阻止应用程序启动。
  • 在加载配置时同步阻塞。这将阻止应用程序启动,直到有效路由数据可用。
  • 或者,它可以选择在后台加载配置时返回一个空的 IProxyConfig 实例。当配置可用时,提供程序需要触发 IChangeToken

代理会验证给定的配置,如果配置无效,就会产生异常,导致应用程序无法启动。提供程序可通过使用 IConfigValidator 对路由和群集进行预验证,并采取任何其认为适当的措施(如排除无效条目)来避免这种情况。

原子性

一旦通过 GetConfig() 交给代理,提供给代理的配置对象和集合应为只读,不得修改。

重新加载

如果 IChangeToken 支持 ActiveChangeCallbacks,代理处理完初始配置集后,就会使用此令牌注册一个回调。如果提供商不支持回调,则将每 5 分钟轮询一次 HasChanged。

当提供商想向代理提供新配置时,它应该这样做:

重新加载

如果 “IChangeToken” 支持 “ActiveChangeCallbacks”(活动更改回调),代理处理完初始配置集后,就会使用此令牌注册一个回调。如果提供商不支持回调,则将每 5 分钟轮询一次 “HasChanged”。

当提供程序要向代理提供新配置时,它应该:

  • 在后台加载该配置。
    • 路由和集群对象是不可变的,因此必须为任何新数据创建新实例。
    • 不变路由和群集的对象可以重复使用,也可以创建新实例,但会通过差分来检测变化。
  • 可选择使用 IConfigValidator 验证配置,然后才从先前的 IProxyConfig 实例向 IChangeToken 发送新数据可用的信号。代理会再次调用 GetConfig() 来获取新数据。

重新加载配置与首次加载配置有重要区别。

  • 新配置将与当前配置进行比较,只有修改过的路由或群集才会被更新。更新将以原子方式应用,只会影响新请求,而不会影响当前正在处理的请求。
  • 重载过程中出现的任何错误都将被记录并抑制。应用程序将继续使用上次已知的良好配置。
  • 如果 GetConfig()抛出,代理将无法监听未来的更改,因为 IChangeToken是一次性的。

一旦验证并应用了新配置,代理将使用新的 IChangeToken 注册一个回调。请注意,如果接连发出多个重新加载信号,代理可能会跳过其中一些,并在下一个可用配置准备就绪时立即加载。每个 IProxyConfig 都包含完整的配置状态,因此不会丢失任何内容。

示例

InMemoryConfigProvider](https://github.com/microsoft/reverse-proxy/blob/main/src/ReverseProxy/Configuration/InMemoryConfigProvider.cs) 提供了一个 IProxyConfigProvider 的示例,其中手动加载了路由和群集。