
Azure Application Services replaced Nginx with YARP, achieving more than 80% throughput. They process more than 160B requests per day (1.9m RPS). This is a great technological innovation from Microsoft.
First of all, let's introduce what Yarp is
What is Yarp?
YARP (Yet Another Reverse Proxy) is an open source, high-performance reverse proxy library developed by Microsoft and written in the C#language. It is intended to serve as the basis for building reverse proxy servers on the. NET platform. YARP is mainly targeted at. NET 5 and above, allowing developers to easily implement reverse proxy functions in. NET applications.
The main features and functions of YARP:
- ** Modularity and extensibility: ** YARP is designed to be highly modular, which means that internal components such as HTTP request routing, Load Balancer, health checks, etc. can be replaced or expanded as needed.
- ** Performance: ** YARP is optimized for high performance, leveraging. NET's asynchronous programming model and efficient IO operations to handle large numbers of concurrent connections.
- ** Configuration Driver: ** YARP's behavior can be controlled through configuration, supporting dynamic loading of configuration from files, databases, or other sources.
- ** Routing: ** Request routing rules can be configured based on various parameters (such as path, header, query parameters).
- ** Load Balancer: ** Built-in multiple Load Balancer strategies, such as polling, minimum connections, random selection, etc., and you can customize the Load Balancer policies.
- ** Health check: ** Supports health checks for backend services to ensure that requests are only forwarded to healthy backend service instances.
- ** Converters: ** Allows conversion of requests and responses, such as modifying headers, paths, or query parameters.
- ** Session affinity: ** Supports Session affinity to ensure that requests from the same client are sent to the same backend service instance.
Some scenarios for using YARP:
- ** Reverse Proxy: ** Provides an intermediate layer between clients and backend services for request forwarding and Load Balancer.
- **API Gateway: ** As an API gateway in the microservice architecture, it provides routing, authentication, monitoring and other functions.
- ** Edge Services: ** Provides a security layer between the application and the outside world, handling tasks such as SSL termination and request restriction.
Yarp is simple to use
Create a WebApi project
安装NuGet包
<ItemGroup>
<PackageReference Include="Yarp.ReverseProxy" Version="2.0.0" />
</ItemGroup>
打开appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ReverseProxy": {
"Routes": {
"route1": {
"ClusterId": "cluster1",
"Match": {
"Path": "{**catch-all}"
}
}
},
"Clusters": {
"cluster1": {
"Destinations": {
"destination1": {
"Address": "https://cn.bing.com/"
}
}
}
}
}
}
打开Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
var app = builder.Build();
app.MapReverseProxy();
app.Run();
然后启动项目,访问我们的 api 就会被代理转发到bing上

Yarp tool proxy use
Below we provide a way to use yarp in middleware
我们需要用到IHttpForwarder
先修改Program.cs 在这里我们注入了HttpForwarder,然后提供一个 Run 中间件,在中间件中手动指定了端点的地址https://cn.bing.com/ 然后我们启动一下项目。
using Yarp.ReverseProxy.Forwarder;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpForwarder(); // 注入IHttpForwarder
var app = builder.Build();
var httpMessage = new HttpMessageInvoker(new HttpClientHandler());
app.Run((async context =>
{
var httpForwarder = context.RequestServices.GetRequiredService<IHttpForwarder>();
var destinationPrefix = "https://cn.bing.com/";
await httpForwarder.SendAsync(context, destinationPrefix, httpMessage);
}));
app.Run();
It will also be proxy, but unlike simple use, we control proxy at the code level.

