Yarp 让系统内调度更灵活

  • A+
所属分类:.NET技术
摘要

Yarp 是微软团队开发的一个反向代理组件 , 除了常规的 http 和 https 转换通讯, 它最大的特点是可定制化 ,很容易根据特定场景开发出需要的定制代理通道。

简介

Yarp 是微软团队开发的一个反向代理组件, 除了常规的 http 和 https 转换通讯,它最大的特点是可定制化,很容易根据特定场景开发出需要的定制代理通道。

详细介绍:https://devblogs.microsoft.com/dotnet/announcing-yarp-1-0-release

源码仓库:https://github.com/microsoft/reverse-proxy

文档地址 :https://microsoft.github.io/reverse-proxy/

 

基础使用

1、创建 ASP.NET Core  空项目

使用 Visual Studio :

Yarp 让系统内调度更灵活
 
使用 .NET CLI 命令行创建:
dotnet new web -o MyProxy

2、 修改代码 Program.cs 文件

var builder = WebApplication.CreateBuilder(args); builder.Services.AddReverseProxy()     .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")); var app = builder.Build(); app.MapGet("/Ping", () => "Hello World!"); app.MapReverseProxy(); app.Run();

3、修改配置文件 appsettings.json

{   "Logging": {     "LogLevel": {       "Default": "Information",       "Microsoft.AspNetCore": "Warning"     }   },   "AllowedHosts": "*",   "ReverseProxy": {     "Routes": {       "routeAll": {         "ClusterId": "clusterBaidu",         "Match": {           "Path": "{**catch-all}"         }       }     },     "Clusters": {       "clusterBaidu": {         "Destinations": {           "baidu": {             "Address": "https://www.baidu.com/"           }         }       }     }   } }

这里的配置是将所有的请求都转发到百度。

在 Program.cs 里,还注册了一个 Get 路由 Ping 。

4、启动项目

Yarp 让系统内调度更灵活

能够看到在浏览器访问程序监听的端口号后,显示的是百度的页面。打开 F12 ,看到请求头也是本地的,并不是百度的域名。

测试手动注册的路由 Ping :

Yarp 让系统内调度更灵活

能够显示正常。

5、问题整理

(1) Yarp 是不是只能做这种简单的转发?

不是,往下有配置文件说明。

(2) JSON 配置文件里有什么要注意的地方吗?

有。在这个演示的配置文件中 ReverseProxy:Clusters:cluster1:Destinations:destination1:Address 对应的值是:https://www.baidu.com/ ,如果去掉 www ,在项目启动后会跳转到百度首页,不是代理转发。去掉末尾的 / 符合没有任何影响。

(3) Yarp 会影响到程序中注册的路由吗?

不会影响到程序内部注册的路由。在 Program.cs 中无论 app.MapReverseProxy(); 在上还是在下,在访问 Ping 的时候,都是返回 Hello World!

var app = builder.Build(); app.MapReverseProxy(); app.MapGet("/Ping", () => "Hello World!"); app.Run();

 

进阶探索

1、多地址代理

 修改配置文件 appsettings.json ,实现默认路由跳转百度,当访问 /movie 是访问 b站。

{   "Logging": {     "LogLevel": {       "Default": "Information",       "Microsoft.AspNetCore": "Warning"     }   },   "AllowedHosts": "*",   "ReverseProxy": {     "Routes": {       "routeBaidu": {         "ClusterId": "clusterBaidu",         "Match": {           "Path": "{**catch-all}"         }       },       "routeBiliBili": {         "ClusterId": "clusterBiliBili",         "Match": {           "Path": "/movie/{**catch-all}"         }       }     },     "Clusters": {       "clusterBaidu": {         "Destinations": {           "baidu": {             "Address": "https://www.baidu.com/"           }         }       },       "clusterBiliBili": {         "Destinations": {           "bilibili": {             "Address": "https://www.bilibili.com/"           }         }       }     }   } }

 测试结果:

Yarp 让系统内调度更灵活

Yarp 让系统内调度更灵活

在后面输入路由 /movie 后能够跳转到b站。但是b站网页没有完整显示,图片都没有,这是网站上的策略问题,对于数据接口没有这些问题。

Yarp 让系统内调度更灵活

详细的配置文件说明,可以查看 https://microsoft.github.io/reverse-proxy/articles/config-files.html

2、规则匹配

网页上太多资源,为了方便测试,启用两个 api 接口。地址分别是:http://localhost:5241/ 和 https://localhost:7184/

两个 api 接口中分别注册 /test 路由。

