5

如何在本機使用 Dapr 進行微服務應用程式開發

 2 years ago
source link: https://blog.miniasp.com/post/2021/10/19/Developing-Microservice-apps-using-Dapr-locally
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

最近花了點時間在玩 Dapr 這套非常優異的開發工具,當你想要開發分散式應用程式或想實現微服務架構,都可以深入瞭解看看,保證不虛此行。本篇文章我打算分享如何在本機使用 Dapr 開發微服務分散式應用程式,幫助大家更容易的上手這套分散式應用開發工具!👍

初始化 Dapr

  1. 安裝 Dapr CLI 命令列工具

    請依據官網文件 Install the Dapr CLI 進行安裝。

    安裝好之後你可以執行 dapr -v 取得版本資訊:

    CLI version: 1.4.0
    Runtime version: n/a
    

    你會看到 CLI 版本,但是第一次安裝還不會看到 Runtime 版本,因此你還需要對 Dapr 進行初始化!

  2. 安裝 Docker Desktop 工具

    由於 Dapr 支援兩種運行模式:Self-HostedKubernetes

    如果要用 Self-Hosted 模式在本機執行 Dapr Runtime 就需要先安裝好 Docker Desktop 才能順利運作,這也是最簡單環境配置了!

  3. 初始化 Dapr 執行環境 (Dapr Runtime)

    請依據官網文件 Initialize Dapr in your local environment 進行安裝。

    其實也就是執行以下命令就可以完成,非常容易:

    dapr init
    
    Making the jump to hyperspace...
    Installing runtime version 1.4.3
    Downloading binaries and setting up components...
    Downloaded binaries and completed components set up.
    daprd binary has been installed to C:\Users\User\.dapr\bin.
    dapr_placement container is running.
    dapr_redis container is running.
    dapr_zipkin container is running.
    Use `docker ps` to check running containers.
    Success! Dapr is up and running. To get started, go here: https://aka.ms/dapr-getting-started
    

    初始化過程會建立三個 Docker 容器,還會在你的家目錄下新增一個 .dapr 目錄,裡面會有 Dapr 重要的元件定義檔與 dapr 執行檔。

    執行完成後你可以立即使用 docker ps 查看 Docker 正在運行中的容器:

    docker ps
    

    預設會有三個重要的容器會執行起來:

    1. dapr_redis: 用來提供 Dapr 架構下的「狀態元件」使用
    2. dapr_zipkin: 用來提供 Dapr 架構下的「可觀測性元件」使用
    3. dapr_placement: 用來提供 Dapr 架構下「服務呼叫元件」使用
    CONTAINER ID   IMAGE               COMMAND                  CREATED              STATUS                        PORTS                              NAMES
    721cd994441d   redis               "docker-entrypoint.s…"   About a minute ago   Up About a minute             0.0.0.0:6379->6379/tcp             dapr_redis
    55d8466e19f6   daprio/dapr:1.4.3   "./placement"            About a minute ago   Up About a minute             0.0.0.0:6050->50005/tcp            dapr_placement
    b50ab3bf9f7f   openzipkin/zipkin   "start-zipkin"           About a minute ago   Up About a minute (healthy)   9410/tcp, 0.0.0.0:9411->9411/tcp   dapr_zipkin
    

體驗 Dapr 開發過程:使用狀態管理元件

由於微服務(分散式)應用程式彼此之間都是獨立運行的,每個微服務都應該採「無狀態」(Stateless)的方式開發,但是有些商業邏輯還是需要透過「狀態」來保存處理過程的短暫資料。此時 Dapr 提供了一個 State management 構成要素(Building Block),專門用來提供「狀態管理」等服務,有了這層微服務,你就不用在你自己的微服務下實作狀態管理,大幅簡化狀態管理的複雜度。

以下我會使用 .NET 6 來做示範開發。

  1. 建立 Console 專案

    mkdir DaprCounter && cd DaprCounter
    dotnet new console
    dotnet run
    
  2. 加入 Dapr .NET SDKDapr.Client 套件 (GitHub)

    dotnet add package Dapr.Client
    
  3. 使用 DaprClient 讀寫狀態管理元件

    using Dapr.Client;
    
    // 這裡要設定 ~/.dapr/components/statestore.yaml 裡面定義的 metadata.name 名稱
    const string storeName = "statestore";
    
    // 這裡要設定應用程式會用到的「鍵名」,若要跨微服務存取相同狀態則要用相同的鍵名
    const string key = "counter";
    
    var daprClient = new DaprClientBuilder().Build();
    var counter = await daprClient.GetStateAsync<int>(storeName, key);
    
    while (true)
    {
        Console.WriteLine($"Counter = {counter++}");
    
        await daprClient.SaveStateAsync(storeName, key, counter);
        await Task.Delay(1000);
    }
    
  4. 透過 Dapr CLI 啟動微服務 (並存取狀態服務元件)

    dapr run --app-id DaprCounter dotnet run
    
  5. 透過 Dapr CLI 停止微服務

    dapr stop --app-id DaprCounter
    

