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_placement,dapr_redis,dapr_zipkin 3个容器都在正常运行
- Tye
 Tye 是微软开发提供的一款简化分布式应用开发的辅助命令行工具。用.NET写的,自带Dapr扩展。
 dotnet tool全局安装后,可以如下验证:- tye --version
 
知识储备
- Yarp配置
 Yarp主要要配置的东西就是Cluster(集群)和ProxyRoute(路由),
 本例中,ProxyRoute通过配置文件加载,Cluster指向Dapr-sidecar,由程序动态添加。
原理

- Yarp服务收到http请求
- 自定义Yarp转换:
http(s)://<Yarp服务>/api/<服务名>/XXXXX
 转为
 http://<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
 
- GatewayDemo.csproj 增加包引用: 
- 动态添加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('/', 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("/", async context => { await context.Response.WriteAsync("Hello World!"); });
 });
 }
 }
 
- 配置tye 
 参考:- name: dapr-yarp.sample
 extensions:
 - name: dapr
 components-path: "./components/"
 config: dapr-config
 exclude-placement-container: true
 placement-port: 6050
 - name: zipkin
 services:
 - name: gateway-demo #服务名称最好不要有大写,容易出问题
 project: ./GatewayDemo/GatewayDemo.csproj
 - name: service-sample #服务名称最好不要有大写,容易出问题
 project: ./ServiceSample/ServiceSample.csproj
 - name: zipkin #dapr的追踪服务
 external: true
 bindings:
 - name: http
 port: 9411
 ingress:
 - name: ingress
 rules:
 - service: gateway-demo
 path: /
 bindings:
 - name: ingress
 protocol: https
 port: 44363 #对外端口
 - ./componnets/文件夹下还有一些配置文件,这里就不贴了 
- 用tye同时运行多个项目 
 确保dapr那3个容器服务正常运行后,运行 tye run,通过tye的Dapr扩展,运行显示以下几个服务:- gateway-demo
- service-sample
- zipkin
- gateway-demo-dapr
- service-sample-dapr
- ingress
 
- 测试: 
 浏览器直接打开:- https://localhost:44363/api/service-sample/sample/1234
 如果显示- {"account":"1234","balance":100}就说明通了。
附上Demo代码
尾声
因为目前没有用到对外的grpc调用和Actor、发布订阅等功能,所以本示例没有相关代码演示。
Yarp虽然还在preview,但有兴趣的.NET技术栈的玩家已经可以用它来做些简单的反向代理了。
YARP实现Dapr服务调用的反向代理的更多相关文章
- 生产环境中nginx既做web服务又做反向代理
		一.写对于初入博客园的感想 众所周知,nginx是一个高性能的HTTP和反向代理服务器,在以前工作中要么实现http要么做反向代理或者负载均衡.尚未在同一台nginx或者集群上同时既实现HTTP又实现 ... 
- Dubbo服务调用的动态代理和负载均衡
		Dubbo服务调用的动态代理及负载均衡源码解析请参见:http://manzhizhen.iteye.com/blog/2314514 
- 【Azure微服务 Service Fabric 】Service Fabric中应用开启外部访问端口及微服务之间通过反向代理端口访问问题
		问题描述 1) 当成功的在Service Fabric集群中部署了应用后,如何来访问呢?如果是一个Web服务,它的URL又是什么呢? 2) 当Service Fabric集群中,服务之间如需要相互访问 ... 
- Linux服务-配置Nginx反向代理
		任务目标:实现基于轮询的方式调度三台web,并验证结果:实现基于权重的方式调度三台web,并验证结果:实现基于hash的方式调用三台web,并验证结果 由于刚刚做了nfs设置,为了提现实验结果,我在w ... 
- docker学习(6) docker中搭建java服务及nginx反向代理
		先看下容器规划: 上图中mysql容器的搭建见上篇博客,service1/2为java rest service,创建脚本如下: docker run -d -h service1 \ -v /Use ... 
