微軟的分佈式應用框架 Dapr

微服務架構已成爲構建雲原生應用程序的標準,微服務架構提供了使人信服的好處,包括可伸縮性,鬆散的服務耦合和獨立部署,可是這種方法的成本很高,須要瞭解和熟練掌握分佈式系統。爲了使用全部開發人員可以使用任何語言和任何框架輕鬆地構建便攜式微服務應用程序,不管是開發新項目仍是遷移現有代碼html

Dapr 介紹

Github:  https://github.com/dapr/daprgit

Dapr是一種可移植的,事件驅動的,無服務器運行時,用於構建跨雲和邊緣的分佈式應用程序。github

Distributed Application Runtime. An event-driven, portable runtime for building microservices on cloud and edge.web

dapr_conceptual_model

其中提到了多語言和多開發者框架,我認爲這是他選擇的經過通訊共享信息,即 HTTPGRPC 支持多語言等特性。微軟想經過這個設定一個構建微服務應用的規則。從根本上確立你開發的每個應用的獨立性。賦能每一個開發者,爲了使Dapr對於不一樣的語言更加方便,它還包括針對Go,Java,JavaScript,.NET和Python的語言特定的SDK。這些SDK經過類型化的語言API(而不是調用http / gRPC API)公開了Dapr構建塊中的功能,例如保存狀態,發佈事件或建立actor。這使開發人員可使用他們選擇的語言編寫無狀態和有狀態功能以及參與者的組合。而且因爲這些SDK共享Dapr運行時,您甚至能夠得到跨語言的actor和功能支持!redis

Dapr還能夠與任何開發人員框架集成。例如,在Dapr .NET SDK中,您將找到ASP.NET Core集成,該集成帶來了可響應其餘服務的發佈/訂閱事件的狀態路由控制器,從而使ASP.NET Core成爲構建微服務Web應用程序的更好框架。docker


不過須要注意的是Dapr目前正處於Alpha階段, 今天剛發佈了0.2版本。在v1.0穩定版本發佈以前,建議不要用於生產環境。shell


下面進行一個 QuickStartapi

環境

  1. Install Docker(微服務已經離不開容器化了)
  2. Install Dapr CLI
  3. Install .Net Core SDK 3.0

在Windows 上經過Powershell 安裝:服務器

powershell -Command "iwr -useb https://raw.githubusercontent.com/dapr/cli/master/install/install.ps1 | iex"
而後把 c:\dapr 添加到環境變量 PATH
運行dapr命令,檢查輸出是否正常

C:\workshop\Github\dotnet-sdk>dapr --help架構

         __
     ____/ /___ _____  _____
    / __  / __ '/ __ \/ ___/
   / /_/ / /_/ / /_/ / /   
   \__,_/\__,_/ .___/_/
               /_/

======================================================
A serverless runtime for hyperscale, distributed systems

Usage:
   dapr [command]

Available Commands:
   help        Help about any command
   init        Setup dapr in Kubernetes or Standalone modes
   list        List all dapr instances
   publish     publish an event to multiple consumers
   run         Launches dapr and your app side by side
   send        invoke a dapr app with an optional payload
   stop        Stops a running dapr instance and its associated app
   uninstall   removes a dapr installation

Flags:
   -h, --help      help for dapr
       --version   version for dapr

Use "dapr [command] --help" for more information about a command.


執行初始化(會啓動 docker 容器)
dapr init 
Making the jump to hyperspace... 
Downloading binaries and setting up components 
Success! Dapr is up and running
下載.NET SDk代碼
https://github.com/dapr/dotnet-sdk ,裏面有.NET Core的多個示例代碼:
示例 描述
1. Actor Demonstrates creating virtual actors that encapsulate code and state. Also see docs in this repo for a tutorial.
2. ASP.NET Core Demonstrates ASP.NET Core integration with Dapr by create Controllers and Routes.
3. gRPC client

The gRPC client sample shows how to make Dapr calls to publish events, save state, get state and delete state using a gRPC client.


咱們一塊兒來看下ASP.NET Core的Demo;

例子中主 咱們使用 Dapr 的交互。Dapr經過 Runtime

  • 提供 Dapr API 給多語言調用。
  • 提供 狀態管理 By state stores


/// <summary>
  /// Sample showing Dapr integration with controller.
  /// </summary>
  [ApiController]
  public class SampleController : ControllerBase
  {
      /// <summary>
      /// Gets the account information as specified by the id.
      /// </summary>
      /// <param name="account">Account information for the id from Dapr state store.</param>
      /// <returns>Account information.</returns>
      [HttpGet("{account}")]
      public ActionResult<Account> Get(StateEntry<Account> account)
      {
          if (account.Value is null)
          {
              return this.NotFound();
          }

         return account.Value;
      }


     /// <summary>
      /// Method for depositing to account as psecified in transaction.
      /// </summary>
      /// <param name="transaction">Transaction info.</param>
      /// <param name="stateClient">State client to interact with dapr runtime.</param>
      /// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
      [Topic("deposit")]
      [HttpPost("deposit")]
      public async Task<ActionResult<Account>> Deposit(Transaction transaction, [FromServices] StateClient stateClient)
      {
          var state = await stateClient.GetStateEntryAsync<Account>(transaction.Id);
          state.Value ??= new Account() { Id = transaction.Id, };
          state.Value.Balance += transaction.Amount;
          await state.SaveAsync();
          return state.Value;
      }


     /// <summary>
      /// Method for withdrawing from account as specified in transaction.
      /// </summary>
      /// <param name="transaction">Transaction info.</param>
      /// <param name="stateClient">State client to interact with dapr runtime.</param>
      /// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
      [Topic("withdraw")]
      [HttpPost("withdraw")]
      public async Task<ActionResult<Account>> Withdraw(Transaction transaction, [FromServices] StateClient stateClient)
      {
          var state = await stateClient.GetStateEntryAsync<Account>(transaction.Id);

         if (state.Value == null)
          {
              return this.NotFound();
          }

         state.Value.Balance -= transaction.Amount;
          await state.SaveAsync();
          return state.Value;
      }
  }