// http://localhost:5241/ app.MapGet("/test", () => "Welcome to Api111!");  // https://localhost:7184/ app.MapGet("/test", () => "Welcome to Api222!");

启动两个 api 程序。

C:UsersTest>curl http://localhost:5241/test Welcome to Api111!  C:UsersTest>curl https://localhost:7184/test Welcome to Api222!

修改 MyProxy 项目的配置文件 appsettings.json 

{   "Logging": {     "LogLevel": {       "Default": "Information",       "Microsoft.AspNetCore": "Warning"     }   },   "AllowedHosts": "*",   "ReverseProxy": {     "Routes": {       "routeOne": {         "ClusterId": "clusterOne",         "Match": {           "Path": "/test/{**catch-all}",           "QueryParameters": [             {               "Name": "number",               "Values": [ "1" ]             }           ]         }       },       "routeTwo": {         "ClusterId": "clusterTwo",         "Match": {           "Path": "/test/{**catch-all}",           "QueryParameters": [             {               "Name": "number",               "Values": [ "2" ]             }           ]         }       },       "routeBaidu": {         "ClusterId": "clusterBaidu",         "Match": {           "Path": "{**catch-all}"         }       }     },     "Clusters": {       "clusterOne": {         "Destinations": {           "apiOne": {             "Address": "http://localhost:5241/"           }         }       },       "clusterTwo": {         "Destinations": {           "apiTwo": {             "Address": "https://localhost:7184/"           }         }       },       "clusterBaidu": {         "Destinations": {           "baidu": {             "Address": "https://www.baidu.com/"           }         }       }     }   } }

Path :监听路由地址。

QueryParameters:匹配参数。

QueryParameters:Name:参数名。

QueryParameters:Values:参数值。

MyProxy 的监听端口是 http://localhost:5024/ 访问结果如下

C:UsersTest>curl http://localhost:5024/ping Hello World!  C:UsersTest>curl http://localhost:5024/test <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>404 Not Found</title> </head><body> <h1>Not Found</h1> <p>The requested URL /test was not found on this server.</p> </body></html>  C:UsersTest>curl http://localhost:5024/test?number=1 Welcome to Api111!  C:UsersTest>curl http://localhost:5024/test?number=2 Welcome to Api222!

能够根据参数以及参数值导向对应的地址。

3、问题整理

(1)为什么访问 /movie 不能正常显示网页。

因为 b站某些接口开启了防盗链,还有跨域检测。

Yarp 让系统内调度更灵活

Yarp 让系统内调度更灵活

(2)在根据参数匹配中,如果匹配的路由一样,监听的参数一样,参数值也一样会怎么样?

访问该路由地址会报错。

(3)路由匹配的优先级?

程序内注册的路由优先级最高,其次才是 Yarp 在配置文件里加载的。

 

小试牛刀

加班写业务中,待完成。

 

踩坑集锦

1、non-ASCII 

项目要代理某网页,在使用下载功能的时候,接口返回 502 。

info: Yarp.ReverseProxy.Forwarder.HttpForwarder[48]       ResponseHeaders: The destination returned a response that cannot be proxied back to the client.       System.InvalidOperationException: Invalid non-ASCII or control character in header: 0x00E4          at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ThrowInvalidHeaderCharacter(Char ch)          at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ValidateHeaderValueCharacters(StringValues headerValues)          at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpResponseHeaders.SetValueFast(String key, StringValues value)          at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.Microsoft.AspNetCore.Http.IHeaderDictionary.set_Item(String key, StringValues value)          at Yarp.ReverseProxy.Forwarder.HttpTransformer.CopyResponseHeaders(HttpHeaders source, IHeaderDictionary destination)          at Yarp.ReverseProxy.Forwarder.HttpTransformer.TransformResponseAsync(HttpContext httpContext, HttpResponseMessage proxyResponse)          at Yarp.ReverseProxy.Transforms.Builder.StructuredTransformer.TransformResponseAsync(HttpContext httpContext, HttpResponseMessage proxyResponse)          at Yarp.ReverseProxy.Forwarder.HttpForwarder.SendAsync(HttpContext context, String destinationPrefix, HttpMessageInvoker httpClient, ForwarderRequestConfig requestConfig, HttpTransformer transformer)

去 GitHub 翻 Issues

Yarp 让系统内调度更灵活

下载接口能正常访问,文件流也能完整地拿到。重写了所有的响应头没有用。这种不开源的商业站点,也猜不到字符编码。

最后妥协了,用了一个 .NET 服务在服务器上下载后再转发。

代理非常规服务接口时,一定要多测试。