天天看點

Dapr實戰(二) 服務調用

服務調用是什麼

在分布式應用程式中的服務之間進行調用會涉及到許多挑戰。 例如:

  • 維護其他服務的位址。
  • 如何安全地調用服務。
  • 在發生短暫的 暫時性錯誤 時如何處理重試。
  • 分布式應用程式調用鍊路追蹤。

服務調用建構塊通過使用 Dapr 挎鬥作為服務的 反向代理 來解決這些難題。

工作原理

Dapr實戰(二) 服務調用

由于調用經過Sidecar,Dapr 可以注入一些有其他行為:

  • 失敗時自動重試調用。
  • 通過互相 (mTLS) 身份驗證(包括自動證書滾動更新),在服務之間進行調用。
  • 使用通路控制政策控制用戶端可以執行的操作。
  • 捕獲服務間所有調用的跟蹤和名額,以提供分布式調用鍊路追蹤與診斷。

任何應用程式都可以通過使用 Dapr 中内置的本機 Invoke API 來調用 Dapr Sidecar。 可以通過 HTTP 或 gRPC 調用 API。 使用以下 URL 調用 HTTP API:

http://localhost:<dapr-port>/v1.0/invoke/<application-id>/method/<method-name>      
  • <dapr-port>

     Dapr 正在偵聽的 HTTP 端口。
  • <application-id>

     要調用的服務的應用程式 ID。
  • <method-name>

     要在遠端服務上調用的方法的名稱。

項目示範

我們使用.NET5建立兩個WebAPI項目:BackEnd和FrontEnd,通過FrontEnd調用BackEnd

Dapr實戰(二) 服務調用

指定BackEnd預設啟動端口5000

public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>().UseUrls("http://*:5000");
                });      

通過Dapr CLI啟動BackEnd,指定sidecar端口為3511,預設為3500,指定app-port是5000,與BackEnd預設端口保持一緻

dapr run --dapr-http-port 3511 --app-port 5000 --app-id backend dotnet  .\BackEnd\bin\Debug\net5.0\BackEnd.dll      
C:\demo\test\DaprBackEnd>dapr run --dapr-http-port 3511 --app-port 5000 --app-id backend dotnet  .\BackEnd\bin\Debug\net5.0\BackEnd.dll
Starting Dapr with id backend. HTTP Port: 3511. gRPC Port: 30204
time="2021-09-23T14:14:08.3785429+08:00" level=info msg="starting Dapr Runtime -- version 1.4.0 -- commit ed969edc72b3934fffb481f079b736f3588e373a" app_id=backend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:14:08.3831089+08:00" level=info msg="log level set to: info" app_id=backend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:14:08.3831089+08:00" level=info msg="metrics server started on :30205/" app_id=backend instance=chesterchen-lap scope=dapr.metrics type=log ver=1.4.0
time="2021-09-23T14:14:08.3861203+08:00" level=info msg="standalone mode configured" app_id=backend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:14:08.3861203+08:00" level=info msg="app id: backend" app_id=backend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:14:08.3871107+08:00" level=info msg="mTLS is disabled. Skipping certificate request and tls validation" app_id=backend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:14:08.4115681+08:00" level=info msg="local service entry announced: backend -> 10.32.193.9:30209" app_id=backend instance=chesterchen-lap scope=dapr.contrib type=log ver=1.4.0
time="2021-09-23T14:14:08.4115681+08:00" level=info msg="Initialized name resolution to mdns" app_id=backend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:14:08.4127024+08:00" level=info msg="loading components" app_id=backend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:14:08.4160224+08:00" level=info msg="component loaded. name: pubsub, type: pubsub.redis/v1" app_id=backend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:14:08.4160224+08:00" level=info msg="waiting for all outstanding components to be processed" app_id=backend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:14:08.4187042+08:00" level=info msg="component loaded. name: statestore, type: state.redis/v1" app_id=backend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:14:08.4187042+08:00" level=info msg="all outstanding components processed" app_id=backend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:14:08.4192486+08:00" level=info msg="enabled gRPC tracing middleware" app_id=backend instance=chesterchen-lap scope=dapr.runtime.grpc.api type=log ver=1.4.0
time="2021-09-23T14:14:08.4192486+08:00" level=info msg="enabled gRPC metrics middleware" app_id=backend instance=chesterchen-lap scope=dapr.runtime.grpc.api type=log ver=1.4.0
time="2021-09-23T14:14:08.4192486+08:00" level=info msg="API gRPC server is running on port 30204" app_id=backend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:14:08.4197888+08:00" level=info msg="enabled metrics http middleware" app_id=backend instance=chesterchen-lap scope=dapr.runtime.http type=log ver=1.4.0
time="2021-09-23T14:14:08.4197888+08:00" level=info msg="enabled tracing http middleware" app_id=backend instance=chesterchen-lap scope=dapr.runtime.http type=log ver=1.4.0
time="2021-09-23T14:14:08.4202954+08:00" level=info msg="http server is running on port 3511" app_id=backend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:14:08.420335+08:00" level=info msg="The request body size parameter is: 4" app_id=backend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:14:08.420335+08:00" level=info msg="enabled gRPC tracing middleware" app_id=backend instance=chesterchen-lap scope=dapr.runtime.grpc.internal type=log ver=1.4.0
time="2021-09-23T14:14:08.4225403+08:00" level=info msg="enabled gRPC metrics middleware" app_id=backend instance=chesterchen-lap scope=dapr.runtime.grpc.internal type=log ver=1.4.0
time="2021-09-23T14:14:08.4230868+08:00" level=info msg="internal gRPC server is running on port 30209" app_id=backend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:14:08.4230868+08:00" level=info msg="application protocol: http. waiting on port 5000.  This will block until the app is listening on that port." app_id=backend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
== APP == info: Microsoft.Hosting.Lifetime[0]
== APP ==       Now listening on: http://[::]: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: Production
== APP == info: Microsoft.Hosting.Lifetime[0]
== APP ==       Content root path: C:\demo\test\DaprBackEnd
time="2021-09-23T14:14:08.7252681+08:00" level=info msg="application discovered on port 5000" app_id=backend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
== APP == info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
== APP ==       Request starting HTTP/1.1 GET http://127.0.0.1:5000/dapr/config application/json -
== APP == info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
== APP ==       Request finished HTTP/1.1 GET http://127.0.0.1:5000/dapr/config application/json - - 404 0 - 17.3850ms
time="2021-09-23T14:14:08.7693649+08:00" level=info msg="application configuration loaded" app_id=backend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:14:08.7699003+08:00" level=info msg="actor runtime started. actor idle timeout: 1h0m0s. actor scan interval: 30s" app_id=backend instance=chesterchen-lap scope=dapr.runtime.actor type=log ver=1.4.0
== APP == info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
== APP ==       Request starting HTTP/1.1 GET http://127.0.0.1:5000/dapr/subscribe application/json -
== APP == info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
time="2021-09-23T14:14:08.7736383+08:00" level=info msg="dapr initialized. Status: Running. Init Elapsed 387.518ms" app_id=backend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
== APP ==       Request finished HTTP/1.1 GET http://127.0.0.1:5000/dapr/subscribe application/json - - 404 0 - 0.1789ms
time="2021-09-23T14:14:08.7888444+08:00" level=info msg="placement tables updated, version: 0" app_id=backend instance=chesterchen-lap scope=dapr.runtime.actor.internal.placement type=log ver=1.4.0
Updating metadata for app command: dotnet .\BackEnd\bin\Debug\net5.0\BackEnd.dll
You're up and running! Both Dapr and your app logs will appear here.      

現在修改FrontEnd裡Demo,指定啟動端口5001

public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>().UseUrls("http://*:5001");
                });      

引入Nuget包 Dapr.Client,建立DaprController

Dapr實戰(二) 服務調用

1.使用 HttpClient調用HTTP服務

using Dapr.Client;

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;

namespace FrontEnd.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class DaprController : ControllerBase
    {

        private readonly ILogger<DaprController> _logger;

        public DaprController(ILogger<DaprController> logger)
        {
            _logger = logger;
        }

        // 通過HttpClient調用BackEnd
        [HttpGet]
        public async Task<ActionResult> GetAsync()
        {
            using var httpClient = DaprClient.CreateInvokeHttpClient();
            var result = await httpClient.GetAsync("http://backend/WeatherForecast");
            var resultContent = string.Format("result is {0} {1}", result.StatusCode, await result.Content.ReadAsStringAsync());
            return Ok(resultContent);
        }
    }
}      

GetAsync API中通過DaprClient.CreateInvokeHttpClient()建立了HttpClient,通過GetAsync方法調用了backend服務中的WeatherForecastAPI。

Sidecar使用可插接式名稱解析元件來解析服務BackEnd的位址。在自承載模式下,Dapr 使用 mdn 來查找它。 在 Kubernetes 模式下運作時,Kubernetes DNS 服務将确定位址。

2.使用 DaprClient調用HTTP服務

// 通過DaprClient調用BackEnd
        [HttpGet("get2")]
        public async Task<ActionResult> Get2Async()
        {
            using var daprClient = new DaprClientBuilder().Build();
            var result = await daprClient.InvokeMethodAsync<IEnumerable<WeatherForecast>>(HttpMethod.Get, "backend", "WeatherForecast");
            return Ok(result);
        }      