這裏重點是狀態存儲,即將  經過 StateClient 存儲在  中,咱們經過狀態轉移在  裏實現了 stateDaprDaprstateless。

Dapr 運行.NET 應用程序

演示Dapr的服務調用,在終端中切換到項目目錄,而後使用dapr啓動應用


C:\workshop\Github\dotnet-sdk\samples\AspNetCore\ControllerSample>dapr run --app-id routing --app-port 5000 dotnet run   
Starting Dapr with id routing. HTTP Port: 61102. gRPC Port: 61103
You're up and running! Both Dapr and your app logs will appear here.


注意: 以上dapr run命令,經過app-id指定了應用的ID,經過app-port指定了應用的端口(webapi默認使用5000做爲http端口),後跟dotnet run命名啓動當前項目。可參考Dapr文檔服務調用


後臺運行的 CLI 命令,這裏是前臺打印的日誌, 注意到 .NET App 在指定的 5000 端口運行,同時還有狀態存儲的 redis6379 端口運行


== DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="starting Dapr Runtime -- version 0.2.0 -- commit c75b11
1-dirty"
     == DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="log level set to: info"
== DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="standalone mode configured"
== DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="dapr id: routing"
== DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="loaded component messagebus (pubsub.redis)"       
     == DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="loaded component statestore (state.redis)"
     == DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="application protocol: http. waiting on port 5000"
     == APP == info: Microsoft.Hosting.Lifetime[0]
     == APP ==       Now listening on: http://localhost:5000
== APP == info: Microsoft.Hosting.Lifetime[0]
== APP ==       Application started. Press Ctrl+C to shut down.
     == APP == info: Microsoft.Hosting.Lifetime[0]
== APP ==       Hosting environment: Development
== APP == info: Microsoft.Hosting.Lifetime[0]
== APP ==       Content root path: C:\workshop\Github\dotnet-sdk\samples\AspNetCore\ControllerSample
     == DAPR == time="2019-11-16T18:33:31+08:00" level=info msg="application discovered on port 5000"
     == DAPR == 2019-11-16 18:33:32.029764 I | redis: connecting to localhost:6379
     == DAPR == 2019-11-16 18:33:32.036316 I | redis: connected to localhost:6379 (localAddr: [::1]:61164, remAddr:
[::1]:6379)
     == DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actor runtime started. actor idle timeout: 1h0m0s.
actor scan interval: 30s"
     == DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: starting connection attempt to placement se
rvice at localhost:6050"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="http server is running on port 61102"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="gRPC server is running on port 61103"
     == DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="local service entry announced"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="dapr initialized. Status: Running. Init Elapsed 917
6.5164ms"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: established connection to placement service
  at localhost:6050"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: placement order received: lock"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: placement order received: update"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: placement tables updated"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: placement order received: unlock"
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[100]
== APP ==       Start processing HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[100]
== APP ==       Sending HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[101]
== APP ==       Received HTTP response after 2228.2998000000002ms - OK    
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[101]      
== APP ==       End processing HTTP request after 2257.3405000000002ms - OK
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[100]
== APP ==       Start processing HTTP request POST http://localhost:61102/v1.0/state
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[100]
== APP ==       Sending HTTP request POST http://localhost:61102/v1.0/state
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[101]
     == APP ==       Received HTTP response after 67.46000000000001ms - Created
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[101]
== APP ==       End processing HTTP request after 68.0343ms - Created
     == APP == info: System.Net.Http.HttpClient.state.LogicalHandler[100]
     == APP ==       Start processing HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[100]
== APP ==       Sending HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[101]
== APP ==       Received HTTP response after 5.8247ms - OK
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[101]
== APP ==       End processing HTTP request after 6.268400000000001ms - OK
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[100]
== APP ==       Start processing HTTP request POST http://localhost:61102/v1.0/state
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[100]
== APP ==       Sending HTTP request POST http://localhost:61102/v1.0/state
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[101]
== APP ==       Received HTTP response after 4.5181000000000004ms - Created
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[101]
== APP ==       End processing HTTP request after 4.6208ms - Created
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[100]
== APP ==       Start processing HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[100]
== APP ==       Sending HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[101]
== APP ==       Received HTTP response after 20.2967ms - OK
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[101]
== APP ==       End processing HTTP request after 20.691100000000002ms – OK



爲了同時實現可移植性和與現有代碼的輕鬆集成,Dapr經過http或gRPC提供了標準API。Dapr端口可從Dapr啓動日誌中獲取,如如下日誌表示Dapr公開的HTTP端口爲61102(經過Dapr也可以使用gRPC方式進行服務調用)


== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="http server is running on port 61102"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="gRPC server is running on port 61103"


咱們可經過如下地址來調用示例方法:

直接調用:GET http://localhost:5000/17

經過Dapr服務調用: GET http://localhost:61102/v1.0/invoke/routing/method/17

注意: Dapr的服務調用是有dapr sidecar來實現的,在被調用的服務中無需注入任何與dapr相關的代碼。
相關文章
相關標籤/搜索