YARP — Xây dựng API Gateway cho Microservices trên .NET 10

Posted on: 4/24/2026 8:11:50 PM

50M+ NuGet downloads YARP
~0.2ms Overhead mỗi request
HTTP/3 Hỗ trợ giao thức mới nhất
100% Tích hợp ASP.NET Core

Trong kiến trúc microservices, API Gateway là thành phần không thể thiếu — đóng vai trò cổng vào duy nhất cho mọi request từ client, xử lý cross-cutting concerns như authentication, rate limiting, load balancing và request transformation. Thay vì dùng các giải pháp bên ngoài như Kong hay Nginx, hệ sinh thái .NET có YARP (Yet Another Reverse Proxy) — một reverse proxy hiệu năng cao, được Microsoft phát triển và sử dụng nội bộ cho Azure, Bing và Microsoft 365.

1. YARP là gì và tại sao nên dùng?

YARP là một thư viện reverse proxy mã nguồn mở, xây dựng trên nền ASP.NET Core. Không giống các API Gateway truyền thống hoạt động như một ứng dụng độc lập, YARP được nhúng trực tiếp vào pipeline ASP.NET Core — cho phép tận dụng toàn bộ middleware ecosystem có sẵn.

graph LR
    A[Client Mobile/Web/SPA] -->|HTTPS| B[YARP API Gateway
.NET 10] B -->|Route /api/orders/*| C[Order Service
Port 5001] B -->|Route /api/products/*| D[Product Service
Port 5002] B -->|Route /api/users/*| E[User Service
Port 5003] B -->|Route /api/notifications/*| F[Notification Service
Port 5004] style B fill:#e94560,stroke:#fff,color:#fff style C fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style D fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style E fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style F fill:#f8f9fa,stroke:#e94560,color:#2c3e50
Kiến trúc API Gateway với YARP — single entry point cho microservices

Tại sao không dùng Ocelot?

Ocelot từng là lựa chọn phổ biến cho .NET API Gateway, nhưng dự án đã chậm cập nhật và không theo kịp các tính năng mới của ASP.NET Core. YARP được Microsoft chính thức hỗ trợ, cập nhật cùng mỗi bản .NET mới, và đã chứng minh hiệu năng vượt trội trong production tại quy mô Microsoft.

2. Cài đặt và cấu hình cơ bản

2.1. Khởi tạo project

dotnet new webapi -n ApiGateway
cd ApiGateway
dotnet add package Yarp.ReverseProxy

2.2. Cấu hình Program.cs

var builder = WebApplication.CreateBuilder(args);

// Đăng ký YARP services
builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));

var app = builder.Build();

// Map reverse proxy vào pipeline
app.MapReverseProxy();

app.Run();

2.3. Cấu hình Routes và Clusters trong appsettings.json

{
  "ReverseProxy": {
    "Routes": {
      "orders-route": {
        "ClusterId": "orders-cluster",
        "Match": {
          "Path": "/api/orders/{**catch-all}"
        },
        "Transforms": [
          { "PathRemovePrefix": "/api/orders" }
        ]
      },
      "products-route": {
        "ClusterId": "products-cluster",
        "Match": {
          "Path": "/api/products/{**catch-all}"
        },
        "Transforms": [
          { "PathRemovePrefix": "/api/products" }
        ]
      }
    },
    "Clusters": {
      "orders-cluster": {
        "LoadBalancingPolicy": "RoundRobin",
        "Destinations": {
          "orders-1": { "Address": "https://localhost:5001" },
          "orders-2": { "Address": "https://localhost:5011" }
        }
      },
      "products-cluster": {
        "Destinations": {
          "products-1": { "Address": "https://localhost:5002" }
        }
      }
    }
  }
}

Hai khái niệm cốt lõi

Routes định nghĩa pattern matching cho incoming request (path, headers, query string). Clusters đại diện cho nhóm backend services nhận request — mỗi cluster có thể có nhiều destination để load balancing.

3. Load Balancing — Phân tải thông minh

YARP hỗ trợ nhiều thuật toán load balancing, cho phép chọn chiến lược phù hợp với từng loại service.

PolicyMô tảKhi nào dùng
RoundRobinLuân phiên đều giữa các instanceCác instance cùng cấu hình
LeastRequestsGửi đến instance ít request nhấtRequest có thời gian xử lý khác nhau
RandomChọn ngẫu nhiênTest, development
PowerOfTwoChoicesChọn tốt hơn trong 2 instance ngẫu nhiênCân bằng giữa ngẫu nhiên và optimal
FirstAlphabeticalLuôn gửi đến instance đầu tiênActive-passive failover

3.1. Health Checks — Tự động phát hiện lỗi

YARP hỗ trợ cả ActivePassive health checks để tự động loại bỏ instance không khỏe mạnh khỏi pool.

{
  "orders-cluster": {
    "LoadBalancingPolicy": "RoundRobin",
    "HealthCheck": {
      "Active": {
        "Enabled": true,
        "Interval": "00:00:10",
        "Timeout": "00:00:05",
        "Policy": "ConsecutiveFailures",
        "Path": "/health"
      },
      "Passive": {
        "Enabled": true,
        "Policy": "TransportFailure",
        "ReactivationPeriod": "00:00:30"
      }
    },
    "Destinations": {
      "orders-1": { "Address": "https://localhost:5001" },
      "orders-2": { "Address": "https://localhost:5011" }
    }
  }
}
sequenceDiagram
    participant C as Client
    participant G as YARP Gateway
    participant S1 as Service Instance 1
    participant S2 as Service Instance 2

    Note over G: Active Health Check mỗi 10s
    G->>S1: GET /health
    S1-->>G: 200 OK ✓
    G->>S2: GET /health
    S2-->>G: 503 Error ✗

    Note over G: Đánh dấu S2 unhealthy
    C->>G: GET /api/orders/123
    G->>S1: Forward request
    S1-->>G: 200 Response
    G-->>C: 200 Response

    Note over G: 30s sau — reactivation
    G->>S2: GET /health
    S2-->>G: 200 OK ✓
    Note over G: S2 trở lại healthy
Luồng Health Check — tự động loại bỏ và phục hồi instance

4. Authentication & Authorization tập trung

Một trong những lợi thế lớn nhất của API Gateway là tập trung xác thực tại một điểm duy nhất, giúp các downstream services không phải tự validate token.

var builder = WebApplication.CreateBuilder(args);

// Cấu hình JWT Authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Authority = "https://auth.example.com";
        options.Audience = "api-gateway";
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            ValidateIssuer = true,
            ValidateAudience = true,
            ClockSkew = TimeSpan.Zero
        };
    });

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AdminOnly", policy =>
        policy.RequireRole("admin"));
    options.AddPolicy("ReadAccess", policy =>
        policy.RequireAuthenticatedUser());
});

builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();
app.MapReverseProxy();

app.Run();

Áp dụng policy lên từng route:

{
  "Routes": {
    "admin-route": {
      "ClusterId": "admin-cluster",
      "AuthorizationPolicy": "AdminOnly",
      "Match": { "Path": "/api/admin/{**catch-all}" }
    },
    "public-route": {
      "ClusterId": "products-cluster",
      "AuthorizationPolicy": "anonymous",
      "Match": { "Path": "/api/products/{**catch-all}" }
    }
  }
}

5. Rate Limiting — Bảo vệ backend

Từ .NET 7, ASP.NET Core tích hợp sẵn Rate Limiting middleware. YARP tận dụng trực tiếp tính năng này qua RateLimiterPolicy trên mỗi route.

builder.Services.AddRateLimiter(options =>
{
    options.AddFixedWindowLimiter("fixed-policy", opt =>
    {
        opt.PermitLimit = 100;
        opt.Window = TimeSpan.FromMinutes(1);
        opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
        opt.QueueLimit = 10;
    });

    options.AddSlidingWindowLimiter("sliding-policy", opt =>
    {
        opt.PermitLimit = 200;
        opt.Window = TimeSpan.FromMinutes(1);
        opt.SegmentsPerWindow = 4;
    });

    options.AddTokenBucketLimiter("token-bucket", opt =>
    {
        opt.TokenLimit = 50;
        opt.ReplenishmentPeriod = TimeSpan.FromSeconds(10);
        opt.TokensPerPeriod = 10;
        opt.AutoReplenishment = true;
    });

    options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
});

// Trong pipeline
app.UseRateLimiter();
app.MapReverseProxy();
{
  "Routes": {
    "orders-route": {
      "ClusterId": "orders-cluster",
      "RateLimiterPolicy": "fixed-policy",
      "Match": { "Path": "/api/orders/{**catch-all}" }
    }
  }
}

6. Request Transforms — Biến đổi request/response

YARP cung cấp hệ thống transform mạnh mẽ để thay đổi request trước khi forward và response trước khi trả về client.

graph LR
    A[Client Request] --> B[YARP Gateway]
    B --> C{Transforms}
    C --> D[Path Rewrite]
    C --> E[Header Inject]
    C --> F[Query String]
    D --> G[Backend Service]
    E --> G
    F --> G
    G --> H{Response Transforms}
    H --> I[Header Remove]
    H --> J[Body Transform]
    I --> K[Client Response]
    J --> K

    style B fill:#e94560,stroke:#fff,color:#fff
    style C fill:#2c3e50,stroke:#fff,color:#fff
    style H fill:#2c3e50,stroke:#fff,color:#fff
Pipeline Transform của YARP — xử lý request và response

6.1. Transform qua cấu hình

{
  "Routes": {
    "v2-products": {
      "ClusterId": "products-v2-cluster",
      "Match": { "Path": "/api/v2/products/{**remainder}" },
      "Transforms": [
        { "PathPattern": "/products/{**remainder}" },
        { "RequestHeader": "X-Gateway", "Set": "YARP" },
        { "RequestHeader": "X-Request-Id", "Set": "{random-guid}" },
        { "ResponseHeaderRemove": "X-Internal-Debug" },
        { "X-Forwarded": "Set", "For": "Append", "Proto": "Set" }
      ]
    }
  }
}

6.2. Custom Transform bằng code

builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"))
    .AddTransforms(context =>
    {
        // Thêm correlation ID cho distributed tracing
        context.AddRequestTransform(transformContext =>
        {
            var correlationId = transformContext.HttpContext
                .Request.Headers["X-Correlation-Id"]
                .FirstOrDefault() ?? Guid.NewGuid().ToString();

            transformContext.ProxyRequest.Headers
                .Add("X-Correlation-Id", correlationId);

            return ValueTask.CompletedTask;
        });

        // Log response status
        context.AddResponseTransform(transformContext =>
        {
            var logger = transformContext.HttpContext
                .RequestServices.GetRequiredService<ILogger<Program>>();

            logger.LogInformation(
                "Proxy response: {StatusCode} for {Path}",
                transformContext.ProxyResponse?.StatusCode,
                transformContext.HttpContext.Request.Path);

            return ValueTask.CompletedTask;
        });
    });

7. Backend-For-Frontend (BFF) Pattern

YARP cho phép triển khai BFF pattern — tạo các gateway chuyên biệt cho từng loại client (mobile, web, third-party) bằng cách routing dựa trên headers.

{
  "Routes": {
    "mobile-orders": {
      "ClusterId": "mobile-bff-cluster",
      "Match": {
        "Path": "/api/orders/{**catch-all}",
        "Headers": [
          {
            "Name": "X-Client-Type",
            "Values": ["mobile"],
            "Mode": "ExactHeader"
          }
        ]
      }
    },
    "web-orders": {
      "ClusterId": "web-bff-cluster",
      "Match": {
        "Path": "/api/orders/{**catch-all}",
        "Headers": [
          {
            "Name": "X-Client-Type",
            "Values": ["web"],
            "Mode": "ExactHeader"
          }
        ]
      }
    }
  }
}
graph TD
    A[Mobile App] -->|X-Client-Type: mobile| G[YARP Gateway]
    B[Web SPA] -->|X-Client-Type: web| G
    C[Third-party] -->|API Key| G

    G --> D[Mobile BFF
Payload tối ưu, ít data] G --> E[Web BFF
Payload đầy đủ, rich UI] G --> F[Partner API
Versioned, throttled] D --> H[Order Service] D --> I[User Service] E --> H E --> I E --> J[Analytics Service] F --> H style G fill:#e94560,stroke:#fff,color:#fff style D fill:#f8f9fa,stroke:#4CAF50,color:#2c3e50 style E fill:#f8f9fa,stroke:#2196F3,color:#2c3e50 style F fill:#f8f9fa,stroke:#ff9800,color:#2c3e50
BFF Pattern — Gateway chuyên biệt cho từng loại client

8. Canary Deployment & A/B Testing

YARP hỗ trợ phân phối traffic theo tỉ lệ — nền tảng cho canary deployment và A/B testing mà không cần infrastructure riêng.

builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"))
    .AddTransforms(context =>
    {
        context.AddRequestTransform(transformContext =>
        {
            // A/B testing dựa trên user ID hash
            var userId = transformContext.HttpContext.User
                .FindFirst("sub")?.Value ?? "anonymous";
            var bucket = Math.Abs(userId.GetHashCode()) % 100;

            if (bucket < 10) // 10% traffic đến version mới
            {
                transformContext.ProxyRequest.Headers
                    .Add("X-Feature-Group", "canary");
            }

            return ValueTask.CompletedTask;
        });
    });
{
  "Clusters": {
    "products-cluster": {
      "LoadBalancingPolicy": "RoundRobin",
      "Destinations": {
        "stable": {
          "Address": "https://products-v1:5002",
          "Metadata": { "Weight": "90" }
        },
        "canary": {
          "Address": "https://products-v2:5012",
          "Metadata": { "Weight": "10" }
        }
      }
    }
  }
}

9. Observability — Giám sát và tracing

YARP tích hợp hoàn hảo với OpenTelemetry, cung cấp traces, metrics và logs xuyên suốt từ gateway đến backend.

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        tracing
            .AddAspNetCoreInstrumentation()
            .AddHttpClientInstrumentation()
            .AddOtlpExporter(options =>
            {
                options.Endpoint = new Uri("http://otel-collector:4317");
            });
    })
    .WithMetrics(metrics =>
    {
        metrics
            .AddAspNetCoreInstrumentation()
            .AddHttpClientInstrumentation()
            .AddMeter("Yarp.ReverseProxy")
            .AddOtlpExporter();
    });

builder.Logging.AddOpenTelemetry(logging =>
{
    logging.AddOtlpExporter();
});

Metrics sẵn có từ YARP

YARP tự động phát ra các metrics qua System.Diagnostics.Metrics: số request mỗi route, latency P50/P95/P99, số lỗi proxy, health check results. Tích hợp Prometheus/Grafana chỉ cần thêm AddPrometheusExporter().

10. Dynamic Configuration — Thay đổi cấu hình runtime

Không phải lúc nào cấu hình cũng nằm trong appsettings.json. YARP hỗ trợ dynamic configuration từ database, Consul, Kubernetes hoặc bất kỳ nguồn nào.

public class DatabaseProxyConfigProvider : IProxyConfigProvider
{
    private readonly IServiceProvider _serviceProvider;
    private CancellationTokenSource _cts = new();

    public DatabaseProxyConfigProvider(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public IProxyConfig GetConfig()
    {
        using var scope = _serviceProvider.CreateScope();
        var dbContext = scope.ServiceProvider
            .GetRequiredService<GatewayDbContext>();

        var routes = dbContext.ProxyRoutes
            .Where(r => r.IsActive)
            .Select(r => new RouteConfig
            {
                RouteId = r.RouteId,
                ClusterId = r.ClusterId,
                Match = new RouteMatch { Path = r.PathPattern }
            })
            .ToList();

        var clusters = dbContext.ProxyClusters
            .Include(c => c.Destinations)
            .Select(c => new ClusterConfig
            {
                ClusterId = c.ClusterId,
                Destinations = c.Destinations
                    .ToDictionary(
                        d => d.DestinationId,
                        d => new DestinationConfig { Address = d.Address })
            })
            .ToList();

        return new DatabaseProxyConfig(routes, clusters, _cts.Token);
    }

    public void Reload()
    {
        var oldCts = _cts;
        _cts = new CancellationTokenSource();
        oldCts.Cancel();
    }
}

11. So sánh YARP với các giải pháp khác

Tiêu chíYARPOcelotKongNginx
Ngôn ngữC# / .NETC# / .NETLua / GoC
Tích hợp .NETNative — ASP.NET Core middlewareTốt nhưng API cũPlugin / sidecarReverse proxy thuần
Hiệu năngRất cao (~0.2ms overhead)Trung bìnhCaoRất cao
Cộng đồngMicrosoft chính thứcCommunityEnterprise + OSSRất lớn
Dynamic configIProxyConfigProviderHạn chếAdmin APIReload config file
gRPC proxyKhôngCó (stream)
WebSocket
HTTP/3Có (.NET 10)KhôngKhôngExperimental

12. Production Checklist

Những điều cần lưu ý khi deploy YARP lên production

  • HTTPS Termination: Cấu hình TLS certificate tại gateway, forward HTTP nội bộ để giảm overhead cho backend services
  • Connection Pooling: YARP tự quản lý HTTP connection pool — điều chỉnh MaxConnectionsPerServer theo tải thực tế
  • Timeout Configuration: Set timeout cho từng cluster riêng biệt — service xử lý nhanh (user lookup) và chậm (report generation) cần timeout khác nhau
  • Circuit Breaker: Kết hợp với Polly hoặc Microsoft.Extensions.Resilience để tránh cascading failures
  • Logging Level: Ở production, set YARP log level thành Warning để tránh log flood — chỉ bật Information/Debug khi troubleshooting
  • Header Size Limits: Tăng MaxRequestHeadersTotalSize nếu dùng JWT token lớn (chứa nhiều claims)
// Production-ready configuration
builder.WebHost.ConfigureKestrel(options =>
{
    options.Limits.MaxRequestHeadersTotalSize = 64 * 1024; // 64KB
    options.Limits.MaxRequestBodySize = 50 * 1024 * 1024;  // 50MB
    options.Limits.Http2.MaxStreamsPerConnection = 200;
    options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(2);
});

builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"))
    .ConfigureHttpClient((context, handler) =>
    {
        handler.MaxConnectionsPerServer = 100;
        handler.EnableMultipleHttp2Connections = true;
        handler.SslOptions.RemoteCertificateValidationCallback =
            (sender, cert, chain, errors) => true; // Dev only!
    });

13. Kiến trúc tổng thể Production

graph TD
    LB[Load Balancer
Cloudflare / ALB] --> G1[YARP Gateway
Instance 1] LB --> G2[YARP Gateway
Instance 2] G1 --> AUTH[Auth Middleware
JWT Validation] G2 --> AUTH AUTH --> RL[Rate Limiter
Fixed/Sliding/Token] RL --> TRANSFORM[Request Transforms
Headers, Path Rewrite] TRANSFORM --> S1[Order Service
3 instances] TRANSFORM --> S2[Product Service
2 instances] TRANSFORM --> S3[User Service
2 instances] G1 --> OTEL[OpenTelemetry
Collector] G2 --> OTEL OTEL --> GRAF[Grafana
Dashboard] style LB fill:#2c3e50,stroke:#fff,color:#fff style G1 fill:#e94560,stroke:#fff,color:#fff style G2 fill:#e94560,stroke:#fff,color:#fff style OTEL fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style GRAF fill:#f8f9fa,stroke:#4CAF50,color:#2c3e50
Kiến trúc YARP Gateway trong production — high availability với observability
2020
Microsoft công bố YARP dưới dạng preview, sử dụng nội bộ cho Azure Application Gateway
2021
YARP 1.0 GA — hỗ trợ .NET 6, HTTP/2, load balancing, health checks
2022–2023
YARP 2.x — thêm gRPC proxy, WebSocket improvements, dynamic configuration API
2024–2025
Tích hợp native với .NET 8/9 Rate Limiting, OpenTelemetry, HTTP/3 experimental
2026
YARP trên .NET 10 — HTTP/3 stable, Native AOT support, tích hợp Microsoft.Extensions.Resilience

Kết luận

YARP không chỉ là một reverse proxy — nó là API Gateway framework cho phép xây dựng gateway tùy chỉnh hoàn toàn trong hệ sinh thái .NET. Với khả năng tích hợp native vào ASP.NET Core pipeline, YARP cho phép tận dụng mọi thứ mà .NET cung cấp: authentication, rate limiting, logging, DI — mà không cần học thêm công cụ hay ngôn ngữ nào khác.

Đối với các team đang xây dựng microservices trên .NET, YARP là lựa chọn tự nhiên nhất — hiệu năng ngang Nginx, linh hoạt hơn Ocelot, và được Microsoft đảm bảo dài hạn.

Tham khảo