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

楔子

优惠券网 m.cps3.cn

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

开发环境

  1. WSL
    windows下跨平台开发的优秀方案,Linux 分发版我用的Ubuntu 20.04
  2. Docker Desktop
    虽然Docker不是Dapr开发环境的唯一选择,但Docker的跨平台做的很好,尤其Docker Desktop可视化,还自带Docker-Compose,安装也方便,可以弹射起步。
    安装完打开后,打开控制台,验证一下:
    docker --version
    
  3. dotnet SDK
    YARP最低支持.NET Core 3.1,这个时间点(2021.04),推荐.NET 5
    验证:
    dotnet --version
    
  4. Dapr
    Dapr安装我记得挺快的,之后的初始化dapr init,网不好的话,可能要多试几次。
    初始化确认Docker Desktopdapr_placementdapr_redisdapr_zipkin 3个容器都在正常运行
  5. Tye
    Tye 是微软开发提供的一款简化分布式应用开发的辅助命令行工具。用.NET写的,自带Dapr扩展。
    dotnet tool全局安装后,可以如下验证:
    tye --version
    

知识储备

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

原理

image

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

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

  3. 请求转给Dapr-sidecar
  4. Dapr 服务发现 终端服务并调用
  5. 返回响应

开发

  1. 创建两个Asp.Net Core项目:

    1. GatewayDemo 网关Demo
    2. ServiceSample 示例服务
  2. 完成 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
            });
        }
    }
    
  3. 添加引用

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

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

    2. 这个示例比较简单,不需要引用Dapr的SDK
  4. 动态添加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())
    
  5. 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。

  6. 添加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!"); });
            });
        }
    }
    
  7. 配置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/文件夹下还有一些配置文件,这里就不贴了

  8. 用tye同时运行多个项目
    确保dapr那3个容器服务正常运行后,运行 tye run,通过tye的Dapr扩展,运行显示以下几个服务:

    1. gateway-demo
    2. service-sample
    3. zipkin
    4. gateway-demo-dapr
    5. service-sample-dapr
    6. ingress
  9. 测试:
    浏览器直接打开:https://localhost:44363/api/service-sample/sample/1234
    如果显示 {"account":"1234","balance":100} 就说明通了。

附上Demo代码

github

尾声

因为目前没有用到对外的grpc调用和Actor、发布订阅等功能,所以本示例没有相关代码演示。
Yarp虽然还在preview,但有兴趣的.NET技术栈的玩家已经可以用它来做些简单的反向代理了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值