经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » Nginx » 查看文章
微软用它取代了`Nginx`吞吐量提升了百分之八十!
来源:cnblogs  作者:tokengo  时间:2024/1/10 8:57:56  对本文有异议

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

  1. <ItemGroup>
  2. <PackageReference Include="Yarp.ReverseProxy" Version="2.0.0" />
  3. </ItemGroup>

打开appsettings.json

  1. {
  2. "Logging": {
  3. "LogLevel": {
  4. "Default": "Information",
  5. "Microsoft": "Warning",
  6. "Microsoft.Hosting.Lifetime": "Information"
  7. }
  8. },
  9. "AllowedHosts": "*",
  10. "ReverseProxy": {
  11. "Routes": {
  12. "route1" : {
  13. "ClusterId": "cluster1",
  14. "Match": {
  15. "Path": "{**catch-all}"
  16. }
  17. }
  18. },
  19. "Clusters": {
  20. "cluster1": {
  21. "Destinations": {
  22. "destination1": {
  23. "Address": "https://cn.bing.com/"
  24. }
  25. }
  26. }
  27. }
  28. }
  29. }

打开Program.cs

  1. var builder = WebApplication.CreateBuilder(args);
  2. builder.Services.AddReverseProxy()
  3. .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
  4. var app = builder.Build();
  5. app.MapReverseProxy();
  6. app.Run();

然后启动项目,访问我们的api就会被代理转发到bing

Yarp工具代理使用

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

我们需要用到IHttpForwarder

先修改Program.cs 在这里我们注入了HttpForwarder,然后提供一个Run中间件,在中间件中手动指定了端点的地址https://cn.bing.com/ 然后我们启动一下项目。

  1. using Yarp.ReverseProxy.Forwarder;
  2. var builder = WebApplication.CreateBuilder(args);
  3. builder.Services.AddHttpForwarder(); // 注入IHttpForwarder
  4. var app = builder.Build();
  5. var httpMessage = new HttpMessageInvoker(new HttpClientHandler());
  6. app.Run((async context =>
  7. {
  8. var httpForwarder = context.RequestServices.GetRequiredService<IHttpForwarder>();
  9. var destinationPrefix = "https://cn.bing.com/";
  10. await httpForwarder.SendAsync(context, destinationPrefix, httpMessage);
  11. }));
  12. app.Run();

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

使用yarp修改Bing的响应内容

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

打开Program.cs

  1. var builder = WebApplication.CreateBuilder(args);
  2. builder.Services.AddHttpForwarder(); // 注入IHttpForwarder
  3. var app = builder.Build();
  4. var httpMessage = new HttpMessageInvoker(new HttpClientHandler()
  5. {
  6. // 忽略https错误
  7. ServerCertificateCustomValidationCallback = (_, _, _, _) => true,
  8. AllowAutoRedirect = false,
  9. AutomaticDecompression = DecompressionMethods.GZip,
  10. UseCookies = false,
  11. UseProxy = false,
  12. UseDefaultCredentials = true,
  13. });
  14. var destinationPrefix = "https://cn.bing.com/";
  15. var bingTransformer = new BingTransformer();
  16. app.Run((async context =>
  17. {
  18. var httpForwarder = context.RequestServices.GetRequiredService<IHttpForwarder>();
  19. await httpForwarder.SendAsync(context, destinationPrefix, httpMessage, new ForwarderRequestConfig(),
  20. bingTransformer);
  21. }));
  22. app.Run();

