YARP HTTP Client Configuration (Cấu hình HTTP Client của YARP)
Giới thiệu
Mỗi Cluster có một instance HttpMessageInvoker riêng được sử dụng để chuyển tiếp các yêu cầu đến các Destination của nó. Cấu hình được định nghĩa theo từng cluster. Khi khởi động YARP, tất cả các cluster đều nhận được instance HttpMessageInvoker mới, tuy nhiên nếu sau đó cấu hình cluster bị thay đổi, IForwarderHttpClientFactory sẽ chạy lại và quyết định xem có nên tạo HttpMessageInvoker mới hay tiếp tục sử dụng cái hiện có. Triển khai mặc định của IForwarderHttpClientFactory tạo một HttpMessageInvoker mới khi có thay đổi đối với HttpClientConfig.
Các thuộc tính của outgoing requests (yêu cầu đi ra) cho một cluster nhất định cũng có thể được cấu hình. Chúng được định nghĩa trong ForwarderRequestConfig.
Cấu hình được biểu diễn khác nhau tùy thuộc vào việc bạn đang sử dụng model IConfiguration hay model code-first (code ưu tiên).
IConfiguration
Các kiểu này tập trung vào việc định nghĩa cấu hình có thể serialize. Model cấu hình dựa trên code được mô tả bên dưới trong phần "Code Configuration".
HttpClient
Cấu hình HTTP client dựa trên HttpClientConfig và được biểu diễn bởi schema cấu hình sau. Nếu bạn cần cách tiếp cận chi tiết hơn, hãy sử dụng triển khai tùy chỉnh của IForwarderHttpClientFactory.
"HttpClient": {
"SslProtocols": [ "<protocol-names>" ],
"MaxConnectionsPerServer": "<int>",
"DangerousAcceptAnyServerCertificate": "<bool>",
"RequestHeaderEncoding": "<encoding-name>",
"ResponseHeaderEncoding": "<encoding-name>",
"EnableMultipleHttp2Connections": "<bool>",
"WebProxy": {
"Address": "<url>",
"BypassOnLocal": "<bool>",
"UseDefaultCredentials": "<bool>"
}
}Các cài đặt cấu hình:
- SslProtocols - Giao thức SSL được kích hoạt trên HTTP client đã cho. Tên giao thức được chỉ định dưới dạng mảng chuỗi. Giá trị mặc định là None.
"SslProtocols": [ "Tls11", "Tls12" ]
- MaxConnectionsPerServer - số lượng kết nối HTTP 1.1 tối đa mở đồng thời đến cùng một server. Giá trị mặc định là int32.MaxValue.
"MaxConnectionsPerServer": "10"
- DangerousAcceptAnyServerCertificate - cho biết liệu client có kiểm tra tính hợp lệ của chứng chỉ SSL của server hay không. Đặt thành
truesẽ hoàn toàn tắt validation (xác thực). Giá trị mặc định làfalse.
"DangerousAcceptAnyServerCertificate": "true"
- RequestHeaderEncoding - cho phép mã hóa khác ngoài ASCII cho outgoing request headers (header yêu cầu đi ra). Cài đặt giá trị này sẽ sử dụng
SocketsHttpHandler.RequestHeaderEncodingSelectorvà dùng mã hóa đã chọn cho tất cả các header. Giá trị sau đó được phân tích bởiEncoding.GetEncoding, sử dụng các giá trị như: "utf-8", "iso-8859-1", v.v.
"RequestHeaderEncoding": "utf-8"
- ResponseHeaderEncoding - cho phép mã hóa khác ngoài ASCII cho incoming response headers (header phản hồi đến). Cài đặt giá trị này sẽ sử dụng
SocketsHttpHandler.ResponseHeaderEncodingSelectorvà dùng mã hóa đã chọn cho tất cả các header. Giá trị sau đó được phân tích bởiEncoding.GetEncoding, sử dụng các giá trị như: "utf-8", "iso-8859-1", v.v.
"ResponseHeaderEncoding": "utf-8"
Lưu ý rằng nếu bạn đang sử dụng mã hóa khác ngoài ASCII, bạn cũng cần cấu hình server của mình để chấp nhận yêu cầu và/hoặc gửi phản hồi với các header như vậy. Ví dụ, khi sử dụng Kestrel làm server, hãy dùng KestrelServerOptions.RequestHeaderEncodingSelector / .ResponseHeaderEncodingSelector để cấu hình Kestrel cho phép header Latin1 ("iso-8859-1"):
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(kestrel =>
{
kestrel.RequestHeaderEncodingSelector = _ => Encoding.Latin1;
// và/hoặc
kestrel.ResponseHeaderEncodingSelector = _ => Encoding.Latin1;
});- EnableMultipleHttp2Connections - cho phép mở thêm các kết nối HTTP/2 đến cùng một server khi đạt đến số lượng concurrent streams (luồng đồng thời) tối đa trên tất cả các kết nối hiện có. Mặc định là
true. Xem SocketsHttpHandler.EnableMultipleHttp2Connections
"EnableMultipleHttp2Connections": false
- WebProxy - Cho phép gửi yêu cầu qua outbound HTTP proxy (proxy HTTP đi ra) để đến các destination. Xem
SocketsHttpHandler.Proxyđể biết chi tiết. - Address - Địa chỉ URL của outbound proxy.
- BypassOnLocal - Bool cho biết liệu các yêu cầu đến địa chỉ local có bỏ qua outbound proxy hay không.
- UseDefaultCredentials - Bool cho biết liệu thông tin xác thực ứng dụng hiện tại có được sử dụng để xác thực với outbound proxy hay không. ASP.NET Core không impersonate (mạo danh) người dùng đã xác thực cho các yêu cầu đi ra.
"WebProxy": {
"Address": "http://myproxy:8080",
"BypassOnLocal": "true",
"UseDefaultCredentials": "false"
}ForwarderHttpClientFactory mặc định đặt UseProxy thành false. Nếu bạn muốn sử dụng default system proxy (proxy hệ thống mặc định) thay vì chỉ định địa chỉ proxy tùy chỉnh, bạn có thể đặt lại UseProxy thành true bằng phương thức ConfigureHttpClient. Điều này hữu ích khi debug với các công cụ như Fiddler. Xem phần Configuring the http client để biết ví dụ.
HttpRequest
Cấu hình HTTP request dựa trên ForwarderRequestConfig và được biểu diễn bởi schema cấu hình sau.
"HttpRequest": {
"ActivityTimeout": "<timespan>",
"Version": "<string>",
"VersionPolicy": ["RequestVersionOrLower", "RequestVersionOrHigher", "RequestVersionExact"],
"AllowResponseBuffering": "<bool>"
}Các cài đặt cấu hình:
- ActivityTimeout - thời gian một yêu cầu được phép không hoạt động giữa bất kỳ hoạt động nào hoàn thành, sau đó nó sẽ bị hủy. Mặc định là 100 giây. Timeout sẽ reset khi nhận được response headers hoặc sau khi đọc hoặc ghi thành công bất kỳ dữ liệu yêu cầu, phản hồi hoặc streaming nào như gRPC hay WebSockets. TCP keep-alives và HTTP/2 protocol pings sẽ không reset timeout, nhưng WebSocket pings sẽ reset.
- Version - version của outgoing request. Các giá trị được hỗ trợ hiện tại là
1.0,1.1,2và3. Giá trị mặc định là 2. - VersionPolicy - xác định cách version cuối cùng được chọn cho các outgoing requests. Xem HttpRequestMessage.VersionPolicy. Giá trị mặc định là
RequestVersionOrLower. - AllowResponseBuffering - cho phép sử dụng write buffering (đệm ghi) khi gửi phản hồi trở lại cho client, nếu server hosting YARP (ví dụ: IIS) hỗ trợ. LƯU Ý: bật tính năng này có thể làm hỏng các kịch bản SSE (server side event - sự kiện phía server).
Ví dụ cấu hình
Ví dụ dưới đây cho thấy 2 mẫu cấu hình HTTP client và request cho cluster1 và cluster2.
{
"Clusters": {
"cluster1": {
"LoadBalancingPolicy": "Random",
"HttpClient": {
"SslProtocols": [
"Tls11",
"Tls12"
],
"MaxConnectionsPerServer": "10",
"DangerousAcceptAnyServerCertificate": "true"
},
"HttpRequest": {
"ActivityTimeout": "00:00:30"
},
"Destinations": {
"cluster1/destination1": {
"Address": "https://localhost:10000/"
},
"cluster1/destination2": {
"Address": "http://localhost:10010/"
}
}
},
"cluster2": {
"HttpClient": {
"SslProtocols": [
"Tls12"
]
},
"HttpRequest": {
"Version": "1.1",
"VersionPolicy": "RequestVersionExact"
},
"Destinations": {
"cluster2/destination1": {
"Address": "https://localhost:10001/"
}
}
}
}
}Code Configuration (Cấu hình bằng code)
Cấu hình HTTP client sử dụng kiểu HttpClientConfig.
Sau đây là ví dụ về HttpClientConfig sử dụng cấu hình dựa trên code. Một instance của HttpClientConfig được gán cho thuộc tính ClusterConfig.HttpClient trước khi truyền mảng cluster cho phương thức LoadFromMemory.
var routes = new[]
{
new RouteConfig()
{
RouteId = "route1",
ClusterId = "cluster1",
Match =
{
Path = "{**catch-all}"
}
}
};
var clusters = new[]
{
new ClusterConfig()
{
ClusterId = "cluster1",
Destinations =
{
{ "destination1", new DestinationConfig() { Address = "https://localhost:10000" } }
},
HttpClient = new HttpClientConfig { MaxConnectionsPerServer = 10, SslProtocols = SslProtocols.Tls11 | SslProtocols.Tls12 }
}
};
services.AddReverseProxy()
.LoadFromMemory(routes, clusters);Configuring the http client (Cấu hình http client)
ConfigureHttpClient cung cấp một callback để tùy chỉnh các cài đặt SocketsHttpHandler được sử dụng để proxy các yêu cầu. Điều này sẽ được gọi mỗi khi cluster được thêm hoặc thay đổi. Cài đặt cluster được áp dụng cho handler trước callback. Dữ liệu tùy chỉnh có thể được cung cấp trong cluster metadata. Ví dụ này cho thấy việc thêm một client certificate (chứng chỉ client) để xác thực proxy với các destination server.
var clientCert = new X509Certificate2("path");
services.AddReverseProxy()
.ConfigureHttpClient((context, handler) =>
{
handler.SslOptions.ClientCertificates.Add(clientCert);
});Sử dụng default system proxy
ForwarderHttpClientFactory mặc định đặt UseProxy thành false để tránh overhead của việc phát hiện và cấu hình proxy. Nếu bạn cần sử dụng default system proxy (ví dụ, khi debug với Fiddler hoặc các công cụ proxy khác), bạn có thể đặt lại UseProxy thành true:
services.AddReverseProxy()
.ConfigureHttpClient((context, handler) =>
{
handler.UseProxy = true;
});Cấu hình này cho phép HttpClient sử dụng cài đặt proxy mặc định của hệ thống mà không cần chỉ định thủ công địa chỉ proxy trong cấu hình WebProxy.
Custom IForwarderHttpClientFactory (Tùy chỉnh IForwarderHttpClientFactory)
Nếu cần kiểm soát trực tiếp việc xây dựng HTTP client, IForwarderHttpClientFactory mặc định có thể được thay thế bằng một triển khai tùy chỉnh. Đối với một số tùy chỉnh, bạn có thể kế thừa từ ForwarderHttpClientFactory mặc định và ghi đè các phương thức cấu hình client.
Nên đặt các thuộc tính SocketsHttpHandler sau đây với cùng giá trị như factory mặc định để duy trì hành vi reverse proxy đúng đắn và tránh overhead không cần thiết.
new SocketsHttpHandler
{
UseProxy = false,
AllowAutoRedirect = false,
AutomaticDecompression = DecompressionMethods.None,
UseCookies = false,
EnableMultipleHttp2Connections = true,
ActivityHeadersPropagator = new ReverseProxyPropagator(DistributedContextPropagator.Current),
ConnectTimeout = TimeSpan.FromSeconds(15),
};Luôn trả về instance HttpMessageInvoker thay vì instance HttpClient vốn kế thừa từ HttpMessageInvoker. HttpClient buffer (đệm) các phản hồi theo mặc định, điều này làm hỏng các kịch bản streaming và tăng mức sử dụng bộ nhớ và độ trễ.
Dữ liệu tùy chỉnh có thể được cung cấp trong cluster metadata.
Dưới đây là ví dụ về triển khai tùy chỉnh IForwarderHttpClientFactory.
public class CustomForwarderHttpClientFactory : IForwarderHttpClientFactory
{
public HttpMessageInvoker CreateClient(ForwarderHttpClientContext context)
{
var handler = new SocketsHttpHandler
{
UseProxy = false,
AllowAutoRedirect = false,
AutomaticDecompression = DecompressionMethods.None,
UseCookies = false,
EnableMultipleHttp2Connections = true,
ActivityHeadersPropagator = new ReverseProxyPropagator(DistributedContextPropagator.Current),
ConnectTimeout = TimeSpan.FromSeconds(15),
};
return new HttpMessageInvoker(handler, disposeHandler: true);
}
}