Use yarp to modify Bing's response content
We continue to modify the corresponding content of bing based on the above proxy usage!
打开Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpForwarder(); // 注入IHttpForwarder
var app = builder.Build();
var httpMessage = new HttpMessageInvoker(new HttpClientHandler()
{
// 忽略https错误
ServerCertificateCustomValidationCallback = (_, _, _, _) => true,
AllowAutoRedirect = false,
AutomaticDecompression = DecompressionMethods.GZip,
UseCookies = false,
UseProxy = false,
UseDefaultCredentials = true,
});
var destinationPrefix = "https://cn.bing.com/";
var bingTransformer = new BingTransformer();
app.Run((async context =>
{
var httpForwarder = context.RequestServices.GetRequiredService<IHttpForwarder>();
await httpForwarder.SendAsync(context, destinationPrefix, httpMessage, new ForwarderRequestConfig(),
bingTransformer);
}));
app.Run();
创建BingTransformer.cs
public class BingTransformer : HttpTransformer
{
public override async ValueTask TransformRequestAsync(HttpContext httpContext, HttpRequestMessage proxyRequest,
string destinationPrefix,
CancellationToken cancellationToken)
{
var uri = RequestUtilities.MakeDestinationAddress(destinationPrefix, httpContext.Request.Path,
httpContext.Request.QueryString);
proxyRequest.RequestUri = uri;
proxyRequest.Headers.Host = uri.Host;
await base.TransformRequestAsync(httpContext, proxyRequest, destinationPrefix, cancellationToken);
}
public override async ValueTask<bool> TransformResponseAsync(HttpContext httpContext,
HttpResponseMessage? proxyResponse,
CancellationToken cancellationToken)
{
await base.TransformResponseAsync(httpContext, proxyResponse, cancellationToken);
if (httpContext.Request.Method == "GET" &&
httpContext.Response.Headers["Content-Type"].Any(x => x.StartsWith("text/html")))
{
var encoding = proxyResponse.Content.Headers.FirstOrDefault(x => x.Key == "Content-Encoding").Value;
if (encoding?.FirstOrDefault() == "gzip")
{
var content = proxyResponse?.Content.ReadAsByteArrayAsync(cancellationToken).Result;
if (content != null)
{
var result = Encoding.UTF8.GetString(GZipDecompressByte(content));
result = result.Replace("国内版", "Token Bing 搜索 - 国内版");
proxyResponse.Content = new StringContent(GZipDecompressString(result));
}
}
else if (encoding.FirstOrDefault() == "br")
{
var content = proxyResponse?.Content.ReadAsByteArrayAsync(cancellationToken).Result;
if (content != null)
{
var result = Encoding.UTF8.GetString(BrDecompress(content));
result = result.Replace("国内版", "Token Bing 搜索 - 国内版");
proxyResponse.Content = new ByteArrayContent(BrCompress(result));
}
}
else
{
var content = proxyResponse?.Content.ReadAsStringAsync(cancellationToken).Result;
if (content != null)
{
content = content.Replace("国内版", "Token Bing 搜索 - 国内版");
proxyResponse.Content = new StringContent(content);
}
}
}
return true;
}
/// <summary>
/// 解压GZip
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static byte[] GZipDecompressByte(byte[] bytes)
{
using var targetStream = new MemoryStream();
using var compressStream = new MemoryStream(bytes);
using var zipStream = new GZipStream(compressStream, CompressionMode.Decompress);
using (var decompressionStream = new GZipStream(compressStream, CompressionMode.Decompress))
{
decompressionStream.CopyTo(targetStream);
}
return targetStream.ToArray();
}
/// <summary>
/// 解压GZip
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public static string GZipDecompressString(string str)
{
using var compressStream = new MemoryStream(Encoding.UTF8.GetBytes(str));
using var zipStream = new GZipStream(compressStream, CompressionMode.Decompress);
using var resultStream = new StreamReader(new MemoryStream(compressStream.ToArray()));
return resultStream.ReadToEnd();
}
/// <summary>
/// Br压缩
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static byte[] BrCompress(string str)
{
using var outputStream = new MemoryStream();
using (var compressionStream = new BrotliStream(outputStream, CompressionMode.Compress))
{
compressionStream.Write(Encoding.UTF8.GetBytes(str));
}
return outputStream.ToArray();
}
/// <summary>
/// Br解压
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static byte[] BrDecompress(byte[] input)
{
using (var inputStream = new MemoryStream(input))
using (var outputStream = new MemoryStream())
using (var decompressionStream = new BrotliStream(inputStream, CompressionMode.Decompress))
{
decompressionStream.CopyTo(outputStream);
return outputStream.ToArray();
}
}
}
得到的效果我们将国内版修改成了Token Bing 搜索 - 国内版

Yarp related information
Technical exchange group: 737776595
Official document: microsoft.github.io/reverse-proxy/articles/getting-started.html
Sharing of token