YARP实现Dapr服务调用的反向代理

公司即将新开项目,打算用点时髦的技术,需要探探路。之前没做过微服务项目,没有技术栈方面的积(负)累(债),
干脆就上微软的分布式运行时Dapr......嗯......用来服务发现,然后等测试用的服务开发好了,就开始纠结用啥反向代理/网关,nginx都没怎么用过,更别提其他复杂网关了,这时看了一篇微软的YARP(Yet Another Reverse Proxy)的文章,发现已经preview.10了,还挺简单的,二次开发也方便,就用它先顶上吧。

开发环境

WSL
windows下跨平台开发的优秀方案,Linux 分发版我用的Ubuntu 20.04

Docker Desktop
虽然Docker不是Dapr开发环境的唯一选择,但Docker的跨平台做的很好,尤其Docker Desktop可视化,还自带Docker-Compose,安装也方便,可以弹射起步。
安装完打开后,打开控制台,验证一下:docker --version

dotnet SDK
YARP最低支持.NET Core 3.1,这个时间点(2021.04),推荐.NET 5。
验证:dotnet --version

Dapr
Dapr安装我记得挺快的,之后的初始化dapr init,网不好的话,可能要多试几次。
初始化确认Docker Desktop里dapr_placementdapr_redisdapr_zipkin 3个容器都在正常运行

Tye
Tye 是微软开发提供的一款简化分布式应用开发的辅助命令行工具。用.NET写的,自带Dapr扩展。
dotnet tool全局安装后,可以如下验证:tye --version

知识储备

Yarp配置
Yarp主要要配置的东西就是Cluster(集群)和ProxyRoute(路由),
本例中,ProxyRoute通过配置文件加载,Cluster指向Dapr-sidecar,由程序动态添加。

原理

image

Yarp服务收到http请求

自定义Yarp转换:http(s)://<Yarp服务>/api/<服务名>/XXXXX 转为 <Dapr-sidecar>/v1.0/invoke/<服务名>/method/XXXXX

注:这里的<Dapr-sidecar>可能是动态的,因此不应该写死在配置文件里

请求转给Dapr-sidecar

Dapr 服务发现 终端服务并调用

返回响应

开发

创建两个Asp.Net Core项目:

GatewayDemo 网关Demo

ServiceSample 示例服务

完成 ServiceSample
这个示例比较简单,所以不需要引用Dapr SDK,只需加一个测试用的Controller就好了:

[Controller] [Route("sample")] public class SampleController { [HttpGet("{account}")] public ActionResult Get(string account) { return new JsonResult(new { Account = account, Balance = 100 }); } }

添加引用

GatewayDemo.csproj 增加包引用: <PackageReference Include="Yarp.ReverseProxy" Version="1.0.0-preview.10.*" />

注:Yarp.ReverseProxy从preview.10开始包名字变了,之前叫"Microsoft.ReverseProxy"。

这个示例比较简单,不需要引用Dapr的SDK

动态添加Yarp的Clusters自定义配置

public static IConfigurationBuilder AddDaprConfig(this IConfigurationBuilder configurationBuilder) { var httpEndpoint = DaprDefaults.GetDefaultHttpEndpoint(); //参考Dapr.Client,获取到dapr-sidecar的url return configurationBuilder.AddInMemoryCollection(new[] { new KeyValuePair<string, string>("Yarp:Clusters:dapr-sidecar:Destinations:d1:Address", httpEndpoint), }); } //GatewayDemo的Program.cs Host.CreateDefaultBuilder(args) ... .ConfigureAppConfiguration((_, builder) => builder.AddDaprConfig())

GatewayDemo appsettings 增加Yarp相关配置

Yarp: Routes: - RouteId: r-module-master ClusterId: dapr-sidecar Match: Path: api/service-sample/{**catch-all} Metadata: Dapr: method #标注Dapr

不用在意为什么是yaml,而不是json。

添加Yarp的自定义转换

public class DaprTransformProvider : ITransformProvider { public void ValidateRoute(TransformRouteValidationContext context) { } public void ValidateCluster(TransformClusterValidationContext context) { } public void Apply(TransformBuilderContext context) { string daprAct = null; if (context.Route.Metadata?.TryGetValue(DaprYarpConst.MetaKeys.Dapr, out daprAct) ?? false) //通过元数据判断是否是Dapr服务,在配置文件中设置 { switch (daprAct) { case DaprYarpConst.DaprAct.Method: context.AddRequestTransform(transformContext => { var index = transformContext.Path.Value!.IndexOf('http://www.likecs.com/', 5); // format: /api/<服务>/xxxx var appId = transformContext.Path.Value.Substring(5, index - 5); var newPath = transformContext.Path.Value.Substring(index); transformContext.ProxyRequest.RequestUri = new Uri($"{transformContext.DestinationPrefix}/v1.0/invoke/{appId}/method{newPath}"); return ValueTask.CompletedTask; }); break; } } } } //GatewayDemo的Startup public class Startup { private readonly IConfiguration _configuration; public Startup(IConfiguration configuration) { _configuration = configuration; } public void ConfigureServices(IServiceCollection services) { services.AddReverseProxy() .LoadFromConfig(_configuration.GetSection("Yarp")) .AddTransforms<DaprTransformProvider>(); //加上自定义转换 } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapReverseProxy(); //endpoints.MapGet("http://www.likecs.com/", async context => { await context.Response.WriteAsync("Hello World!"); }); }); } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpsjsd.html