每個透過 dapr run 執行起來的「微服務」都會有個 Sidecar (邊車) 程序,他的主要職責就是幫助你跟其他微服務進行溝通,你不用特別在意這些服務在哪裡,也不用知道這些服務用到了哪些元件(例如狀態管理元件),你的程式只要直接跟這個 Sidecar 進行溝通即可,而溝通的通訊協定同時包含了 HTTPgRPC 這兩種。

事實上,你的應用程式可以直接透過 HttpClient 直接對 Sidecar 進行呼叫,他就會自動幫你將要求發送到正確的另一個微服務。不過,由於 Dapr .NET SDK 已經都幫我們封裝好這些呼叫,所以寫 .NET 的朋友是不太需要這麼做的,但是透過其他程式語言撰寫的微服務,就可以很輕易的直接透過 HttpClient 進行呼叫。所以 Dapr 才會說他是 Distributed APplication Runtime,任何程式語言框架都可以跟 Dapr 進行整合!

你可以做一個有趣的實驗看看:

  1. 啟動 Dapr 但不啟動 DaprCounter 應用程式

    dapr run --app-id DaprCounter --dapr-http-port 3500
    

    這段命令的意思,就是單純啟動 Dapr Sidecar 程序,而不執行任意微服務應用程式,你可以藉此測試 Dapr Sidecar 的能力。

  2. 透過 curl 直接存取狀態服務元件的狀態內容

    curl http://localhost:3500/v1.0/state/statestore/counter
    

    其中 3500 是 Dapr Sidecar 程序的接聽 Port,而 statestore 則是狀態服務元件的名稱,還有 counter 則是先前透過 DaprCounter 應用程式寫入的鍵名。

體驗 Dapr 開發過程:在 ASP.NET Core 使用狀態管理元件

  1. 建立一個全新的 ASP.NET Core 專案

    mkdir DaprCounterASPNET && cd DaprCounterASPNET
    dotnet new webapi --no-https
    dotnet run
    

    這裡加入 --no-https 蠻重要的,因為 Dapr 所管理的微服務預設是走 HTTP 協定,但也可視狀況啟用 mTLS 加密連線。

  2. 加入 Dapr .NET SDKDapr.AspNetCore 套件 (GitHub)

    dotnet add package Dapr.AspNetCore
    
  3. 設定 Dapr 到 DI 服務集合中

    找到 builder.Services.AddControllers(); 並直接加上 .AddDapr() 即可,完成結果如下:

    builder.Services.AddControllers().AddDapr();
    
  4. 修改 Controllers\WeatherForecastController.cs 檔案並加入一個新的 Action 動作方法

    你的 Action 可以利用路由參數模型繫結(Model Binding)功能,自動將繫結到的 {key} 帶入到 [FromState("statestore", "key")] 的第二個參數中,讓你的 counter 參數可以直接接收到狀態服務元件中的資料!

    以下三段程式都是相同的意思,你選一種使用即可:

    [HttpGet("{key}")]
    public IActionResult GetCounter([FromState("statestore", "key")] Dapr.StateEntry<int> counter)
    {
        return Ok(counter.Value);
    }
    
    [HttpGet("{key}")]
    public IActionResult GetCounter([FromState("statestore", "key")] int counter)
    {
        return Ok(counter.Value);
    }
    
    [HttpGet("{key}")]
    public IActionResult GetCounter([FromState("statestore")] Dapr.StateEntry<int> key)
    {
        return Ok(counter.Value);
    }
    

    你也可以注入 DaprClient 來操作狀態管理元件,當你不需要使用路由參數來繫結參數時可以這樣用:

    [HttpGet("counter")]
    public async Task<IActionResult> GetCounter([FromServices] DaprClient daprClient)
    {
        var counter = await daprClient.GetStateEntryAsync<int>("statestore", "counter");
        return Ok(counter.Value);
    }
    
    [HttpGet("counter")]
    public async Task<IActionResult> GetCounter([FromServices] DaprClient daprClient)
    {
        int counter = await daprClient.GetStateAsync<int>("statestore", "counter");
        return Ok(counter);
    }
    
  5. 啟動微服務

    我為了要能夠測試先前透過 DaprCounter 寫入的參數,可以透過這個 ASP.NET Core 應用程式取得一樣的狀態,我這邊在啟動時刻意設定了 --app-idDaprCounter,如此一來就可以取得到相同狀態管理元件下的資訊。這件事意味著 Dapr 在啟動時會參考 --app-id 做為「微服務」的應用程式編號,所有狀態服務皆相依於特定微服務。

    實務上來說,我們不應該在不同的微服務下共用狀態,比較正確的作法,應該是避免共用狀態,當你需要另一個服務的狀態,應該透過該微服務提供的 API 來取得狀態,而非直接存取狀態管理元件下的資料。

    dapr run --app-id DaprCounter --app-port 5000 -- dotnet run --no-launch-profile
    

    注意: 我特別加上 --no-launch-profile 的目的是為了讓 ASP.NET Core 可以跑在預設的 HTTP 5000 Port 底下。這裡的指定 --app-port 的目的,是為了讓 Dapr Sidecar 可以知道你的應用程式接聽的 Port 號,好讓其他微服務日後可以順利的連上。實際上 Dapr Sidecar 會定時檢查 --app-port 是否可以連上,用以確保服務間通訊可以順利進行。

  6. 測試服務執行結果

    curl http://localhost:5000/WeatherForecast/counter
    
  7. 透過 Dapr CLI 停止微服務

    dapr stop --app-id DaprCounter
    

