Yarp 讓系統內排程更靈活

妙蛙草發表於2021-11-29

簡介

Yarp 是微軟團隊開發的一個反向代理元件, 除了常規的 http 和 https 轉換通訊,它最大的特點是可定製化,很容易根據特定場景開發出需要的定製代理通道。

詳細介紹:https://devblogs.microsoft.com/dotnet/announcing-yarp-1-0-release

原始碼倉庫:https://github.com/microsoft/reverse-proxy

文件地址 :https://microsoft.github.io/reverse-proxy/

 

基礎使用

1、建立 ASP.NET Core  空專案

使用 Visual Studio :

Yarp 讓系統內排程更靈活
 
使用 .NET CLI 命令列建立:
dotnet new web -o MyProxy

2、 修改程式碼 Program.cs 檔案

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
var app = builder.Build();
app.MapGet("/Ping", () => "Hello World!");
app.MapReverseProxy();
app.Run();

3、修改配置檔案 appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ReverseProxy": {
    "Routes": {
      "routeAll": {
        "ClusterId": "clusterBaidu",
        "Match": {
          "Path": "{**catch-all}"
        }
      }
    },
    "Clusters": {
      "clusterBaidu": {
        "Destinations": {
          "baidu": {
            "Address": "https://www.baidu.com/"
          }
        }
      }
    }
  }
}

這裡的配置是將所有的請求都轉發到百度。

在 Program.cs 裡,還註冊了一個 Get 路由 Ping 。

4、啟動專案

Yarp 讓系統內排程更靈活

能夠看到在瀏覽器訪問程式監聽的埠號後,顯示的是百度的頁面。開啟 F12 ,看到請求頭也是本地的,並不是百度的域名。

測試手動註冊的路由 Ping :

Yarp 讓系統內排程更靈活

能夠顯示正常。

5、問題整理

(1) Yarp 是不是隻能做這種簡單的轉發?

不是,往下有配置檔案說明。

(2) JSON 配置檔案裡有什麼要注意的地方嗎?

有。在這個演示的配置檔案中 ReverseProxy:Clusters:cluster1:Destinations:destination1:Address 對應的值是:https://www.baidu.com/ ,如果去掉 www ,在專案啟動後會跳轉到百度首頁,不是代理轉發。去掉末尾的 / 符合沒有任何影響。

(3) Yarp 會影響到程式中註冊的路由嗎?

不會影響到程式內部註冊的路由。在 Program.cs 中無論 app.MapReverseProxy(); 在上還是在下,在訪問 Ping 的時候,都是返回 Hello World!

var app = builder.Build();
app.MapReverseProxy();
app.MapGet("/Ping", () => "Hello World!");
app.Run();

 

進階探索

1、多地址代理

 修改配置檔案 appsettings.json ,實現預設路由跳轉百度,當訪問 /movie 是訪問 b站。

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ReverseProxy": {
    "Routes": {
      "routeBaidu": {
        "ClusterId": "clusterBaidu",
        "Match": {
          "Path": "{**catch-all}"
        }
      },
      "routeBiliBili": {
        "ClusterId": "clusterBiliBili",
        "Match": {
          "Path": "/movie/{**catch-all}"
        }
      }
    },
    "Clusters": {
      "clusterBaidu": {
        "Destinations": {
          "baidu": {
            "Address": "https://www.baidu.com/"
          }
        }
      },
      "clusterBiliBili": {
        "Destinations": {
          "bilibili": {
            "Address": "https://www.bilibili.com/"
          }
        }
      }
    }
  }
}

 測試結果:

Yarp 讓系統內排程更靈活

Yarp 讓系統內排程更靈活

在後面輸入路由 /movie 後能夠跳轉到b站。但是b站網頁沒有完整顯示,圖片都沒有,這是網站上的策略問題,對於資料介面沒有這些問題。

Yarp 讓系統內排程更靈活

詳細的配置檔案說明,可以檢視 https://microsoft.github.io/reverse-proxy/articles/config-files.html

2、規則匹配

網頁上太多資源,為了方便測試,啟用兩個 api 介面。地址分別是:http://localhost:5241/ 和 https://localhost:7184/

兩個 api 介面中分別註冊 /test 路由。

// http://localhost:5241/
app.MapGet("/test", () => "Welcome to Api111!");

// https://localhost:7184/
app.MapGet("/test", () => "Welcome to Api222!");

啟動兩個 api 程式。

