YARP實現Dapr服務調用的反向代理


楔子

公司即將新開項目,打算用點時髦的技術,需要探探路。之前沒做過微服務項目,沒有技術棧方面的積(負)累(債),
干脆就上微軟的分布式運行時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技術棧的玩家已經可以用它來做些簡單的反向代理了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM