Reverse Proxy 的新選擇 — Yarp

OrangeRed
9 min readDec 4, 2022

--

Yarp怎麼來的? 從官網所述,Yarp的誕生是因為微軟的內部團隊都存在著反向代理的需求,而這些有需求的人聚在一起開發一個共同的Solution — Yarp

跟其他同類型的Reverse Proxy相比

來源: https://msit.powerbi.com/view?r=eyJrIjoiYTZjMTk3YjEtMzQ3Yi00NTI5LTg5ZDItNmUyMGRlOTkwMGRlIiwidCI6IjcyZjk4OGJmLTg2ZjEtNDFhZi05MWFiLTJkN2NkMDExZGI0NyIsImMiOjV9

來源 microsoft/reverse-proxy#40 (2020)

其實可以看到效能是很優異的,雖然跟NGINX比起來有點落差,記憶體也吃了500M左右 😆 與其他Reverse Proxy相比他的優點是

  1. 使用C#開發
  2. 設定簡單易懂(最基本的只要Config給一給)
  3. 目前是微軟的開源專案

針對 .NET的開發者而言,我覺得光是使用C#開發這點就很值得用了,相較之下學習成本更低也更友善,但Nginx和Envoy相比之下更泛用,我認為以方便和學習性而言Yarp是個很值得使用的Reverse Proxy。

Yarp的Config主要是由Routes和Cluster組成

Routes

包含路徑的參數和相關聯的Cluster,有幾個參數必須要有

  • RouteID — 不可重複的名稱
  • ClusterId — 指向對應的Cluster
  • Match — 限制Route的內容必須符合

Clusters

Cluster的部分通常是對應著Route,設置的選項很多,主要是包含著目的地的名稱和位址,剩下常見LoadBalanc和HealthCheck也是在這邊設定

Example

再來就是簡單的範例,首先最基本的是要安裝Yarp.ReverseProxy

再來起一台WebHost,並且DI相關的Service

var builder = WebApplication.CreateBuilder(args);
var services = builder.Services;
var config = builder.Configuration;

services.AddReverseProxy().LoadFromConfig(config.GetSection("ReverseProxy"));

var app = builder.Build();
app.MapReverseProxy();
app.Run();

再來是Config設定

  "ReverseProxy": {
"Routes": {
"route1": {
"ClusterId": "cluster1",
"Match": {
"Path": "/api/{**catch-all}"
}
}
},
"Clusters": {
"cluster1": {
"Destinations": {
"LoadBalancingPolicy": "RoundRobin",
"Destination1": {
"Address": "https://localhost:7180/"
},
"Destination2": {
"Address": "https://localhost:7289/"
}
}
}
}
}

接下來準備另外兩台要被代理的Host,讓他們只回簡單的Hello API

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("api/hello", () => "Hello API 1");
app.Run();
==============================================================
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("api/hello", () => "Hello API 2");
app.Run();

最後看看Log,可以發現同樣的Route他兩台都有代理到,代表LoadBalancing的設定有成功啦

雖然大部分的反向代理Server都是用config來設定,但我們總會希望多一點彈性,甚至是說讓他也可以做discovery service這件事,所以Yarp有提供給我們用程式去做,但在這邊除了上面列的其實還可以想一想彼此的優缺點,用config的好處是什麼?用程式的方式好處又是什麼?各自的缺點和後續帶來的影響是什麼?

接下來就把剛剛設定的config改成用Code的方式。

首先繼承 IProxyConfigProvider,他會要我們實現GetConfig這個方法

    public class ProxyConfigProvider : IProxyConfigProvider
{
public IProxyConfig GetConfig()
{
return new ProxyConfig();
}
}

再來就是實作的部分,就只是把剛剛appsetting.json的內容搬進來而已

    public class ProxyConfig : IProxyConfig
{
private readonly List<RouteConfig> _routes;
private readonly List<ClusterConfig> _clusters;
private readonly CancellationChangeToken _changeToken;
public IReadOnlyList<RouteConfig> Routes => _routes;
public IReadOnlyList<ClusterConfig> Clusters => _clusters;
public IChangeToken ChangeToken => _changeToken;

public ProxyConfig()
{
_routes = GenerateRoutes();
_clusters = GenerateClusters();
var cancellationTokenSource = new CancellationTokenSource();
_changeToken = new CancellationChangeToken(cancellationTokenSource.Token);
}

private List<RouteConfig> GenerateRoutes()
{
return new List<RouteConfig>
{
new RouteConfig()
{
RouteId = "route1",
ClusterId = "cluster1",
Match = new RouteMatch()
{
Path = "/api/{**catch-all}"
}
}
};
}

private List<ClusterConfig> GenerateClusters()
{
return new List<ClusterConfig>
{
new ClusterConfig()
{
ClusterId = "cluster1",
LoadBalancingPolicy = "RoundRobin",
Destinations = new Dictionary<string, DestinationConfig>{
{
"Destination1", new DestinationConfig()
{
Address = "https://localhost:7180/"
}
},
{
"Destination2", new DestinationConfig()
{
Address = "https://localhost:7289/"
}
}
}
}
};
}
}

Program的部分

var builder = WebApplication.CreateBuilder(args);
var services = builder.Services;
services.AddSingleton<IProxyConfigProvider, ProxyConfigProvider>();
services.AddReverseProxy();

var app = builder.Build();
app.MapReverseProxy();
app.Run();

驗證結束

reference:

YARP Documentation (microsoft.github.io)

--

--