目錄
Welcome to YARP - 1.認識YARP並搭建反向代理服務
- 2.1 - 配置檔案(Configuration Files)
- 2.2 - 配置提供者(Configuration Providers)
- 2.3 - 配置過濾器(Configuration Filters)
Welcome to YARP - 7.健康檢查
Welcome to YARP - 8.分散式跟蹤
介紹
閘道器的 快取和 壓縮是常見的效能最佳化手段 ,用於提高系統的響應速度和降低網路傳輸的開銷。
YARP 的 快取 和 壓縮 其實也是 .NET 本身的功能。只需要配置 .NET 本身的快取和壓縮功能即可。
快取
快取的主要目的是最佳化效能、提高效率,減少對後端服務的負擔。 我們可以對
頻繁請求的靜態資料或不經常更改的資料 進行 API 快取,從而降低對後端服務的請求次數,提高響應速度,減輕後端服務的負載。
也可以對 靜態資源 快取 如:css、js、影像等,從而 加速頁面載入速度,減輕伺服器壓力,提高使用者體驗。 等等。
基於 HTTP 的響應快取
用於快取的主 HTTP 標頭是 Cache-Control,它用於指定快取指令。 當請求從客戶端到達伺服器以及響應從伺服器返回客戶端時,這些指令控制快取行為。 請求和響應在代理伺服器之間移動,並且代理伺服器還必須符合 HTTP 1.1 快取規範。
Cache-Control
通用訊息頭欄位,被用於在 http 請求和響應中,透過指定指令來實現快取機制。快取指令是單向的,這意味著在請求中設定的指令,不一定被包含在響應中。要使用快取,請求頭中必須攜帶
Cache-Control
標頭。同時響應頭中也要做相應的設定。
下表中顯示了常用 Cache-Control
指令
指令 | 操作 |
---|---|
public | 快取可以儲存響應。 |
private | 響應不得由共享快取儲存。 專用快取可以儲存和重用響應。 |
max-age | 客戶端不接受期限大於指定秒數的響應。 示例:max-age=60 (60 秒),max-age=2592000 (1 個月) |
no-cache | 請求時:快取不能使用儲存的響應來滿足請求。 源伺服器重新生成客戶端的響應,中介軟體更新其快取中儲存的響應。 響應時:響應不得用於未經源伺服器驗證的後續請求。 |
no-store | 請求時:快取不得儲存請求。 響應時:快取不得儲存任何部分的響應。 |
相應的我們也要新增響應快取中介軟體:
若要測試響應快取,請使用 Fiddler、Postman 或其他可以顯式設定請求標頭的工具。顯式設定上述的請求標頭。
配置中介軟體
在 中 Program.cs
,將響應快取中介軟體服務 AddResponseCaching 新增到服務集合中,並將應用配置為將中介軟體與 UseResponseCaching 擴充套件方法一起使用。 UseResponseCaching
將中介軟體新增到請求處理管道中:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddReverseProxy()//新增ReverseProxy相關服務到DI
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));//從配置檔案中載入ReverseProxy的設定
builder.Services.AddResponseCaching(options =>
{
options.UseCaseSensitivePaths = false; //確定是否將響應快取在區分大小寫的路徑上。
options.SizeLimit = options.SizeLimit * 10; // 響應快取中介軟體的大小限制(以位元組為單位) 1G
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
// 使用 CORS 中介軟體時,必須在 UseResponseCaching 之前呼叫 UseCors。
// app.UseCors();
app.UseRouting();
// 攔截請求並判斷 請求頭中是否包含 CacheControl 標頭,如果沒有則加上快取標頭
app.Use(async (context, next) =>
{
var header = context.Request.Headers;
var cacheControl = header.CacheControl;
if (!string.IsNullOrEmpty(header.CacheControl))
{
header.CacheControl = new Microsoft.Extensions.Primitives.StringValues("max-age");
}
await next(context);
});
app.UseResponseCaching();
app.Use(async (context, next) =>
{
context.Response.GetTypedHeaders().CacheControl =
new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
context.Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.Vary] = new string[] { "Accept-Encoding" };
await next(context);
});
app.MapReverseProxy();
app.MapGet("/", () => DateTime.Now.ToLongTimeString());
app.Run();
以上示例中:
- Cache-Control:快取可快取響應長達10秒。
- Vary:將中介軟體配置為僅當後續請求的 Accept-Encoding 標頭與原始請求頭匹配時才提供快取的響應。
app.Use(async (context, next) =>
{
var header = context.Request.Headers;
var cacheControl = header.CacheControl;
if (!string.IsNullOrEmpty(header.CacheControl))
{
header.CacheControl = new Microsoft.Extensions.Primitives.StringValues("max-age");
}await next(context);
});
可以看到我們先設定了 請求頭的 快取標頭,如果 沒有此 設定,幾乎所有瀏覽器(標頭值)都會傳送 CacheControl:no-cache 或其他值,強制執行非快取頁面請求,這會導致 ResponseCachingMiddleware 實現忽略此請求(忽略快取)並將其傳遞到伺服器以檢索資料。 如果你用 postman 或者 fiddle則可以主動這隻CacheControl標頭值為上述說的那幾種。
上述示例中我們先 新增了 YARP
服務,然後新增了 AddResponseCaching
響應的快取服務,然後攔截了請求並設定快取標頭,再開啟了 UseResponseCaching()
響應快取中介軟體。接下來設定了 [響應頭的標頭值]( ASP.NET Core 中的響應快取中介軟體 | Microsoft Learn ),最後開啟了 代理 中介軟體。原始碼已上傳GitHub.
壓縮
網路頻寬是一種有限資源。 減小響應大小通常可顯著提高應用的響應速度。 減小有效負載大小的一種方式是壓縮應用的響應。 但是 YARP
預設是禁用解壓縮的,因為它會增加 CPU 開銷。
什麼時候使用 響應壓縮 中介軟體?
在 IIS、Apache 或 Nginx 中使用基於伺服器的響應壓縮技術。
而 HTTP.sys 伺服器和 Kestrel 伺服器當前不提供內建壓縮支援,這時候就需要使用響應壓縮中介軟體了。
我們使用 YARP 的直接轉發來 演示一下 壓縮功能,當然壓縮是 .NET 本身自帶的功能,你也可以透過新增壓縮中介軟體來開啟響應壓縮。
程式碼示例:
using System.Diagnostics;
using System.Net;
using Yarp.ReverseProxy.Forwarder;
using Yarp.ReverseProxy.Transforms;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpForwarder();
// Add services to the container.
var app = builder.Build();
// Configure our own HttpMessageInvoker for outbound calls for proxy operations
var httpClient = new HttpMessageInvoker(new SocketsHttpHandler()
{
UseProxy = false,
AllowAutoRedirect = false,
AutomaticDecompression = DecompressionMethods.GZip, // 設定響應壓縮方式
UseCookies = false,
ActivityHeadersPropagator = new ReverseProxyPropagator(DistributedContextPropagator.Current),
ConnectTimeout = TimeSpan.FromSeconds(15),
});
// Setup our own request transform class
var transformer = new CustomTransformer(); // or HttpTransformer.Default;
var requestConfig = new ForwarderRequestConfig { ActivityTimeout = TimeSpan.FromSeconds(100) };
app.UseRouting();
// Configure the HTTP request pipeline.
app.MapForwarder("/{**catch-all}", "http://localhost:5047", requestConfig, transformer, httpClient);
app.Run();
/// <summary>
/// 自定義請求轉換
/// </summary>
class CustomTransformer : HttpTransformer
{
///<summary>
/// A callback that is invoked prior to sending the proxied request. All HttpRequestMessage
/// fields are initialized except RequestUri, which will be initialized after the
/// callback if no value is provided. The string parameter represents the destination
/// URI prefix that should be used when constructing the RequestUri. The headers
/// are copied by the base implementation, excluding some protocol headers like HTTP/2
/// pseudo headers (":authority").
///</summary>
///<param name="httpContext">傳入請求</param>
///<param name="proxyRequest">傳出的代理請求</param>
///<param name="destinationPrefix">所選目標伺服器的uri字首,可用於建立RequestUri</param>
public override async ValueTask TransformRequestAsync(HttpContext httpContext, HttpRequestMessage proxyRequest, string destinationPrefix, CancellationToken cancellationToken)
{
// 轉發所有頭部資訊
await base.TransformRequestAsync(httpContext, proxyRequest, destinationPrefix, cancellationToken);
// 自定義查詢query 值
var queryContext = new QueryTransformContext(httpContext.Request);
queryContext.Collection.Remove("param1");
queryContext.Collection["s"] = "xx2";
// 分配自定義 URI。在此處連線時請注意額外的斜槓。RequestUtilities.MakeDestinationAddress 是一個安全的預設值。
proxyRequest.RequestUri = RequestUtilities.MakeDestinationAddress("http://localhost:5047", httpContext.Request.Path, queryContext.QueryString);
// 禁止原始請求標頭,使用目標 Uri 中的標頭
proxyRequest.Headers.Host = null;
}
}
上述示例中,我們使用了 YARP 的直接轉發模式,不需要新增 YARP 服務和中介軟體,但是要新增 這個東西 AddHttpForwarder,然後配置 自定義請求轉換 類。並開啟 壓縮模式為 GZip。
如果想看效果記得使用fiddle
抓取請求的返回值檢視原始請求,因為postman和瀏覽器客戶端 都會預設對常用的壓縮格式的資料進行解壓縮。
如果伺服器本身支援壓縮,請使用 基於伺服器的壓縮技術。而 HTTP.sys 伺服器和 Kestrel 伺服器 是不支援壓縮技術的,所以這時候才考慮使用 壓縮中介軟體。
總結
本章我們介紹了 YARP
的快取和壓縮功能,其實也都是 .NET 自身的功能,如果瞭解使用過 .NET的 快取 和 壓縮 中介軟體很容易就能理解。本章示例程式碼已上傳GitHub,建議把程式碼down下來自己實驗一下,再去配合理解。
有什麼問題歡迎留言交流。
下篇文章我們繼續介紹 YARP
的健康檢查功能。