體驗 Dapr 開發過程:從 DaprCounter 呼叫 DaprCounterASPNET 服務

請先用 dapr list 確認上述兩個微服務已經全部停止。

  1. 先調整 ASP.NET Core 專案的 Controllers\WeatherForecastController.cs 控制器

    請確認程式碼中有加入以下兩個 Action 動作方法:

    [HttpGet("counter")]
    public async Task<IActionResult> GetCounter([FromServices] DaprClient daprClient)
    {
        int counter = await daprClient.GetStateAsync<int>("statestore", "counter");
        return Ok(counter);
    }
    
    [HttpPut("counter")]
    public async Task<IActionResult> PutCounter([FromServices] DaprClient daprClient)
    {
        var counter = await daprClient.GetStateEntryAsync<int>("statestore", "counter");
    
        counter.Value += 1;
    
        if (await counter.TrySaveAsync())
        {
            return Ok(counter.Value);
        }
        else
        {
            return BadRequest();
        }
    }
    
  2. 啟動 DaprCounterASPNET 微服務

    dapr run --app-id DaprCounterASPNET --app-port 5000 -- dotnet run --no-launch-profile
    
  3. 調整 Console 專案的 Program.cs 檔案

    你可以從 daprClient.InvokeMethodAsync 的呼叫方式發現,當你對另一個微服務發出 API 呼叫,只需要知道服務名稱(--app-id)就可以連上!

    using Dapr.Client;
    
    var daprClient = new DaprClientBuilder().Build();
    
    while (true)
    {
        var counter = await daprClient.InvokeMethodAsync<int>(HttpMethod.Put, "DaprCounterASPNET", "WeatherForecast/counter");
        Console.WriteLine($"Counter = {counter}");
    
        await Task.Delay(1000);
    }
    

    這裡的 daprClient.InvokeMethodAsync() 方法是非常萬用的,第一個參數 httpMethod 就是準備發出的 HTTP 方法,第二個參數 appId 則是輸入微服務的 App Id,第三個參數 methodName 對於 API 服務來說,就是端點 (Endpoint)。你只要準備好這三個參數,就可以對 Dapr 底下的 DaprCounterASPNET 微服務發出 API 呼叫,完全不用在意對方在哪裡!

  4. 啟動 DaprCounter 微服務

    dapr run --app-id DaprCounter dotnet run
    

    此時你會發現,兩個微服務之間已經相當順利的運作中!👍

移除 Dapr Runtime

  • 解除安裝 Dapr Runtime

    dapr uninstall
    

    這個命令只會移除 dapr_placement 服務與 Dapr CLI 執行檔,不會移除 dapr_redisdapr_zipkin 容器,因為裡面可能還存有一些狀態。

  • 完整解除安裝 Dapr Runtime

    加上 --all 參數就可以一併移除所有跟 Dapr 相依的元件服務:

    dapr uninstall --all
    

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK