Service Invoke | 服务调用是什么?
服务调用是 dapr 的 building blocks 的其中一部分,用于执行直接而安全的服务间方法调用。
通过服务调用,应用程序可以使用 gRPC 或 HTTP 这样的标准协议来发现并可靠地与其他应用程序通信。
在多个需要相互通信的服务环境中(多进程、分布式、微服务),通常面临以下问题:
- 如何发现和调用不同服务上的方法?
- 如何安全地调用其他服务?
- 如何处理重试和瞬态错误?
- 如何使用分布式跟踪调用图来诊断生产中的问题?
Dapr 服务调用具备的能力
Dapr 通过提供服务调用 API 来应对这些问题,这种调用 API 作为反向代理与内置的服务发现相结合, 同时利用内置分布式跟踪、计量、错误处理、加密等功能。
Dapr 采用边车(Sidecar)、去中心化的架构 。 要使用 Dapr 来调用应用程序,请在任意 Dapr 实例上使用 invoke 这个API。 sidecar 编程模型鼓励每个应用程序与自己的 Dapr 实例对话。 Dapr 实例会相互发现并进行通信。
dapr 的 service invoke 同样是 Sidecar 编程模型,具备以下能力:
- 服务发现,on k8s(用于Cloud端部署) 的 DNS(CoreDNS) 和 self-hosted (用于Edge端/独立部署)的 mDNS 模式;
- 失败重试,服务通信访问失败执行相应的访问策略授权,比如应用程序可以控制哪些其他应用程序允许调用或授权可做什么;
- 服务熔断,避免负载过大从而导致响应变慢或失败的情况,使应用程序增强健壮性;
- 通信安全,使用 mTLS 加密服务调用,包括通过 Dapr 哨兵服务来自动证书翻转(certificate rollover);
- 负载均衡,使用 mDNS 协议提供轮询负载均衡的服务调用请求,例如用于本地或多个联网的物理机器;
- 可观测性,内置分布式链路跟踪、计量,以便为应用程序提供洞察和诊断;
可插拔服务发现
Dapr 可以在各种托管平台(云 / Cloud 或 端 / Edge)上运行。为了启用 服务发现 和 服务调用,Dapr 使用可插入的 名称解析组件 。例如,Kubernetes 名称解析组件使用 Kubernetes DNS 服务来解析集群中运行的其他应用程序的位置。自承载计算机可以使用 mDNS 名称解析组件。Consul 名称解析组件可用于任何托管环境,包括 Kubernetes 或自托管环境。
【名称解析组件】
- Self-hosted(自宿主/自承载)模式:
- dapr 内置的 mDNS(运行时/daprd 默认)
- 第三方组件:
- Kubernetes DNS(CoreDNS)
- HashiCorp Consul
注意:在 Kubernetes 版本 1.11 和更高版本中,CoreDNS 是默认的 DNS 服务器。
警告: 从 v1.18 开始,在 kubeadm 中使用 kube-dns 的支持已被废弃,并已在 v1.21 版本中删除。
服务调用负载均衡
Dapr 使用 mDNS 协议为服务调用请求提供 轮循机制的负载平衡,例如,对于单台计算机或多台联网的物理机。
下图显示了其工作原理的示例。如果您有 1 个具有应用程序 ID 的应用程序实例和 3 个具有应用程序 ID 的应用程序实例,并且您从一个应用程序调用到另一个应用程序,则 Dapr 会在 3 个实例(Instance)之间轮循机制。这些实例可以位于同一台计算机上,也可以位于不同的计算机上。FrontEnd Cart 和 FrontEnd Cart
注意:同一应用的 N 个实例具有相同的应用 ID,因为每个应用的应用 ID 是唯一的。您可以拥有该应用程序的多个实例,其中所有这些实例都具有相同的应用程序 ID。
Dapr 服务调用的逻辑
-
Service A 对 Service B 发起 HTTP/gRPC 的调用;
-
Dapr 使用运行在给定托管平台(hosting platform)上的名称解析组件(name resolution component )发现 Service B 的位置;
-
Dapr 将消息转发至 Service B 的 Dapr 边车(Sidecar);
注:Dapr 边车之间的所有调用考虑到性能都优先使用 gRPC,仅服务与 Dapr 边车之间的调用可以是 HTTP 或 gRPC。 -
Service B 的 Dapr 边车将请求转发至 Service B 上的特定端点 (或方法) ,Service B 随后运行其相应的业务逻辑代码;
-
Service B 发送响应给 Service A,响应将转至 Service B 的边车;
-
Service B 的 Dapr 边车将消息转发至 Service A 的 Dapr 边车。
-
Service A 接收响应信息;
任何应用程序都可以通过使用本机 API 来调内置 Dapr 的 Sidecar API。这个 API 可以用 HTTP 或 gRPC 协议调用,使用下面的 URL 来调用 HTTP API:
http://localhost:<dapr-port>/v1.0/invoke/<application-id>/method/<method-name>
- dapr-port : dapr监听的端口。
- application-id:服务要调用的另一个服务的id。
- method-name:服务要调用的另一个服务的方法名称(路由字符串)。
使用 Dapr for dotnet SDK
Dapr .NET SDK 为 .NET 开发者提供了一个易用的、语言相关的开发工具来和Dapr进行集成。SDK为开发者提供了三种远程服务调用的方式:
- Invoke HTTP services using HttpClient
- Invoke HTTP services using DaprClient
- Invoke gRPC services using DaprClient
Dapr 服务调用项目演示(HTTP协议)
上面对 dapr service invoke 做了相应的介绍,接下来我们采用 .net6 (dapr 语言,框架,平台无关,可选择你喜欢的语言/框架演示)分别创建 2 个 WebAPI 项目做演示:
服务(Service) | 框架类型 | 编程语言/平台 | Nuget 包 |
---|---|---|---|
FrontEnd | ASP.NET Core Web API | C# / .NET6 | Dapr.AspNetCore |
BackEnd | ASP.NET Core Web API | C# / .NET6 | \ |
目标:实现 FrontEnd 服务对应 BackEnd 服务的调用(此处我们使用 HTTP 协议演示)。
分别修改服务的 HTTP 端口配置
根据上面的规划创建如下项目结构:
指定 BackEnd 服务的默认启动端口 5000,修改如下配置(applicationUrl):
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:26409",
"sslPort": 0
}
},
"profiles": {
"BackEnd": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
和上面类似,同样修改 FrontEnd 服务默认的启动端口 5001(此处省略 json 配置信息);
BackEnd 服务编写 API 接口
此处使用默认的天气(WeatherForecast)接口,代码如下:
using Microsoft.AspNetCore.Mvc;
using BackEnd.Model;
namespace BackEnd.Controllers
{
[ApiController]
[Route("[controller]/[action]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
_logger.LogInformation("进入 BackEnd.WeatherForecast.Get 方法");
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
}).ToArray();
}
}
}
FrontEnd 服务中添加 Dapr 服务
如果是以前Web API的方式( .net core 3.1.x 或 .net 5.0.x 版本),可以在Startup.cs 文件 ConfigureServices 方法加入一行代码
services.AddControllers().AddDapr();
如果使用 Minimal API (.net 6.x 版本)可以在 Program.cs 文件中的 var builder = WebApplication.CreateBuilder(args); 之后加入一行代码
builder.Services.AddControllers().AddDapr();
FrontEnd 服务添加相应的 Nuget 依赖包
接下来我们为 FrontEnd 服务添加 Nuget 包【Dapr.AspNetCore v1.6.0】
FrontEnd 服务编写 Service invoke 调用 Demo
演示 3 种方式的 http 服务调用:
- 使用 HttpClient 调用 HTTP 服务;
- 使用 DaprClient 调用 HTTP 服务;
- 使用构造函数注入方式(DI)DaprClient 调用 HTTP 服务;
using Dapr.Client;
using FrontEnd.Model;
using Microsoft.AspNetCore.Mvc;
namespace FrontEnd.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class DaprHttpInvokeController : ControllerBase
{
private readonly static string _appId = "backend";
private readonly static string _methodName = "WeatherForecast/Get";
private readonly ILogger<DaprHttpInvokeController> _logger;
private readonly DaprClient _daprClient;
public DaprHttpInvokeController(ILogger<DaprHttpInvokeController> logger, DaprClient daprClient)
{
_logger = logger;
_daprClient = daprClient; // (推荐)构造函数注入 DaprClient
}
[HttpGet]
public async Task<ActionResult> GetAsync()
{
_logger.LogInformation("构建 HttpClient 调用 BackEnd 服务");
using var httpClient = DaprClient.CreateInvokeHttpClient();
var result = await httpClient.GetAsync($"http://{_appId}/{_methodName}");
var resultContent = string.Format("result is {0} {1}", result.StatusCode, await result.Content.ReadAsStringAsync());
return Ok(resultContent);
}
[HttpGet]
public async Task<ActionResult> Get2Async()
{
_logger.LogInformation("构建 DaprClient 调用 BackEnd 服务");
using var daprClient = new DaprClientBuilder().Build();
var result = await daprClient.InvokeMethodAsync<IEnumerable<WeatherForecast>>(HttpMethod.Get, _appId, _methodName);
return Ok(result);
}
[HttpGet]
public async Task<ActionResult> Get3Async()
{
_logger.LogInformation("构造函数 DI 注入 daprClient 调用 BackEnd 服务");
var result = await _daprClient.InvokeMethodAsync<IEnumerable<WeatherForecast>>(HttpMethod.Get, _appId, _methodName);
return Ok(result);
}
}
}
使用 Dapr CLI 启动 app 服务
使用 dapr cli 按顺序先启动 BackEnd 服务,再启动 FrontEnd 服务,命令如下:
# dapr cli 启动 BackEnd 服务,指定 sidecar 端口为 3502(默认为3500),指定 app-port 是 5000,指定 app-id 为 backend,与 BackEnd默认 http 端口保持一致;
dapr run --dapr-http-port 3502 --app-port 5000 --app-id backend dotnet run
# dapr cli 启动 FrontEnd 服务,指定 sidecar 端口为 3501(默认为3500),指定 app-port 是 5001,指定 app-id 为 frontend,与 FrontEnd默认 http 端口保持一致;
dapr run --dapr-http-port 3501 --app-port 5001 --app-id frontend dotnet run
此处演示的服务均在相同的宿主机环境,注意 Sidecar 端口区分,避免冲突;
注:BackEnd 和 FrontEnd 服务如果是发布文件,把后面的 dotnet run 命令修改为指定路径的发布文件 .dll 名称即可,例如 dotnet .\BackEnd\bin\Release\net6.0\BackEnd.dll
- 查看更多 dapr run 用法示例:dapr run -h
Run Dapr and (optionally) your application side by side. Supported platforms: Self-hosted
Usage:
dapr run [flags]
Examples:
# Run a .NET application
dapr run --app-id myapp --app-port 5000 -- dotnet run
# Run a Java application
dapr run --app-id myapp -- java -jar myapp.jar
# Run a NodeJs application that listens to port 3000
dapr run --app-id myapp --app-port 3000 -- node myapp.js
# Run a Python application
dapr run --app-id myapp -- python myapp.py
# Run sidecar only
dapr run --app-id myapp
# Run a gRPC application written in Go (listening on port 3000)
dapr run --app-id myapp --app-port 3000 --app-protocol grpc -- go run main.go
Flags:
-a, --app-id string The id for your application, used for service discovery
--app-max-concurrency int The concurrency level of the application, otherwise is unlimited (default -1)
-p, --app-port int The port your application is listening on (default -1)
-P, --app-protocol string The protocol (gRPC or HTTP) Dapr uses to talk to the application (default "http")
--app-ssl Enable https when Dapr invokes the application
-d, --components-path string The path for components directory (default "C:\\Users\\sws-dev-server\\.dapr\\components")
-c, --config string Dapr configuration file (default "C:\\Users\\sws-dev-server\\.dapr\\config.yaml")
-G, --dapr-grpc-port int The gRPC port for Dapr to listen on (default -1)
--dapr-http-max-request-size int Max size of request body in MB (default -1)
-H, --dapr-http-port int The HTTP port for Dapr to listen on (default -1)
--enable-profiling Enable pprof profiling via an HTTP endpoint
-h, --help Print this help message
--log-level string The log verbosity. Valid values are: debug, info, warn, error, fatal, or panic (default "info")
-M, --metrics-port int The port of metrics on dapr (default -1)
--placement-host-address string The address of the placement service. Format is either <hostname> for default port or <hostname>:<port> for custom port (default "localhost")
--profile-port int The port for the profile server to listen on (default -1)
-u, --unix-domain-socket string Path to a unix domain socket dir. If specified, Dapr API servers will use Unix Domain Sockets
Global Flags:
--log-as-json Log output in JSON format
- dapr run 启动 BackEnd 服务:
Starting Dapr with id backend. HTTP Port: 3502. gRPC Port: 53516
time="2022-03-15T16:04:06.9484626+08:00" level=info msg="starting Dapr Runtime -- version 1.6.0 -- commit 4bb25fab444c4f1a1bf0ffd74293dbd4fdcea580" app_id=backend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:04:06.9484626+08:00" level=info msg="log level set to: info" app_id=backend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:04:06.9484626+08:00" level=info msg="metrics server started on :53517/" app_id=backend instance=sws-it-dev scope=dapr.metrics type=log ver=1.6.0
time="2022-03-15T16:04:06.952488+08:00" level=info msg="standalone mode configured" app_id=backend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:04:06.952488+08:00" level=info msg="app id: backend" app_id=backend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:04:06.952488+08:00" level=info msg="mTLS is disabled. Skipping certificate request and tls validation" app_id=backend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:04:06.9726362+08:00" level=info msg="local service entry announced: backend -> 192.168.10.251:53521" app_id=backend instance=sws-it-dev scope=dapr.contrib type=log ver=1.6.0
time="2022-03-15T16:04:06.9732999+08:00" level=info msg="Initialized name resolution to mdns" app_id=backend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:04:06.9732999+08:00" level=info msg="loading components" app_id=backend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:04:06.9776088+08:00" level=info msg="component loaded. name: pubsub, type: pubsub.redis/v1" app_id=backend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:04:06.9776088+08:00" level=info msg="waiting for all outstanding components to be processed" app_id=backend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:04:06.9808041+08:00" level=info msg="detected actor state store: statestore" app_id=backend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:04:06.9808041+08:00" level=info msg="component loaded. name: statestore, type: state.redis/v1" app_id=backend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:04:06.9808041+08:00" level=info msg="all outstanding components processed" app_id=backend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:04:06.9808041+08:00" level=info msg="enabled gRPC tracing middleware" app_id=backend instance=sws-it-dev scope=dapr.runtime.grpc.api type=log ver=1.6.0
time="2022-03-15T16:04:06.9813394+08:00" level=info msg="enabled gRPC metrics middleware" app_id=backend instance=sws-it-dev scope=dapr.runtime.grpc.api type=log ver=1.6.0
type=log ver=1.6.0
time="2022-03-15T16:04:06.9813394+08:00" level=info msg="enabled metrics http middleware" app_id=backend instance=sws-it-dev scope=dapr.runtime.http type=log ver=1.6.0
time="2022-03-15T16:04:06.9813394+08:00" level=info msg="enabled tracing http middleware" app_id=backend instance=sws-it-dev scope=dapr.runtime.http type=log ver=1.6.0
time="2022-03-15T16:04:06.9818624+08:00" level=info msg="http server is running on port 3502" app_id=backend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:04:06.9818624+08:00" level=info msg="The request body size parameter is: 4" app_id=backend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:04:06.9818624+08:00" level=info msg="enabled gRPC tracing middleware" app_id=backend instance=sws-it-dev scope=dapr.runtime.grpc.internal type=log ver=1.6.0
time="2022-03-15T16:04:06.9824163+08:00" level=info msg="enabled gRPC metrics middleware" app_id=backend instance=sws-it-dev scope=dapr.runtime.grpc.internal type=log ver=1.6.0
time="2022-03-15T16:04:06.9824163+08:00" level=info msg="internal gRPC server is running on port 53521" app_id=backend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:04:06.9824163+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=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
== APP == ��������...
Updating metadata for app command: dotnet run
You're up and running! Both Dapr and your app logs will appear here.
== APP == info: Microsoft.Hosting.Lifetime[14]
== 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:\Users\sws-dev-server\Desktop\dapr-demo\dapr-demo-all\BackEnd\
time="2022-03-15T16:04:08.6315432+08:00" level=info msg="application discovered on port 5000" app_id=backend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:04:08.8113386+08:00" level=info msg="application configuration loaded" app_id=backend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:04:08.8118274+08:00" level=info msg="actor runtime started. actor idle timeout: 1h0m0s. actor scan interval: 30s" app_id=backend instance=sws-it-dev scope=dapr.runtime.actor type=log ver=1.6.0
time="2022-03-15T16:04:08.8155091+08:00" level=info msg="dapr initialized. Status: Running. Init Elapsed 1863.0211000000002ms" app_id=backend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:04:08.9183574+08:00" level=info msg="placement tables updated, version: 0" app_id=backend instance=sws-it-dev scope=dapr.runtime.actor.internal.placement type=log ver=1.6.0
- dapr run 启动 FrontEnd 服务
Starting Dapr with id frontend. HTTP Port: 3501. gRPC Port: 53577
time="2022-03-15T16:07:29.6081037+08:00" level=info msg="starting Dapr Runtime -- version 1.6.0 -- commit 4bb25fab444c4f1a1bf0ffd74293dbd4fdcea580" app_id=frontend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:07:29.608619+08:00" level=info msg="log level set to: info" app_id=frontend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:07:29.608619+08:00" level=info msg="metrics server started on :53578/" app_id=frontend instance=sws-it-dev scope=dapr.metrics type=log ver=1.6.0
time="2022-03-15T16:07:29.6112893+08:00" level=info msg="standalone mode configured" app_id=frontend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:07:29.6112893+08:00" level=info msg="app id: frontend" app_id=frontend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:07:29.6122979+08:00" level=info msg="mTLS is disabled. Skipping certificate request and tls validation" app_id=frontend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:07:29.6311265+08:00" level=info msg="local service entry announced: frontend -> 192.168.10.251:53582" app_id=frontend instance=sws-it-dev scope=dapr.contrib type=log ver=1.6.0
time="2022-03-15T16:07:29.6321288+08:00" level=info msg="Initialized name resolution to mdns" app_id=frontend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:07:29.6321288+08:00" level=info msg="loading components" app_id=frontend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:07:29.6353047+08:00" level=info msg="component loaded. name: pubsub, type: pubsub.redis/v1" app_id=frontend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:07:29.6353047+08:00" level=info msg="waiting for all outstanding components to be processed" app_id=frontend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:07:29.6379374+08:00" level=info msg="detected actor state store: statestore" app_id=frontend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:07:29.6379374+08:00" level=info msg="component loaded. name: statestore, type: state.redis/v1" app_id=frontend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:07:29.6379374+08:00" level=info msg="all outstanding components processed" app_id=frontend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:07:29.6384667+08:00" level=info msg="enabled gRPC tracing middleware" app_id=frontend instance=sws-it-dev scope=dapr.runtime.grpc.api type=log ver=1.6.0
time="2022-03-15T16:07:29.6384667+08:00" level=info msg="enabled gRPC metrics middleware" app_id=frontend instance=sws-it-dev scope=dapr.runtime.grpc.api type=log ver=1.6.0
time="2022-03-15T16:07:29.6384667+08:00" level=info msg="API gRPC server is running on port 53577" app_id=frontend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:07:29.6389943+08:00" level=info msg="enabled metrics http middleware" app_id=frontend instance=sws-it-dev scope=dapr.runtime.http type=log ver=1.6.0
time="2022-03-15T16:07:29.6389943+08:00" level=info msg="enabled tracing http middleware" app_id=frontend instance=sws-it-dev scope=dapr.runtime.http type=log ver=1.6.0
time="2022-03-15T16:07:29.6389943+08:00" level=info msg="http server is running on port 3501" app_id=frontend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:07:29.6389943+08:00" level=info msg="The request body size parameter is: 4" app_id=frontend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:07:29.6395291+08:00" level=info msg="enabled gRPC tracing middleware" app_id=frontend instance=sws-it-dev scope=dapr.runtime.grpc.internal type=log ver=1.6.0
time="2022-03-15T16:07:29.6395291+08:00" level=info msg="enabled gRPC metrics middleware" app_id=frontend instance=sws-it-dev scope=dapr.runtime.grpc.internal type=log ver=1.6.0
time="2022-03-15T16:07:29.6395291+08:00" level=info msg="internal gRPC server is running on port 53582" app_id=frontend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:07:29.6395291+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=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
== APP == ��������...
Updating metadata for app command: dotnet run
You're up and running! Both Dapr and your app logs will appear here.
== APP == info: Microsoft.Hosting.Lifetime[14]
== APP == Now listening on: http://localhost: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: Development
== APP == info: Microsoft.Hosting.Lifetime[0]
== APP == Content root path: C:\Users\sws-dev-server\Desktop\dapr-demo\dapr-demo-all\FrontEnd\
time="2022-03-15T16:07:31.3547333+08:00" level=info msg="application discovered on port 5001" app_id=frontend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:07:31.5798394+08:00" level=info msg="application configuration loaded" app_id=frontend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:07:31.5802044+08:00" level=info msg="actor runtime started. actor idle timeout: 1h0m0s. actor scan interval: 30s" app_id=frontend instance=sws-it-dev scope=dapr.runtime.actor type=log ver=1.6.0
time="2022-03-15T16:07:31.5839825+08:00" level=info msg="dapr initialized. Status: Running. Init Elapsed 1972.6932000000002ms" app_id=frontend instance=sws-it-dev scope=dapr.runtime type=log ver=1.6.0
time="2022-03-15T16:07:31.6965976+08:00" level=info msg="placement tables updated, version: 0" app_id=frontend instance=sws-it-dev scope=dapr.runtime.actor.internal.placement type=log ver=1.6.0
使用 Dapr CLI 查看运行中的 app 服务
dapr list
输出信息:
APP ID HTTP PORT GRPC PORT APP PORT COMMAND AGE CREATED PID
backend 3502 53516 5000 dotnet run 6h 2022-03-15 16:04.06 13268
frontend 3501 55148 5001 dotnet run 5h 2022-03-15 17:26.04 6504
分别查看 BackEnd 服务和 FrontEnd 服务的 Swagger 页面,显示如下:
- BackEnd 服务 => http://localhost:5000/swagger/index.html
- FrontEnd 服务 => http://localhost:5001/swagger/index.html
测试服务的可访问性
先测试下 BackEnd 服务的 API 接口是否可正常访问,访问测试如下:
- 使用 curl 访问测试:
curl -X 'GET' \
'http://localhost:5000/WeatherForecast/Get' \
-H 'accept: text/plain'
- 使用 Swagger 页面或 ApiPost 工具访问测试:
测试结果 BackEnd 服务的 API 接口可正常访问,接下在 FrontEnd 服务的 Swagger 页面测试调用 BackEnd 服务的 API 接口,测试如下:
- 使用 curl 访问测试:
# 1.构建 HttpClient 调用 BackEnd 服务
curl -X 'GET' \
'http://localhost:5001/api/DaprHttpInvoke/Get' \
-H 'accept: */*'
# 2.构建 DaprClient 调用 BackEnd 服务
curl -X 'GET' \
'http://localhost:5001/api/DaprHttpInvoke/Get2' \
-H 'accept: */*'
# 3.构造函数 DI 注入 daprClient 调用 BackEnd 服务
curl -X 'GET' \
'http://localhost:5001/api/DaprHttpInvoke/Get3' \
-H 'accept: */*'
- 使用 Swagger 页面访问测试:
- Dapr CLI 测试 API 访问性
调用 BackEnd 服务的 【http://localhost:5000/WeatherForecast/Get】接口:
dapr invoke --app-id backend --verb "GET" --method WeatherForecast/Get
输出信息:
[{"date":"2022-03-17T11:46:04.3251768+08:00","temperatureC":24,"temperatureF":75,"summary":"Chilly"},{"date":"2022-03-18T11:46:04.3251995+08:00","temperatureC":6,"temperatureF":42,"summary":"Hot"},{"date":"2022-03-19T11:46:04.3251998+08:00","temperatureC":-3,"temperatureF":27,"summary":"Warm"},{"date":"2022-03-20T11:46:04.3252+08:00","temperatureC":51,"temperatureF":123,"summary":"Scorching"},{"date":"2022-03-21T11:46:04.3252001+08:00","temperatureC":37,"temperatureF":98,"summary":"Freezing"}]
App invoked successfully
调用 FrontEnd 服务的【http://localhost:5001/api/DaprHttpInvoke/Get4】接口:
dapr invoke --app-id frontend --verb "GET" --method api/DaprHttpInvoke/Get4
输出信息:
[{"date":"2022-03-17T11:47:12.5673945+08:00","temperatureC":-17,"temperatureF":2,"summary":"Sweltering"},{"date":"2022-03-18T11:47:12.5674017+08:00","temperatureC":-2,"temperatureF":29,"summary":"Bracing"},{"date":"2022-03-19T11:47:12.567402+08:00","temperatureC":38,"temperatureF":100,"summary":"Bracing"},{"date":"2022-03-20T11:47:12.5674021+08:00","temperatureC":38,"temperatureF":100,"summary":"Balmy"},{"date":"2022-03-21T11:47:12.5674023+08:00","temperatureC":-11,"temperatureF":13,"summary":"Sweltering"}]
App invoked successfully
其他另外两个服务调用类似,经测试均可正常调用,此处就不在截图说明。
总结
在需要多服务链路调用的环境中,Dapr 服务调用构建块通过使用 Dapr sidecar 作为服务的反向代理来解决这些相应的挑战,分布式环境中服务调用面临的 “挑战” 全部下沉到 Dapr Sidecar 构建块(building block),从而让开发人员更加专注于项目的业务代码环境;
- Dapr .NET SDK 提供了多种调用远程方法的途径,对于 HttpClient 的支持能够让开发者重用现存代码并能够与其他现存的框架进行集成(比如:Refit、RestSharp、Polly)。
- DaprClient 支持使用 HTTP 或 gRPC 语义直接使用 Dapr 的服务调用 API。
- HttpClient 只支持使用 HTTP 协议来使用 Dapr 的服务调用 API。
- Dapr Sidecar 模式使 基础设施模块 与 业务代码 分离,与目标服务分离,但各自是独立的进程(daprd.exe)运行,整体形成统一的原子体对外提供服务。
参考文档
- 【服务调用概述】=> https://docs.dapr.io/zh-hans/developing-applications/building-blocks/service-invocation/service-invocation-overview/