Dapr for dotnet | 服务调用-Service invoke(HTTP协议)

Service Invoke | 服务调用是什么?

服务调用是 dapr 的 building blocks 的其中一部分,用于执行直接而安全的服务间方法调用。
dapr building blocks
通过服务调用,应用程序可以使用 gRPCHTTP 这样的标准协议来发现并可靠地与其他应用程序通信。

在多个需要相互通信的服务环境中(多进程、分布式、微服务),通常面临以下问题:

  • 如何发现和调用不同服务上的方法?
  • 如何安全地调用其他服务?
  • 如何处理重试和瞬态错误?
  • 如何使用分布式跟踪调用图来诊断生产中的问题?

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(自宿主/自承载)模式:
  1. dapr 内置的 mDNS(运行时/daprd 默认)
  • 第三方组件:
  1. Kubernetes DNS(CoreDNS)
  2. 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 服务调用的逻辑

dapr 服务调用逻辑

  1. Service A 对 Service B 发起 HTTP/gRPC 的调用;

  2. Dapr 使用运行在给定托管平台(hosting platform)上的名称解析组件(name resolution component )发现 Service B 的位置;

  3. Dapr 将消息转发至 Service B 的 Dapr 边车(Sidecar);
    注:Dapr 边车之间的所有调用考虑到性能都优先使用 gRPC,仅服务与 Dapr 边车之间的调用可以是 HTTP 或 gRPC。

  4. Service B 的 Dapr 边车将请求转发至 Service B 上的特定端点 (或方法) ,Service B 随后运行其相应的业务逻辑代码;

  5. Service B 发送响应给 Service A,响应将转至 Service B 的边车;

  6. Service B 的 Dapr 边车将消息转发至 Service A 的 Dapr 边车。

  7. 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为开发者提供了三种远程服务调用的方式:

  1. Invoke HTTP services using HttpClient
  2. Invoke HTTP services using DaprClient
  3. Invoke gRPC services using DaprClient

Dapr 服务调用项目演示(HTTP协议)

上面对 dapr service invoke 做了相应的介绍,接下来我们采用 .net6 (dapr 语言,框架,平台无关,可选择你喜欢的语言/框架演示)分别创建 2 个 WebAPI 项目做演示:

服务(Service)框架类型编程语言/平台Nuget 包
FrontEndASP.NET Core Web APIC# / .NET6Dapr.AspNetCore
BackEndASP.NET Core Web APIC# / .NET6\

目标:实现 FrontEnd 服务对应 BackEnd 服务的调用(此处我们使用 HTTP 协议演示)。

分别修改服务的 HTTP 端口配置

根据上面的规划创建如下项目结构:
dapr 服务调用项目结构
指定 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】
Dapr.AspNetCore

FrontEnd 服务编写 Service invoke 调用 Demo

演示 3 种方式的 http 服务调用:

  1. 使用 HttpClient 调用 HTTP 服务;
  2. 使用 DaprClient 调用 HTTP 服务;
  3. 使用构造函数注入方式(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
    服务swagger

测试服务的可访问性

先测试下 BackEnd 服务的 API 接口是否可正常访问,访问测试如下:

  • 使用 curl 访问测试:
curl -X 'GET' \
  'http://localhost:5000/WeatherForecast/Get' \
  -H 'accept: text/plain'
  • 使用 Swagger 页面或 ApiPost 工具访问测试:

backend api 测试
测试结果 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 页面访问测试:

DaprHttpInvoke/Get

  • 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 的支持能够让开发者重用现存代码并能够与其他现存的框架进行集成(比如:RefitRestSharpPolly)。
  • 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/
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ChaITSimpleLove

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值