创建BingTransformer.cs

  1. public class BingTransformer : HttpTransformer
  2. {
  3. public override async ValueTask TransformRequestAsync(HttpContext httpContext, HttpRequestMessage proxyRequest,
  4. string destinationPrefix,
  5. CancellationToken cancellationToken)
  6. {
  7. var uri = RequestUtilities.MakeDestinationAddress(destinationPrefix, httpContext.Request.Path,
  8. httpContext.Request.QueryString);
  9. proxyRequest.RequestUri = uri;
  10. proxyRequest.Headers.Host = uri.Host;
  11. await base.TransformRequestAsync(httpContext, proxyRequest, destinationPrefix, cancellationToken);
  12. }
  13. public override async ValueTask<bool> TransformResponseAsync(HttpContext httpContext,
  14. HttpResponseMessage? proxyResponse,
  15. CancellationToken cancellationToken)
  16. {
  17. await base.TransformResponseAsync(httpContext, proxyResponse, cancellationToken);
  18. if (httpContext.Request.Method == "GET" &&
  19. httpContext.Response.Headers["Content-Type"].Any(x => x.StartsWith("text/html")))
  20. {
  21. var encoding = proxyResponse.Content.Headers.FirstOrDefault(x => x.Key == "Content-Encoding").Value;
  22. if (encoding?.FirstOrDefault() == "gzip")
  23. {
  24. var content = proxyResponse?.Content.ReadAsByteArrayAsync(cancellationToken).Result;
  25. if (content != null)
  26. {
  27. var result = Encoding.UTF8.GetString(GZipDecompressByte(content));
  28. result = result.Replace("国内版", "Token Bing 搜索 - 国内版");
  29. proxyResponse.Content = new StringContent(GZipDecompressString(result));
  30. }
  31. }
  32. else if (encoding.FirstOrDefault() == "br")
  33. {
  34. var content = proxyResponse?.Content.ReadAsByteArrayAsync(cancellationToken).Result;
  35. if (content != null)
  36. {
  37. var result = Encoding.UTF8.GetString(BrDecompress(content));
  38. result = result.Replace("国内版", "Token Bing 搜索 - 国内版");
  39. proxyResponse.Content = new ByteArrayContent(BrCompress(result));
  40. }
  41. }
  42. else
  43. {
  44. var content = proxyResponse?.Content.ReadAsStringAsync(cancellationToken).Result;
  45. if (content != null)
  46. {
  47. content = content.Replace("国内版", "Token Bing 搜索 - 国内版");
  48. proxyResponse.Content = new StringContent(content);
  49. }
  50. }
  51. }
  52. return true;
  53. }
  54. /// <summary>
  55. /// 解压GZip
  56. /// </summary>
  57. /// <param name="bytes"></param>
  58. /// <returns></returns>
  59. public static byte[] GZipDecompressByte(byte[] bytes)
  60. {
  61. using var targetStream = new MemoryStream();
  62. using var compressStream = new MemoryStream(bytes);
  63. using var zipStream = new GZipStream(compressStream, CompressionMode.Decompress);
  64. using (var decompressionStream = new GZipStream(compressStream, CompressionMode.Decompress))
  65. {
  66. decompressionStream.CopyTo(targetStream);
  67. }
  68. return targetStream.ToArray();
  69. }
  70. /// <summary>
  71. /// 解压GZip
  72. /// </summary>
  73. /// <param name="str"></param>
  74. /// <returns></returns>
  75. public static string GZipDecompressString(string str)
  76. {
  77. using var compressStream = new MemoryStream(Encoding.UTF8.GetBytes(str));
  78. using var zipStream = new GZipStream(compressStream, CompressionMode.Decompress);
  79. using var resultStream = new StreamReader(new MemoryStream(compressStream.ToArray()));
  80. return resultStream.ReadToEnd();
  81. }
  82. /// <summary>
  83. /// Br压缩
  84. /// </summary>
  85. /// <param name="input"></param>
  86. /// <returns></returns>
  87. public static byte[] BrCompress(string str)
  88. {
  89. using var outputStream = new MemoryStream();
  90. using (var compressionStream = new BrotliStream(outputStream, CompressionMode.Compress))
  91. {
  92. compressionStream.Write(Encoding.UTF8.GetBytes(str));
  93. }
  94. return outputStream.ToArray();
  95. }
  96. /// <summary>
  97. /// Br解压
  98. /// </summary>
  99. /// <param name="input"></param>
  100. /// <returns></returns>
  101. public static byte[] BrDecompress(byte[] input)
  102. {
  103. using (var inputStream = new MemoryStream(input))
  104. using (var outputStream = new MemoryStream())
  105. using (var decompressionStream = new BrotliStream(inputStream, CompressionMode.Decompress))
  106. {
  107. decompressionStream.CopyTo(outputStream);
  108. return outputStream.ToArray();
  109. }
  110. }
  111. }

得到的效果我们将国内版修改成了Token Bing 搜索 - 国内版

Yarp AOT尝试

下面我们将Yarp进行AOT尝试,首先打开我们的项目,添加以下参数

  1. <Project Sdk="Microsoft.NET.Sdk.Web">
  2. <PropertyGroup>
  3. <TargetFramework>net8.0</TargetFramework>
  4. <Nullable>enable</Nullable>
  5. <ImplicitUsings>enable</ImplicitUsings>
  6. <InvariantGlobalization>true</InvariantGlobalization>
  7. <PublishAot>true</PublishAot>
  8. </PropertyGroup>
  9. <ItemGroup>
  10. <PackageReference Include="Yarp.ReverseProxy" Version="2.0.0" />
  11. </ItemGroup>
  12. </Project>

AOT以后的大小15MB

测试转发并无问题

优化AOT大小,添加以下参数

  1. <Project Sdk="Microsoft.NET.Sdk.Web">
  2. <PropertyGroup>
  3. <TargetFramework>net8.0</TargetFramework>
  4. <Nullable>enable</Nullable>
  5. <ImplicitUsings>enable</ImplicitUsings>
  6. <InvariantGlobalization>true</InvariantGlobalization>
  7. <PublishAot>true</PublishAot>
  8. <StackTraceSupport>false</StackTraceSupport>
  9. <OptimizationPreference>Size</OptimizationPreference>
  10. <PublishTrimmed>true</PublishTrimmed>
  11. <BlazorEnableTimeZoneSupport>false</BlazorEnableTimeZoneSupport>
  12. <EventSourceSupport>false</EventSourceSupport>
  13. <HttpActivityPropagationSupport>false</HttpActivityPropagationSupport>
  14. <EnableUnsafeBinaryFormatterSerialization>false</EnableUnsafeBinaryFormatterSerialization>
  15. <MetadataUpdaterSupport>false</MetadataUpdaterSupport>
  16. <UseNativeHttpHandler>true</UseNativeHttpHandler>
  17. <TrimMode>link</TrimMode>
  18. </PropertyGroup>
  19. <ItemGroup>
  20. <PackageReference Include="Yarp.ReverseProxy" Version="2.0.0" />
  21. </ItemGroup>
  22. </Project>

减少2MB左右,并且正常运行代理

Yarp相关资料

技术交流群:737776595

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

来着token的分享

原文链接:https://www.cnblogs.com/hejiale010426/p/17954360

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号