Nguon: Microsoft Learn · .NET 8.0

YARP Middleware (Phần mềm trung gian YARP)

Nguồn: YARP Middleware

Giới thiệu

ASP.NET Core sử dụng một pipeline middleware (đường ống phần mềm trung gian) để chia xử lý request thành các bước riêng biệt. Nhà phát triển ứng dụng có thể thêm và sắp xếp middleware theo nhu cầu. ASP.NET Core middleware cũng được sử dụng để triển khai và tùy chỉnh chức năng reverse proxy (proxy ngược).

Mặc định

Mẫu getting started thể hiện phương thức Configure sau. Đoạn code này thiết lập một pipeline middleware với các công cụ phát triển, routing (định tuyến), và các endpoint được cấu hình proxy (MapReverseProxy).

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

Overload MapReverseProxy() không tham số trong ReverseProxyIEndpointRouteBuilderExtensions bao gồm tất cả proxy middleware tiêu chuẩn cho session affinity (ái lực phiên), load balancing (cân bằng tải), passive health checks (kiểm tra sức khỏe thụ động), và bước cuối cùng là proxy request. Mỗi bước này kiểm tra cấu hình của route, cluster và destination đã khớp, sau đó thực hiện nhiệm vụ tương ứng.

Thêm Middleware

Middleware được thêm vào pipeline ứng dụng sẽ thấy request ở các trạng thái xử lý khác nhau tùy thuộc vào vị trí thêm middleware. Middleware thêm trước UseRouting sẽ thấy tất cả các request và có thể thao tác chúng trước khi bất kỳ routing nào diễn ra. Middleware thêm giữa UseRoutingUseEndpoints có thể gọi HttpContext.GetEndpoint() để kiểm tra endpoint nào mà routing đã khớp với request (nếu có), và sử dụng bất kỳ metadata nào được liên kết với endpoint đó. Đây là cách Authentication (Xác thực), Authorization (Phân quyền)CORS được xử lý.

ReverseProxyIEndpointRouteBuilderExtensions cung cấp một overload của MapReverseProxy cho phép bạn xây dựng một middleware pipeline chỉ chạy cho các request khớp với các route được cấu hình proxy.

csharp
app.MapReverseProxy(proxyPipeline =>
{
    proxyPipeline.Use((context, next) =>
    {
        // Custom inline middleware (middleware tùy chỉnh nội tuyến)

        return next();
    });
    proxyPipeline.UseSessionAffinity();
    proxyPipeline.UseLoadBalancing();
    proxyPipeline.UsePassiveHealthChecks();
});

Theo mặc định, overload này của MapReverseProxy chỉ bao gồm thiết lập tối thiểu, logic proxy, và giới hạn thực thi ở đầu và cuối pipeline. Middleware cho session affinity, load balancing, và passive health checks không được bao gồm theo mặc định để bạn có thể loại trừ, thay thế, hoặc kiểm soát thứ tự của chúng cùng với bất kỳ middleware bổ sung nào.

Custom Proxy Middleware (Middleware Proxy Tùy chỉnh)

Middleware bên trong pipeline MapReverseProxy có quyền truy cập vào tất cả dữ liệu và trạng thái proxy liên quan đến một request (route, cluster, destinations, v.v.) thông qua IReverseProxyFeature. Điều này có sẵn từ HttpContext.Features hoặc phương thức mở rộng HttpContext.GetReverseProxyFeature().

Dữ liệu trong IReverseProxyFeature được snapshot từ cấu hình proxy lúc bắt đầu pipeline proxy và sẽ không bị ảnh hưởng bởi các thay đổi cấu hình proxy xảy ra trong khi request đang được xử lý.

csharp
proxyPipeline.Use((context, next) =>
{
    var proxyFeature = context.GetReverseProxyFeature();
    var cluster = proxyFeature.Cluster;
    var destinations = proxyFeature.AvailableDestinations;

    return next();
});

Những gì có thể làm với middleware

Middleware có thể tạo log, kiểm soát xem một request có được proxy không, ảnh hưởng đến nơi nó được proxy đến, và thêm các tính năng bổ sung như xử lý lỗi, retry (thử lại), v.v.

Log và Metrics (Nhật ký và Chỉ số)

Middleware có thể kiểm tra các trường request và response để tạo log và tổng hợp metrics. Xem ghi chú về body trong phần "Những gì không nên làm với middleware" bên dưới.

csharp
proxyPipeline.Use(async (context, next) =>
{
    LogRequest(context);
    await next();
    LogResponse(context);
});

Gửi phản hồi ngay lập tức

Nếu một middleware kiểm tra request và xác định rằng request đó không nên được proxy, nó có thể tự tạo response và trả về quyền kiểm soát cho server mà không gọi next().

csharp
proxyPipeline.Use((context, next) =>
{
    if (!CheckAllowedRequest(context, out var reason))
    {
        context.Response.StatusCode = StatusCodes.Status400BadRequest;
        return context.Response.WriteAsync(reason);
    }

    return next();
});

Lọc destinations (đích đến)

Middleware như session affinity và load balancing xem xét IReverseProxyFeature và cấu hình cluster để quyết định destination nào một request nên được gửi đến.

AllDestinations liệt kê tất cả các destination trong cluster đã chọn.

AvailableDestinations liệt kê các destination hiện tại được coi là đủ điều kiện để xử lý request. Nó được khởi tạo bằng AllDestinations, loại trừ những destination không khỏe mạnh nếu health checks được bật. AvailableDestinations nên được thu hẹp xuống còn một destination duy nhất ở cuối pipeline, nếu không thì một destination sẽ được chọn ngẫu nhiên từ phần còn lại.

ProxiedDestination được thiết lập bởi logic proxy ở cuối pipeline để chỉ ra destination nào đã được sử dụng. Nếu không còn destination nào khả dụng thì một response lỗi 503 sẽ được gửi.

csharp
proxyPipeline.Use(async (context, next) =>
{
    var proxyFeature = context.GetReverseProxyFeature();
    proxyFeature.AvailableDestinations = Filter(proxyFeature.AvailableDestinations);

    await next();

    Report(proxyFeature.ProxiedDestination);
});

DestinationState triển khai IReadOnlyList<DestinationState> vì vậy một destination đơn lẻ có thể được gán cho AvailableDestinations mà không cần tạo list mới.

Xử lý lỗi

Middleware có thể bọc lệnh gọi await next() trong một khối try/catch để xử lý ngoại lệ từ các thành phần sau.

Logic proxy ở cuối pipeline (IHttpForwarder) không ném ngoại lệ cho các lỗi proxy request phổ biến. Những lỗi này được thu thập và báo cáo trong IForwarderErrorFeature có sẵn từ HttpContext.Features hoặc phương thức mở rộng HttpContext.GetForwarderErrorFeature().

csharp
proxyPipeline.Use(async (context, next) =>
{
    await next();

    var errorFeature = context.GetForwarderErrorFeature();
    if (errorFeature is not null)
    {
        Report(errorFeature.Error, errorFeature.Exception);
    }
});

Nếu response chưa bắt đầu (HttpResponse.HasStarted), nó có thể được xóa (HttpResponse.Clear()) và gửi một response thay thế, hoặc các trường proxy feature có thể được đặt lại và request được thử lại.

Những gì không nên làm với middleware

Middleware nên thận trọng khi sửa đổi các trường request như headers để ảnh hưởng đến request proxy đi. Các sửa đổi như vậy có thể can thiệp với các tính năng như retry và có thể được xử lý tốt hơn bởi transforms.

Middleware PHẢI kiểm tra HttpResponse.HasStarted trước khi sửa đổi các trường response sau khi gọi next(). Nếu response đã bắt đầu được gửi đến client thì middleware không còn có thể sửa đổi nó (ngoại trừ có thể Trailers). Transforms có thể được sử dụng để kiểm tra và ngăn chặn các response không mong muốn. Nếu không, xem ghi chú tiếp theo.

Middleware nên tránh tương tác với request hoặc response body. Body không được buffer theo mặc định, vì vậy tương tác với chúng có thể ngăn chúng đến đích. Mặc dù có thể bật buffering, nhưng điều này không được khuyến khích vì nó có thể thêm overhead đáng kể về bộ nhớ và độ trễ. Sử dụng cách tiếp cận wrapped, streaming được khuyến nghị nếu body phải được kiểm tra hoặc sửa đổi. Xem middleware ResponseCompression làm ví dụ.

Middleware KHÔNG ĐƯỢC thực hiện bất kỳ công việc đa luồng nào trên một request riêng lẻ, HttpContext và các thành viên liên quan của nó không an toàn cho luồng.