DaprController中新增API Get2Async 

3.使用注入方式調用 DaprClient

首先引入Nuget包Dapr.AspNetCore,然後在Startup.cs注入Dapr

public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers().AddDapr();
        }      

建立DaprDIController

using Dapr.Client;

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;

namespace FrontEnd.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class DaprDIController : ControllerBase
    {
        private readonly ILogger<DaprDIController> _logger;
        private readonly DaprClient _daprClient;
        public DaprDIController(ILogger<DaprDIController> logger, DaprClient daprClient)
        {
            _logger = logger;
            _daprClient = daprClient;
        }

        [HttpGet()]
        public async Task<ActionResult> GetAsync()
        {
            var result = await _daprClient.InvokeMethodAsync<IEnumerable<WeatherForecast>>(HttpMethod.Get, "backend", "WeatherForecast");
            return Ok(result);
        }
    }
}      

以上代碼通過注入方式注入DaprClient

4.使用DaprClient同樣可以調用GRPC

await daprClient.InvokeMethodGrpcAsync<Order, OrderConfirmation>("orderservice", "submitOrder", order);      

與HTTP調用方式一緻,不再為GRPC建立server

通過Dapr CLI啟動FrontEnd,指定sidecar端口為3501,預設為3500,指定app-port是5001,與FrontEnd預設端口保持一緻

dapr run --dapr-http-port 3501 --app-port 5001  --app-id frontend dotnet  .\FrontEnd\bin\Debug\net5.0\FrontEnd.dll      
C:\demo\test\DaprBackEnd>dapr run --dapr-http-port 3501 --app-port 5001  --app-id frontend dotnet  .\FrontEnd\bin\Debug\net5.0\FrontEnd.dll
Starting Dapr with id frontend. HTTP Port: 3501. gRPC Port: 1045
time="2021-09-23T14:15:24.5222236+08:00" level=info msg="starting Dapr Runtime -- version 1.4.0 -- commit ed969edc72b3934fffb481f079b736f3588e373a" app_id=frontend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:15:24.5269659+08:00" level=info msg="log level set to: info" app_id=frontend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:15:24.5269659+08:00" level=info msg="metrics server started on :1046/" app_id=frontend instance=chesterchen-lap scope=dapr.metrics type=log ver=1.4.0
time="2021-09-23T14:15:24.5302603+08:00" level=info msg="standalone mode configured" app_id=frontend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:15:24.5302904+08:00" level=info msg="app id: frontend" app_id=frontend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:15:24.5302904+08:00" level=info msg="mTLS is disabled. Skipping certificate request and tls validation" app_id=frontend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:15:24.559128+08:00" level=info msg="local service entry announced: frontend -> 10.32.193.9:1051" app_id=frontend instance=chesterchen-lap scope=dapr.contrib type=log ver=1.4.0
time="2021-09-23T14:15:24.559128+08:00" level=info msg="Initialized name resolution to mdns" app_id=frontend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:15:24.560108+08:00" level=info msg="loading components" app_id=frontend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:15:24.5641089+08:00" level=info msg="component loaded. name: pubsub, type: pubsub.redis/v1" app_id=frontend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:15:24.5641089+08:00" level=info msg="waiting for all outstanding components to be processed" app_id=frontend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:15:24.5671082+08:00" level=info msg="component loaded. name: statestore, type: state.redis/v1" app_id=frontend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:15:24.5671082+08:00" level=info msg="all outstanding components processed" app_id=frontend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:15:24.5671082+08:00" level=info msg="enabled gRPC tracing middleware" app_id=frontend instance=chesterchen-lap scope=dapr.runtime.grpc.api type=log ver=1.4.0
time="2021-09-23T14:15:24.5671082+08:00" level=info msg="enabled gRPC metrics middleware" app_id=frontend instance=chesterchen-lap scope=dapr.runtime.grpc.api type=log ver=1.4.0
time="2021-09-23T14:15:24.5671082+08:00" level=info msg="API gRPC server is running on port 1045" app_id=frontend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:15:24.5684039+08:00" level=info msg="enabled metrics http middleware" app_id=frontend instance=chesterchen-lap scope=dapr.runtime.http type=log ver=1.4.0
time="2021-09-23T14:15:24.5700491+08:00" level=info msg="enabled tracing http middleware" app_id=frontend instance=chesterchen-lap scope=dapr.runtime.http type=log ver=1.4.0
time="2021-09-23T14:15:24.5700491+08:00" level=info msg="http server is running on port 3501" app_id=frontend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:15:24.5705931+08:00" level=info msg="The request body size parameter is: 4" app_id=frontend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:15:24.5705931+08:00" level=info msg="enabled gRPC tracing middleware" app_id=frontend instance=chesterchen-lap scope=dapr.runtime.grpc.internal type=log ver=1.4.0
time="2021-09-23T14:15:24.5705931+08:00" level=info msg="enabled gRPC metrics middleware" app_id=frontend instance=chesterchen-lap scope=dapr.runtime.grpc.internal type=log ver=1.4.0
time="2021-09-23T14:15:24.5711294+08:00" level=info msg="internal gRPC server is running on port 1051" app_id=frontend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:15:24.5711294+08:00" level=info msg="application protocol: http. waiting on port 5001.  This will block until the app is listening on that port." app_id=frontend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
== APP == info: Microsoft.Hosting.Lifetime[0]
== APP ==       Now listening on: http://[::]:5001
== APP == info: Microsoft.Hosting.Lifetime[0]
== APP ==       Application started. Press Ctrl+C to shut down.
== APP == info: Microsoft.Hosting.Lifetime[0]
== APP ==       Hosting environment: Production
== APP == info: Microsoft.Hosting.Lifetime[0]
== APP ==       Content root path: C:\demo\test\DaprBackEnd
time="2021-09-23T14:15:24.8729143+08:00" level=info msg="application discovered on port 5001" app_id=frontend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
== APP == info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
== APP ==       Request starting HTTP/1.1 GET http://127.0.0.1:5001/dapr/config application/json -
== APP == info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
== APP ==       Request finished HTTP/1.1 GET http://127.0.0.1:5001/dapr/config application/json - - 404 0 - 19.0408ms
time="2021-09-23T14:15:24.9188354+08:00" level=info msg="application configuration loaded" app_id=frontend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:15:24.9193692+08:00" level=info msg="actor runtime started. actor idle timeout: 1h0m0s. actor scan interval: 30s" app_id=frontend instance=chesterchen-lap scope=dapr.runtime.actor type=log ver=1.4.0
== APP == info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
== APP ==       Request starting HTTP/1.1 GET http://127.0.0.1:5001/dapr/subscribe application/json -
== APP == info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
== APP ==       Request finished HTTP/1.1 GET http://127.0.0.1:5001/dapr/subscribe application/json - - 404 0 - 0.2000ms
time="2021-09-23T14:15:24.9236093+08:00" level=info msg="dapr initialized. Status: Running. Init Elapsed 393.349ms" app_id=frontend instance=chesterchen-lap scope=dapr.runtime type=log ver=1.4.0
time="2021-09-23T14:15:24.9393948+08:00" level=info msg="placement tables updated, version: 0" app_id=frontend instance=chesterchen-lap scope=dapr.runtime.actor.internal.placement type=log ver=1.4.0
Updating metadata for app command: dotnet .\FrontEnd\bin\Debug\net5.0\FrontEnd.dll
You're up and running! Both Dapr and your app logs will appear here.      

