YARP 中间件

介绍

ASP.NET Core 使用 中间件管道 将请求处理划分为离散步骤。 应用开发人员可以根据需要添加和订购中间件。 ASP.NET 核心中间件还用于实现和自定义反向代理功能。

违约

入门 示例显示了以下 Configure 方法。 这将设置一个中间件管道,其中包含开发工具、路由和代理配置的终结点(MapReverseProxy)。

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

ReverseProxyIEndpointRouteBuilderExtensions 重载中的无参数 MapReverseProxy() 包括会话亲和性负载均衡被动运行状况检查和请求的最终代理的所有标准代理中间件。 其中每个检查匹配路由、群集和目标的配置,并相应地执行其任务。

添加中间件

添加到应用程序管道的中间件将看到请求处于不同处理状态,具体取决于中间件的添加位置。 在 UseRouting 之前添加的中间件将查看所有请求,并可以在任何路由发生之前对其进行操作。 在 UseRoutingUseEndpoints 之间添加的中间件可以调用 HttpContext.GetEndpoint() 来检查与请求匹配的终结点路由(如果有),并使用与该终结点关联的任何元数据。 这是如何处理 身份验证、授权CORS

ReverseProxyIEndpointRouteBuilderExtensions 提供了一个 MapReverseProxy 的重载,使你能够构建一个中间件管道,该管道仅在与代理配置路由匹配的请求上运行。

app.MapReverseProxy(proxyPipeline =>
{
    proxyPipeline.Use((context, next) =>
    {
        // Custom inline middleware

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

默认情况下,MapReverseProxy 的此重载仅包括管道开头和末尾的最小设置、代理逻辑和限制强制执行。 默认情况下不包括用于会话相关性、负载均衡和被动运行状况检查的中间件,因此可以使用任何其他中间件排除、替换或控制其排序。

自定义代理中间件

MapReverseProxy 管道内的中间件可通过 IReverseProxyFeature访问与请求(路由、群集、目标等)关联的所有代理数据和状态。 这可从 HttpContext.Features 或扩展方法 HttpContext.GetReverseProxyFeature()获取。

IReverseProxyFeature 中的数据是从代理管道开始时的代理配置快照的,不会受到处理请求时发生的代理配置更改的影响。

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

    return next();
});

中间件的用途

中间件可以生成日志、控制请求是否得到代理、影响其代理位置,并添加其他功能,例如错误处理、重试等。

日志和指标

中间件可以检查请求和响应字段以生成日志和聚合指标。 请参阅下面“中间件禁忌操作”下的关于主体的注意事项。

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

发送即时响应

如果中间件检查请求并确定它不应进行代理,则它可能会生成自己的响应并将控制权返回到服务器,而无需调用 next()

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

    return next();
});

筛选目标

会话相关性和负载均衡等中间件检查 IReverseProxyFeature 和群集配置,以确定应将请求发送到的目标。

AllDestinations 列出所选群集中的所有目标。

AvailableDestinations 列出了当前被认为有资格处理请求的目标。 它被初始化为 AllDestinations,如果启用了运行状况检查,则不包括运行不正常的项。 AvailableDestinations 应在管道结束时减少到单个目标,否则将从其余部分随机选择一个目标。

ProxiedDestination 由管道末尾的代理逻辑设置,以指示哪个目标最终被使用。 如果没有剩余的可用目标,则会发送 503 错误响应。

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

    await next();

    Report(proxyFeature.ProxiedDestination);
});

DestinationState 实现 IReadOnlyList<DestinationState>,因此无需创建新列表即可将单个目标分配给 AvailableDestinations

错误处理

中间件可以在 try/catch 块中包装对 await next() 的调用,以处理来自后续组件的异常。

管道末尾的代理逻辑(IHttpForwarder)不会引发常见请求代理错误的异常。 这些在 HttpContext.FeaturesHttpContext.GetForwarderErrorFeature() 扩展方法提供的 IForwarderErrorFeature 中捕获和报告。

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

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

如果响应尚未启动(HttpResponse.HasStarted),则可以清除(HttpResponse.Clear())并发送备用响应,或者可以重置代理功能字段并重试请求。

中间件禁忌操作

中间件应谨慎修改请求字段(如标头),以干预传出的代理请求。 此类修改可能会干扰重试等功能,并且可以通过 转换更好地处理。

中间件必须在调用 HttpResponse.HasStarted后修改响应字段之前检查 next()。 如果响应已经开始发送给客户端,则中间件不能再对其进行修改(预告片除外)。 转换可用于检查和抑制不需要的响应。 否则,请参阅下一条注释。

中间件应避免与请求或响应正文交互。 默认情况下,内容不会缓冲,因此与其交互可能会妨碍它们到达目标。 尽管可以启用缓冲,但不建议这样做,因为它可能会增加大量的内存和延迟开销。 如果必须检查或修改正文,建议使用封装的流式方法。 有关示例,请参阅 ResponseCompression 中间件。

中间件不得对单个请求执行任何多线程工作,HttpContext 及其关联的成员不是线程安全的。