C:\Users\Test>curl http://localhost:5241/test
Welcome to Api111!

C:\Users\Test>curl https://localhost:7184/test
Welcome to Api222!

修改 MyProxy 專案的配置檔案 appsettings.json 

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ReverseProxy": {
    "Routes": {
      "routeOne": {
        "ClusterId": "clusterOne",
        "Match": {
          "Path": "/test/{**catch-all}",
          "QueryParameters": [
            {
              "Name": "number",
              "Values": [ "1" ]
            }
          ]
        }
      },
      "routeTwo": {
        "ClusterId": "clusterTwo",
        "Match": {
          "Path": "/test/{**catch-all}",
          "QueryParameters": [
            {
              "Name": "number",
              "Values": [ "2" ]
            }
          ]
        }
      },
      "routeBaidu": {
        "ClusterId": "clusterBaidu",
        "Match": {
          "Path": "{**catch-all}"
        }
      }
    },
    "Clusters": {
      "clusterOne": {
        "Destinations": {
          "apiOne": {
            "Address": "http://localhost:5241/"
          }
        }
      },
      "clusterTwo": {
        "Destinations": {
          "apiTwo": {
            "Address": "https://localhost:7184/"
          }
        }
      },
      "clusterBaidu": {
        "Destinations": {
          "baidu": {
            "Address": "https://www.baidu.com/"
          }
        }
      }
    }
  }
}

Path :監聽路由地址。

QueryParameters:匹配引數。

QueryParameters:Name:引數名。

QueryParameters:Values:引數值。

MyProxy 的監聽埠是 http://localhost:5024/ 訪問結果如下

C:\Users\Test>curl http://localhost:5024/ping
Hello World!

C:\Users\Test>curl http://localhost:5024/test
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /test was not found on this server.</p>
</body></html>

C:\Users\Test>curl http://localhost:5024/test?number=1
Welcome to Api111!

C:\Users\Test>curl http://localhost:5024/test?number=2
Welcome to Api222!

能夠根據引數以及引數值導向對應的地址。

3、問題整理

(1)為什麼訪問 /movie 不能正常顯示網頁。

因為 b站某些介面開啟了防盜鏈,還有跨域檢測。

Yarp 讓系統內排程更靈活

Yarp 讓系統內排程更靈活

(2)在根據引數匹配中,如果匹配的路由一樣,監聽的引數一樣,引數值也一樣會怎麼樣?

訪問該路由地址會報錯。

(3)路由匹配的優先順序?

程式內註冊的路由優先順序最高,其次才是 Yarp 在配置檔案里載入的。

 

小試牛刀

加班寫業務中,待完成。

 

踩坑集錦

1、non-ASCII 

專案要代理某網頁,在使用下載功能的時候,介面返回 502 。

info: Yarp.ReverseProxy.Forwarder.HttpForwarder[48]
      ResponseHeaders: The destination returned a response that cannot be proxied back to the client.
      System.InvalidOperationException: Invalid non-ASCII or control character in header: 0x00E4
         at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ThrowInvalidHeaderCharacter(Char ch)
         at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ValidateHeaderValueCharacters(StringValues headerValues)
         at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpResponseHeaders.SetValueFast(String key, StringValues value)
         at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.Microsoft.AspNetCore.Http.IHeaderDictionary.set_Item(String key, StringValues value)
         at Yarp.ReverseProxy.Forwarder.HttpTransformer.CopyResponseHeaders(HttpHeaders source, IHeaderDictionary destination)
         at Yarp.ReverseProxy.Forwarder.HttpTransformer.TransformResponseAsync(HttpContext httpContext, HttpResponseMessage proxyResponse)
         at Yarp.ReverseProxy.Transforms.Builder.StructuredTransformer.TransformResponseAsync(HttpContext httpContext, HttpResponseMessage proxyResponse)
         at Yarp.ReverseProxy.Forwarder.HttpForwarder.SendAsync(HttpContext context, String destinationPrefix, HttpMessageInvoker httpClient, ForwarderRequestConfig requestConfig, HttpTransformer transformer)

去 GitHub 翻 Issues

Yarp 讓系統內排程更靈活

下載介面能正常訪問,檔案流也能完整地拿到。重寫了所有的響應頭沒有用。這種不開源的商業站點,也猜不到字元編碼。

最後妥協了,用了一個 .NET 服務在伺服器上下載後再轉發。

代理非常規服務介面時,一定要多測試。

 

相關文章