- Nginx设置Https反向代理,指向Docker Gitlab11.3.9 Https服务
		目录 目录 1.GitLab11.3.9的安装 2.域名在阿里云托管,申请免费的1年证书 3.Gitlab 的 https 配置 4.Nginx 配置 https,反向代理指向 Gitlab 配置 目 ... 
- nginx 为什么要反向代理  影藏后端   高效连接(给nginx,他自己返回)  端口冲突解决  多个服务
		nginx 为什么要反向代理 影藏后端 高效连接(给nginx,他自己返回) 端口冲突解决 多个服务 单机使用反向代理可以根据不同url匹配到不同站点 rsync 的工作原理和应用实例 ... 
- Dapr初体验之服务调用
		初次理解服务调用 在微服务中,有一个难点就是:如果你想使用各个服务组件,你就得知道不同服务的地址和端口,也就是服务发现. 在传统应用我们是怎么做的?就是在web项目里配置上api地址,如下: 在一个w ... 
- ngnix 反向代理
		1 课程目标 掌握nginx+tomcat反向代理的使用方法. 掌握nginx作为负载均衡器的使用方法. 掌握nginx实现web缓存方法. 2 nginx介绍 2.1 ... 
随机推荐
- django学习-4.url动态传值
			1.前言 我们在浏览器访问一个网页A是通过一个指定的url地址去访问的.但在浏览器用一个不存在的url地址去执行访问是打不开正确的网页的,只会打开一个浏览器自带的有错误提示的网页. 在django框架 ... 
- CentOS7安装Mysql并配置远程访问
			(su root登录到root账户) 下载repo源 wget http://repo.mysql.com/mysql-community-release-el7-5.noarch.rpm 安装rpm ... 
- Vue和Element基础使用,综合案例学生列表实现
			知识点梳理 课堂讲义 1.Vue 快速入门 1.1.Vue的介绍 Vue是一套构建用户界面的渐进式前端框架. 只关注视图层,并且非常容易学习,还可以很方便的与其它库或已有项目整合. 通过尽可能简单的A ... 
- 低功耗蓝牙   ATT/GATT/Service/Characteristic   规格解读
			什么是蓝牙service和characteristic?如何理解蓝牙profile? ATT和GATT两者如何区分?什么是attribute? attribute和characteristic的区别是 ... 
- EF获取数据库表名和列名
			EF获取数据库表名和列名 新建 模板 小书匠 /// <summary> /// 通过当前DBContext上下文获取对应数据库中所有得表 /// </summary> ... 
- 【python3】 解:import导包机制
			模块和包 模块:我们定义的.py结尾的文件就是一个模块,模块中通常定义了类.方法.变量等一系列功能: 包:存放模块的文件夹,含有init.py文件,定义path属性. import语句的作用 impo ... 
- PyQt5之 QTableView 添加复选框(自定义委托)
			import sys from untitled import Ui_Form from PyQt5.QtWidgets import QApplication, QWidget, QStyleOpt ... 
- 《C++ Primer》笔记 第10章 泛型算法
			迭代器令算法不依赖于容器,但算法依赖于元素类型的操作. 算法永远不会执行容器的操作.算法永远不会改变底层容器的大小. accumulate定义在头文件numeric中,接受三个参数,前两个指出需要求和 ... 
- 关于Laravel框架中Guard的底层实现
			1. 什么是Guard 在Laravel/Lumen框架中,用户的登录/注册的认证基本都已经封装好了,开箱即用.而登录/注册认证的核心就是: 用户的注册信息存入数据库(登记) 从数据库中读取数据和用户 ... 
- 剑指 Offer 30. 包含min函数的栈 + 双栈实现求解栈中的最小值
			剑指 Offer 30. 包含min函数的栈 Offer_30 题目描述: 题解分析: 题目其实考察的是栈的知识,本题的目的是使用两个栈来求解最小值. 第二个栈主要用来维护第一个栈中的最小值,所以它里 ... 