測試調用

1.浏覽器位址欄輸入

http://localhost:3501/v1.0/invoke/frontend/method/dapr      
http://localhost:3501/v1.0/invoke/frontend/method/dapr/get2      
http://localhost:3501/v1.0/invoke/frontend/method/DaprDI      

可以看到正常響應

Dapr實戰(二) 服務調用

 2.DaprCLI測試調用

打開cmd輸入

dapr invoke --app-id frontend --verb "GET" --method dapr      

也可以看到調用成功

C:\Users\chesterychen>dapr invoke --app-id frontend --verb "GET" --method dapr
result is OK [{"date":"2021-09-24T14:20:51.2386681+08:00","temperatureC":47,"temperatureF":116,"summary":"Mild"},{"date":"2021-09-25T14:20:51.2386705+08:00","temperatureC":50,"temperatureF":121,"summary":"Mild"},{"date":"2021-09-26T14:20:51.2386707+08:00","temperatureC":34,"temperatureF":93,"summary":"Hot"},{"date":"2021-09-27T14:20:51.2386708+08:00","temperatureC":42,"temperatureF":107,"summary":"Bracing"},{"date":"2021-09-28T14:20:51.2386709+08:00","temperatureC":-19,"temperatureF":-2,"summary":"Warm"}]
App invoked successfully      

PS:單機運作的情況下,每個服務的sidecar是一個程序,名為daprd,下圖兩個分别是backend和frontend連個服務的

Dapr實戰(二) 服務調用

 鍊路追蹤

自承載的方式下,Dapr預設啟動了zipkin容器,可以通過以下連結檢視

http://localhost:9411/zipkin/      
Dapr實戰(二) 服務調用
Dapr實戰(二) 服務調用