Azure应用服务用YARP取代了Nginx,获得了80%以上的吞吐量。他们每天处理160B多个请求(1.9 m RPS)。这是微软的一项了不起的技术创新。

首先我们来介绍一下什么是Yarp

Yarp是什么?

YARP(Yet Another Reverse Proxy)是一个开源的、高性能的反向代理库,由Microsoft开发,使用C#语言编写。它旨在作为.NET平台上构建反向代理服务器的基础。YARP主要针对.NET 5及以上版本,允许开发者在.NET应用程序中轻松地实现反向代理的功能。

YARP的主要特点和功能:

  1. 模块化和可扩展性: YARP设计成高度模块化的,这意味着可以根据需要替换或扩展内部组件,如HTTP请求路由、负载均衡、健康检查等。
  2. 性能: YARP针对高性能进行了优化,利用了.NET的异步编程模型和高效的IO操作,以处理大量并发连接。
  3. 配置驱动: YARP的行为可以通过配置来控制,支持从文件、数据库或其他来源动态加载配置。
  4. 路由: 可以基于各种参数(如路径、头部、查询参数)配置请求路由规则。
  5. 负载均衡: 内置多种负载均衡策略,如轮询、最少连接、随机选择等,并且可以自定义负载均衡策略。
  6. 健康检查: 支持后端服务的健康检查,以确保请求只会被转发到健康的后端服务实例。
  7. 转换器: 允许对请求和响应进行转换,如修改头部、路径或查询参数。
  8. 会话亲和性: 支持会话亲和性(Session Affinity),确保来自同一客户端的请求被发送到相同的后端服务实例。

使用YARP的一些场景:

  • 反向代理: 在客户端和后端服务之间提供一个中间层,用于请求转发和负载均衡。
  • API网关: 作为微服务架构中的API网关,提供路由、鉴权、监控等功能。
  • 边缘服务: 在应用程序和外部世界之间提供安全层,处理SSL终止、请求限制等任务。

Yarp简单的使用

创建一个WebApi的项目

安装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工具代理使用

下面我们在提供一个在中间件使用yarp的方式

我们需要用到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();

也是一样会被代理过去,但是于简单使用不一样的是我们是在代码层面控制代理的。

使用yarp修改Bing的响应内容

我们继续基于上面的代理使用进行修改bing的相应内容!

打开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相关资料

技术交流群:737776595

官方文档:https://microsoft.github.io/reverse-proxy/articles/getting-started.html

来着token的分享

微软用它取代了`Nginx`吞吐量提升了百分之八十!的更多相关文章

  1. 性能测试记录: ZZ 只改5行代码获得10倍吞吐量提升

    首先得找台足够性能的机器来测试,性能不足时代码运行会出现各种奇怪的现象,导致浪费时间 文章: https://www.jianshu.com/p/4cd8596352ad   只改了5行代码吞吐量提升 ...

  2. nginx高性能WEB服务器系列之八--nginx日志分析与切割

    nginx系列友情链接:nginx高性能WEB服务器系列之一简介及安装https://www.cnblogs.com/maxtgood/p/9597596.htmlnginx高性能WEB服务器系列之二 ...

  3. 菜鸟nginx源代码剖析数据结构篇(十) 自旋锁ngx_spinlock

    菜鸟nginx源代码剖析数据结构篇(十) 自旋锁ngx_spinlock Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.cs ...

  4. Puppet master nginx 扩展提升性能(puppet自动化系列4)

    puppet使用SSL(https)协议来进行通讯,默认情况下,puppet server端使用基于Ruby的WEBRick HTTP服务器.由于WEBRick HTTP服务器在处理agent端的性能 ...

  5. nginx 的提升多个小文件访问的性能模块

    阿里开源的第三方模块下载地址:https://github.com/alibaba/nginx-http-concat 下载模块 git clone https://github.com/alibab ...

  6. 启用reuse_port参数让Nginx性能提升3倍

    为什么启用 reuse_port 记得 2008 年做性能测试的时候,新进7台 lenovo 4核4G 服务器用于性能测试. 当时资源紧张,这7台服务器都装了双系统(Win2003/CentOS5)空 ...

  7. Nginx网络架构实战学习笔记(五):大访问量优化整体思路、ab压力测试及nginx性能统计模块、nginx单机1w并发优化

    文章目录 大访问量优化整体思路 ab压力测试及nginx性能统计模块 ab压力测试及nginx性能统计模块 ab压力测试 nginx性能统计模块 nginx单机1w并发优化 整装待发: socket ...

  8. 转:谷歌大脑科学家 Caffe缔造者 贾扬清 微信讲座完整版

    [转:http://blog.csdn.net/buaalei/article/details/46344675] 大家好!我是贾扬清,目前在Google Brain,今天有幸受雷鸣师兄邀请来和大家聊 ...

  9. 稳定模式在RESTful架构中的应用

    本文由 ImportNew - 乔永琪 翻译自 javaworld.欢迎加入翻译小组.转载请见文末要求. 分布式系统中保持网络稳定的五种方式 重试模式 超时模式 断路器模式 握手模式 隔离壁模式 倘若 ...

  10. CUDA ---- Memory Access

    Memory Access Patterns 大部分device一开始从global Memory获取数据,而且,大部分GPU应用表现会被带宽限制.因此最大化应用对global Memory带宽的使用 ...

随机推荐

  1. oracle多账套(用户)引用同一个账套的表或视图数据

    1.赋权限访问nbjf账套权限给到其他账套用户. grant select on nbjf.receivables to gzjf,hfjf,hfjy; 2.分别登陆 gzjf,hfjf,hfjy账套 ...

  2. Makefile 入门教程

    Makefile 是一个非常强大的构建自动化工具,用于管理项目的编译.链接和其他构建任务.以下是一个详细的 Makefile 使用文档,包括基本概念.语法.示例和常见任务. 1. 基本概念 目标 (T ...

  3. 深入解析Go非类型安全指针:技术全解与最佳实践

    本文全面深入地探讨了Go非类型安全指针,特别是在Go语言环境下的应用.从基本概念.使用场景,到潜在风险和挑战,文章提供了一系列具体的代码示例和最佳实践.目的是帮助读者在保证代码安全和效率的同时,更加精 ...

  4. docker入门加实战—部署Java和前端项目

    docker入门加实战-部署Java和前端项目 部署之前,先删除nginx,和自己创建的dd两个容器: docker rm -f nginx dd 部署Java项目 作为演示,我们的Java项目比较简 ...

  5. go mod tidy总是安装最新依赖,如何查找哪个模块导致某个包安装最新依赖,提供一个小工具

    安装: go install github.com/jan-bar/interesting/findModVer@latest 执行:findModVer d:\myproject 结果如下图所示: ...

  6. Android Studio里导入制作好的图片后,直接报错,图片名下方出现红色波浪线

    症状:Android Studio里导入制作好的图片后,直接报错,图片名下方出现红色波浪线. 比如:bk_Image_BackGround,报红 解决办法:如:bk_Image_BackGround, ...

  7. Jenkins-插件安装-多实例

    1,Jenkins插件安装: Jenkins最大的功能莫过于插件丰富,基于各种插件可以满足各项需求,Jenkins本身是一个框架,真正发挥作用的各种插件.Jenkins默认自带很多插件,如果没有添加新 ...

  8. Lyndon 分解

    介绍 [模板]Lyndon 分解 #include<cstdio> #include<cstring> char s[5000005]; int main(){ scanf(& ...

  9. Windows Server 2022 安装IIS 报错 访问临时文件夹 C:\WINDOWS\TEMP\3C 读取/写入权限 错误: 0x80070005

    在windows中使用命令行方式安装IIS(Web服务器) Windows Server 2022 安装IIS 报错 访问临时文件夹 C:\WINDOWS\TEMP\3C 读取/写入权限 错误: 0x ...

  10. C#12中的Collection expressions(集合表达式语法糖)

    C#12中引入了新的语法糖来创建常见的集合.并且可以使用..来解构集合,将其内联到另一个集合中. 支持的类型 数组类型,例如 int[]. System.Span<T> 和 System. ...