面向.NET开发人员的Dapr——服务调用

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向.NET开发人员的Dapr——服务调用相关的知识,希望对你有一定的参考价值。

目录:

The Dapr service invocation building block

Dapr 服务调用构建块

Across a distributed system, one service often needs to communicate with another to complete a business operation. The Dapr service invocation building block can help streamline the communication between services.

在分布式系统中,一项服务通常需要与其他服务进行通信才能完成业务操作。 Dapr 服务调用构建块可帮助简化服务之间的通信。

What it solves

解决方法

Making calls between services in a distributed application may appear easy, but there are many challenges involved. For example:

  • Where the other services are located.

  • How to call a service securely, given the service address.

  • How to handle retries when short-lived transient errors occur.

在分布式应用程序中的服务之间进行调用可能会很简单,但会涉及到许多挑战。例如:

  • 其他服务的位置。

  • 在给定服务地址的情况下,如何安全地调用服务。

  • 在发生短暂的 暂时性错误 时如何处理重试。

Lastly, as distributed applications compose many different services, capturing insights across service call graphs are critical to diagnosing production issues.

最后,分布式应用程序由许多不同的服务构成,跨服务调用图洞察力对于诊断生产问题至关重要。

The service invocation building block addresses these challenges by using a Dapr sidecar as a reverse proxy for your service.

通过 Dapr 边车作为服务的 反向代理 ,使用服务调用构建块来解决这些难题。

How it works

工作原理

Let's start with an example. Consider two services, "Service A" and "Service B". Service A needs to call the catalog/items API on Service B. While Service A could take a dependency on Service B and make a direct call to it, Service A instead invokes the service invocation API on the Dapr sidecar. Figure 6-1 shows the operation.

让我们从一个示例开始。假设有两个服务:"服务 A" 和 "服务 B"。服务 A 需要在服务 B 上调用  catalog/items API。虽然服务 A 可能会依赖于服务 B 并直接对其进行调用,但此处服务 A 在 Dapr连车上调用服务调用 API。图6-1 显示了该操作。

Figure 6-1. How Dapr service invocation works.

图 6-1. Dapr 服务调用的工作方式。

Note the steps from the previous figure:

请注意上图中的步骤:

  1. Service A makes a call to the catalog/items endpoint in Service B by invoking the service invocation API on the Service A sidecar.      服务 A 通过调用其边车上的服务调用 API,来发出对服务B 终结点(catalog/items )的请求。

    Note      备注

    The sidecar uses a pluggable name resolution mechanism to resolve the address of Service B. In self-hosted mode, Dapr uses mDNS to find it. When running in Kubernetes mode, the Kubernetes DNS service determines the address.      边车使用可插接式名称解析机制来解析服务 B 的地址。在自承载模式下,Dapr 使用 mdn 来确定地址。在 Kubernetes 模式下运行时,Kubernetes DNS 服务将确定地址。

  2. The Service A sidecar forwards the request to the Service B sidecar.      服务 A 的边车将请求转发到服务 B 的边车。

  3. The Service B sidecar makes the actual catalog/items request against the Service B API.      服务 B 的边车对 服务 b 的终结点 catalog/items  发出实际请求。

  4. Service B executes the request and returns a response back to its sidecar.      服务 B 执行请求,并将响应返回给其边车。

  5. The Service B sidecar forwards the response back to the Service A sidecar.      服务 B 的边车将响应转发回服务A的边车。

  6. The Service A sidecar returns the response back to Service A.      服务 A 的连车将响应返回给服务 A。

Because the calls flow through sidecars, Dapr can inject some useful cross-cutting behaviors:

  • Automatically retry calls upon failure.

  • Make calls between services secure with mutual (mTLS) authentication, including automatic certificate rollover.

  • Control what operations clients can do using access control policies.

  • Capture traces and metrics for all calls between services to provide insights and diagnostics.

由于调用流过边车,Dapr 可以注入一些有用的横切(AOP思想)行为:

  • 失败时自动重试调用。

  • 服务器之间调用时使用相互 (mTLS) 身份验证(包括自动证书滚动更新)。

  • 使用访问控制策略控制客户端可以执行的操作。

  • 捕获服务间所有调用的跟踪和指标,以提供洞察和诊断。

Any application can invoke a Dapr sidecar by using the native invoke API built into Dapr. The API can be called with either HTTP or gRPC. Use the following URL to call the HTTP API:

任何应用程序都可以通过使用 Dapr 中内置的本机 调用 API 来调用 Dapr 边车。可以通过 HTTP 或 gRPC 调用 API。使用以下 URL 调用 HTTP API:

http://localhost:<dapr-port>/v1.0/invoke/<application-id>/method/<method-name>
  • <dapr-port> the HTTP port that Dapr is listening on.      Dapr 正在侦听的 HTTP 端口。

  • <application-id> application ID of the service to call.      要调用服务的应用程序 ID。

  • <method-name> name of the method to invoke on the remote service.      要在远程服务上调用的方法的名称。

In the following example, a curl call is made to the catalog/items 'GET' endpoint of Service B:

在下面的示例中, 对 Service B 的终结点 catalog/items 发出了一个"GET" 调用:

curl http://localhost:3500/v1.0/invoke/serviceb/method/catalog/items

Note

备注

The Dapr APIs enable any application stack that supports HTTP or gRPC to use Dapr building blocks. Therefore, the service invocation building block can act as a bridge between protocols. Services can communicate with each other using HTTP, gRPC or a combination of both.

Dapr Api允许支持 HTTP 或 gRPC 的任何应用程序堆栈以使用 Dapr 构建基块。因此,服务调用构建块可充当协议之间的桥梁。服务可以使用 HTTP、gRPC 或两者的组合互相通信。

In the next p, you'll learn how to use the .NET SDK to simplify service invocation calls.

在下一节中,你将了解如何使用 .NET SDK 来简化服务调用。

Use the Dapr .NET SDK

使用 Dapr .NET SDK

The Dapr .NET SDK provides .NET developers with an intuitive and language-specific way to interact with Dapr. The SDK offers developers three ways of making remote service invocation calls:

  1. Invoke HTTP services using HttpClient

  2. Invoke HTTP services using DaprClient

  3. Invoke gRPC services using DaprClient

Dapr .NET SDK 为 .net 开发人员提供了直观的、特定于语言的方式来与 Dapr 交互。SDK 为开发人员提供了三种建立远程服务调用的方式:

  1. 使用 HttpClient 调用 HTTP 服务

  2. 使用 DaprClient 调用 HTTP 服务

  3. 使用 DaprClient 调用 gRPC 服务

Invoke HTTP services using HttpClient

使用 HttpClient 调用 HTTP 服务

The preferred way to call an HTTP endpoint is to use Dapr's rich integration with HttpClient. The following example submits an order by calling the submit method of the orderservice application:

调用 HTTP 终结点的首选方法是使用 Dapr 与 HttpClient 的富集成 。下面的示例通过调用订单服务的 submit 方法来提交订单 :

var httpClient = DaprClient.CreateHttpClient();
await httpClient.PostAsJsonAsync("http://orderservice/submit", order);

In the example, DaprClient.CreateHttpClient returns an HttpClient instance that is used to perform Dapr service invocation. The returned HttpClient uses a special Dapr message handler that rewrites URIs of outgoing requests. The host name is interpreted as the application ID of the service to call. The rewritten request that's actually being called is:

在此示例中, DaprClient.CreateHttpClient 返回 HttpClient的实例, 用于执行 Dapr 服务调用。返回的 HttpClient 使用特殊的 Dapr 消息处理程序,该处理程序会重写传出请求的 uri。"http://orderservice/submit"中的主机名被解释为要调用的服务的应用程序 ID。实际调用的重写请求为:

http://127.0.0.1:3500/v1/invoke/orderservice/method/submit

This example uses the default value for the Dapr HTTP endpoint, which is http://127.0.0.1:<dapr-http-port>/. The value of dapr-http-port is taken from the DAPR_HTTP_PORT environment variable. If it's not set, the default port number 3500 is used.

此示例使用 Dapr HTTP 终结点的默认值,即 http://127.0.0.1:<dapr-http-port>/ 。 dapr-http-port 的值取自环境变量 DAPR_HTTP_PORT 。如果未设置,则使用默认端口号 3500 。

Alternatively, you can configure a custom endpoint in the call to DaprClient.CreateHttpClient:

或者,你可以在调用中配置自定义终结点 DaprClient.CreateHttpClient :

var httpClient = DaprClient.CreateHttpClient(daprEndpoint = "localhost:4000");

You can also directly set the base address by specifying the application ID. This makes it possible to use relative URIs when making a call:

还可以通过指定应用程序 ID 来直接设置基地址。这样就可以在进行调用方法时使用相对 Uri:

var httpClient = DaprClient.CreateHttpClient("orderservice");
await httpClient.PostAsJsonAsync("/submit");

The HttpClient object is intended to be long-lived. A single HttpClient instance can be reused for the lifetime of the application. The next scenario demonstrates how an OrderServiceClient class reuses a Dapr HttpClient instance:

HttpClient对象旨在长时间存在。可以在应用程序的生存期内重复使用单个 HttpClient 实例。下一个方案演示了 OrderServiceClient 类如何重用 Dapr HttpClient 实例:

public void ConfigureServices(IServiceCollection services)
{
    // ...
    services.AddSingleton<IOrderServiceClient, OrderServiceClient>(
        _ => new OrderServiceClient(DaprClient.CreateInvokeHttpClient("orderservice")));
}

In the snippet above, the OrderServiceClient is registered as a singleton with the ASP.NET Core dependency injection system. An implementation factory creates a new HttpClient instance by calling DaprClient.CreateInvokeHttpClient. It then uses the newly created HttpClient to instantiate the OrderServiceClient object. By registering the OrderServiceClient as a singleton, it will be reused for the lifetime of the application.

在上面的代码片段中,使用 ASP.NET Core 依赖关系注入系统将 OrderServiceClient 注册为单一实例。服务工厂通过调用 DaprClient.CreateInvokeHttpClient 创建一个新的 HttpClient 实例。然后,它使用新创建的 HttpClient 来实例化 OrderServiceClient 对象。通过将OrderServiceClient 注册 为单一实例,它将在应用程序的生存期内重复使用。

The OrderServiceClient itself has no Dapr-specific code. Even though Dapr service invocation is used under the hood, you can treat the Dapr HttpClient like any other HttpClient:

OrderServiceClient本身没有特定于 Dapr 的代码。即使在后台使用 Dapr 服务调用,你也可以像处理任何其他 HttpClient 一样处理 Dapr HttpClient:

public class OrderServiceClient : IOrderServiceClient
{
    private readonly HttpClient _httpClient;

    public OrderServiceClient(HttpClient httpClient)
    {
        _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
    }

    public async Task SubmitOrder(Order order)
    {
        var response = await _httpClient.PostAsJsonAsync("submit", order);
        response.EnsureSuccessStatusCode();
    }
}

Using the HttpClient class with Dapr service invocation has many benefits:

  • HttpClient is a well-known class that many developers already use in their code. Using HttpClient for Dapr service invocation allows developers to reuse their existing skills.

  • HttpClient supports advanced scenarios, such as custom headers, and full control over request and response messages.

  • In .NET 5, HttpClient supports automatic serialization and deserialization using System.Text.Json.

  • HttpClient integrates with many existing frameworks and libraries, such as Refit, RestSharp, and Polly.

通过HttpClient 类使用 Dapr 服务调用有很多好处:

  • HttpClient 是许多开发人员已在其代码中使用的众所周知的类。通过 HttpClient 使用 Dapr 服务调用,开发人员可重复使用其现有技能。

  • HttpClient 支持高级方案,如自定义标头,以及对请求和响应消息的完全控制。

  • 在 .NET 5 中,HttpClient 使用System.Text.Js 支持 自动序列化和反序列化。

  • HttpClient 集成了许多现有框架和库 ,如Refit、 RestSharp和 Polly。

Invoke HTTP services using DaprClient

使用 DaprClient 调用 HTTP 服务

While HttpClient is the preferred way to invoke services using HTTP semantics, you can also use the DaprClient.InvokeMethodAsync family of methods. The following example submits an order by calling the submit method of the orderservice application:

尽管 通过HttpClient 使用 HTTP 语义调用服务是首选方法,但也可以使用 DaprClient.InvokeMethodAsync 方法族。下面的示例通过调用 orderservice 应用的submit 方法来提交订单:

var daprClient = new DaprClientBuilder().Build();
try
{
    var confirmation =
        await daprClient.InvokeMethodAsync<Order, OrderConfirmation>(
            "orderservice", "submit", order);
}
catch (InvocationException ex)
{
    // Handle error
}

The third argument, an order object, is serialized internally (with System.Text.JsonSerializer) and sent as the request payload. The .NET SDK takes care of the call to the sidecar. It also deserializes the response to an OrderConfirmation object. Because no HTTP method is specified, the request is executed as an HTTP POST.

第三个参数( order 对象)在内部序列化 (使用 System.Text.JsonSerializer) 并作为请求负载发送。.NET SDK 负责调用边车。它还会反序列化响应为 OrderConfirmation对象 。由于未指定 HTTP 方法,因此将以 HTTP POST 的形式执行请求。

The next example demonstrates how you can make an HTTP GET request by specifying the HttpMethod:

下一个示例演示如何通过指定http谓词来发出 HTTP GET 请求:

var catalogItems = await daprClient.InvokeMethodAsync<IEnumerable<CatalogItem>>(HttpMethod.Get, "catalogservice", "items");

For some scenarios, you may require more control over the request message. For example, when you need to specify request headers, or you want to use a custom serializer for the payload. DaprClient.CreateInvokeMethodRequest creates an HttpRequestMessage. The following example demonstrates how to add an HTTP authorization header to a request message:

在某些情况下,可能需要对请求消息进行更多的控制。例如,当你需要指定请求标头,或你想要有对负载使用自定义序列化时。 DaprClient.CreateInvokeMethodRequest 创建一个 HttpRequestMessage 。下面的示例演示如何在请求消息中增加 HTTP 授权标头:

var request = daprClient.CreateInvokeMethodRequest("orderservice", "submit", order);
request.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);

The HttpRequestMessage now has the following properties set:

  • Url = http://127.0.0.1:3500/v1.0/invoke/orderservice/method/submit

  • HttpMethod = POST

  • Content = JsonContent object containing the JSON-serialized order

  • Headers.Authorization = "bearer <token>"

HttpRequestMessage现在具有以下属性集:

  • Url = http://127.0.0.1:3500/v1.0/invoke/orderservice/method/submit

  • HttpMethod = POST

  • Content = JsonContent 包含 order对象的json字符串

  • 授权标头= "bearer <token> "

Once you've got the request set up the way you want, use DaprClient.InvokeMethodAsync to send it:

按所需方式设置请求后,请使用 DaprClient.InvokeMethodAsync 发送此请求:

var orderConfirmation = await daprClient.InvokeMethodAsync<OrderConfirmation>(request);

DaprClient.InvokeMethodAsync deserializes the response to an OrderConfirmation object if the request is successful. Alternatively, you can use DaprClient.InvokeMethodWithResponseAsync to get full access to the underlying HttpResponseMessage:

如果请求成功,DaprClient.InvokeMethodAsync 会反序列化响应 为OrderConfirmation 对象。或者,您可以使用 DaprClient.InvokeMethodWithResponseAsync 来获取对HttpResponseMessage 底层的完全访问权限 :

var response = await daprClient.InvokeMethodWithResponseAsync(request);
response.EnsureSuccessStatusCode();

var orderConfirmation = response.Content.ReadFromJsonAsync<OrderConfirmation>();

Note

For service invocation calls using HTTP, it's worth considering using the Dapr HttpClient integration presented in the previous p. Using HttpClient gives you additional benefits such as integration with existing frameworks and libraries.

备注

对于使用 HTTP 的服务调用,有必要考虑使用上一节中介绍的 Dapr HttpClient 集成。使用 HttpClient 为你提供了更多好处,例如与现有框架和库集成。

Invoke gRPC services using DaprClient

使用 DaprClient 调用 gRPC 服务

DaprClient provides a family of InvokeMethodGrpcAsync methods for calling gRPC endpoints. The main difference with the HTTP methods is the use of a Protobuf serializer instead of JSON. The following example invokes the submitOrder method of the orderservice over gRPC.

DaprClient 提供了一系列 InvokeMethodGrpcAsync 方法来调用 gRPC 终结点。与 HTTP 方法的主要区别是使用 Protobuf 序列化,而不是 JSON。下面的示例使用gRPC 调用订单服务(orderservice)的 submitOrder 方法 。

var daprClient = new DaprClientBuilder().Build();
try
{
    var confirmation = await daprClient.InvokeMethodGrpcAsync<Order, OrderConfirmation>("orderservice", "submitOrder", order);
}
catch (InvocationException ex)
{
    // Handle error
}

In the example above, DaprClient serializes the given order object using Protobuf and uses the result as the gRPC request body. Likewise, the response body is Protobuf deserialized and returned to the caller. Protobuf typically provides better performance than the JSON payloads used in HTTP service invocation.

在上面的示例中,DaprClient 使用 Protobuf 序列化给定order 对象,并使用序列化结果作为 gRPC 请求正文。同样,响应正文使用 Protobuf 反序列化并返回给调用方。与 HTTP 服务调用中使用的 JSON 负载相比,Protobuf 通常提供更好的性能。

Reference application: eShopOnDapr

参考应用:eShopOnDapr

The original eShopOnContainers microservice reference architecture from Microsoft used a mix of HTTP/REST and gRPC services. The use of gRPC was limited to communication between an aggregator service and core back-end services. Figure 6-2 show the architecture:

Microsoft 的原始 eShopOnContainers 微服务参考架构混合使用了 HTTP/REST 和 gRPC 服务。gRPC的使用 仅限于 聚合器服务 与核心后端服务之间的通信。图6-2 显示了该架构:

Figure 6-2. gRPC and HTTP/REST calls in eShopOnContainers.

图 6-2. eShopOnContainers 中的 gRPC 和 HTTP/REST 调用。

Note the steps from the previous figure:

  1. The front end calls the API gateway using HTTP/REST.

  2. The API gateway forwards simple CRUD (Create, Read, Update, Delete) requests directly to a core back-end service using HTTP/REST.

  3. The API gateway forwards complex requests that involve coordinated calls to multiple back-end services to the web shopping aggregator service.

  4. The aggregator service uses gRPC to call core back-end services.

请注意上图中的步骤:

  1. 前端使用 HTTP/REST 调用 API 网关 。

  2. API 网关直接转发简单的 CRUD (使用 HTTP/REST 将) 请求到核心后端服务。

  3. API 网关将涉及多个后端服务的协调调用的复杂请求转发到 web 购物聚合器服务。

  4. 聚合器服务使用 gRPC 来调用核心后端服务。

In the recently updated eShopOnDapr implementation, Dapr sidecars are added to the services and API gateway. Figure 6-3 show the updated architecture:

在最近更新的 eShopOnDapr 实现中,Dapr 边车被添加到各个服务和 API 网关。图6-3 显示了更新后的架构:

Figure 6-3. Updated eShop architecture using Dapr.

图 6-3。使用 Dapr 的 eShop 架构。

Note the updated steps from the previous figure:

  1. The front end still uses HTTP/REST to call the API gateway.

  2. The API gateway forwards HTTP requests to its Dapr sidecar.

  3. The API gateway sidecar sends the request to the sidecar of the aggregator or back-end service.

  4. The aggregator service uses the Dapr .NET SDK to call back-end services through their sidecar architecture.

请注意上图中更新了的步骤:

  1. 前端仍使用 HTTP/REST 调用 API 网关。

  2. API 网关将 HTTP 请求转发到其 Dapr 边车。

  3. API 网关边车将请求发送到聚合器或后端服务的边车。

  4. 聚合器服务使用 Dapr .NET SDK 通过其边车调用后端服务。

Dapr implements calls between sidecars with gRPC. So even if you're invoking a remote service with HTTP/REST semantics, a part of the transport is still implemented using gRPC.

Dapr 使用 gRPC 实现连车之间的调用。因此,即使你使用 HTTP/REST 语义调用远程服务,也仍然会在微服务间使用 gRPC 来交互。

The eShopOnDapr reference application benefits from the Dapr service invocation building block. The benefits include service discovery, automatic mTLS, and observability.

Dapr 服务调用构建块为eSheopOnDapr参考应用带来的优点包括服务发现、自动 mTLS 和可观察性。

Forward HTTP requests using Envoy and Dapr

使用 Envoy 和 Dapr 转发 HTTP 请求

Both the original and updated eShop application leverage the Envoy proxy as an API gateway. Envoy is an open-source proxy and communication bus that is popular across modern distributed applications. Originating from Lyft, Envoy is owned and maintained by the Cloud-Native Computing Foundation.

eShopOnContainers和eShopOnDapr都将 Envoy Proxy 用作 API 网关。Envoy 是一个 开放源代码和跨现代分布式应用程序常用的通信总线。源自 Lyft,Envoy 由 云原生计算基础拥有和维护。

In the original eShopOnContainers implementation, the Envoy API gateway forwarded incoming HTTP requests directly to aggregator or back-end services. In the new eShopOnDapr, the Envoy proxy forwards the request to a Dapr sidecar. The sidecar provides service invocation, mTLS, and observability.

在原始 eShopOnContainers 实现中,Envoy API 网关会将传入的 HTTP 请求直接转发到聚合器或后端服务。在 新的eShopOnDapr 中,Envoy 代理将请求转发到 Dapr 边车。边车提供服务调用、mTLS 和可观察性。

Envoy is configured using a YAML definition file to control the proxy's behavior. To enable Envoy to forward HTTP requests to a Dapr sidecar container, a dapr cluster is added to the configuration. The cluster configuration contains a host that points to the HTTP port on which the Dapr sidecar is listening:

使用 YAML 定义文件对 Envoy 进行配置,以控制代理的行为。为了使 Envoy 能够将 HTTP 请求转发到 Dapr 边车容器,会将一个 dapr 集群添加到配置中。集群配置包含一个主机,该主机指向 Dapr 边车正在侦听的 HTTP 端口:

clusters:
- name: dapr
  connect_timeout: 0.25s
  type: strict_dns
  hosts:
  - socket_address:
    address: 127.0.0.1
    port_value: 3500

The Envoy routes configuration is updated to rewrite incoming requests as calls to the Dapr sidecar (pay close attention to the prefix_rewrite key/value pair):

更新 Envoy 路由配置,以将传入请求重写为对 Dapr 边车的调用, (请注意 prefix_rewrite 键/值对) :

- name: "c-short"
  match:
    prefix: "/c/"
  route:
    auto_host_rewrite: true
    prefix_rewrite: "/v1.0/invoke/catalog-api/method/"
    cluster: dapr

Consider a scenario where the front-end client wants to retrieve a list of catalog items. The Catalog API provides an endpoint for getting the catalog items:

假设前端客户端要检索目录项。Catalog API  提供用于获取目录项的终结点:

[Route("api/v1/[controller]")]
[ApiController]
public class CatalogController : ControllerBase
{
    [HttpGet("items")]
    public async Task<IActionResult> ItemsAsync(
        [FromQuery] int pageSize = 10,
        [FromQuery] int pageIndex = 0)
    {
        // ...
    }

First, the front end makes a direct HTTP call to the Envoy API gateway.

首先,前端直接使用http调用 Envoy API 网关。

GET http://<api-gateway>/c/api/v1/catalog/items?pageSize=20

The Envoy proxy matches the route, rewrites the HTTP request, and forwards it to the invoke API of its Dapr sidecar:

 Envoy 代理匹配路由,重写 HTTP 请求,并将请求转发到其Dapr 边车的服务调用API:

GET http://127.0.0.1:3500/v1.0/invoke/catalog-api/method/api/v1/catalog/items?pageSize=20

The sidecar handles service discovery and routes the request to the Catalog API sidecar. Finally, the sidecar calls the Catalog API to execute the request, fetch catalog items, and return a response:

 网关Envoy 的边车进行服务发现并将请求路由到Catalog API 的边车。最后,Catalog API 的边车调用Catalog API 来执行请求、提取目录项并返回响应:

GET http://localhost/api/v1/catalog/items?pageSize=20

Make aggregated service calls using the .NET SDK

使用 .NET SDK 进行聚合服务调用

Most calls from the eShop front end are simple CRUD calls. The API gateway forwards them to a single service for processing. Some scenarios, however, require multiple back-end services to work together to complete a request. For these more complex calls, eShop uses the web shopping aggregator service to mediate the workflow across multiple services. Figure 6-4 show the processing sequence of adding an item to your shopping basket:

EShop 前端的大多数调用都是简单的 CRUD 调用。API 网关将它们转发给单个服务进行处理。但在某些情况下,需要多个后端服务协调工作来完成请求。对于更复杂的调用,eShop 使用 web 购物聚合器服务来跨多个服务协调工作流。图6-4 显示了将商品添加到购物篮的序列图:

Figure 6-4. Update shopping basket sequence.

图 6-4 。更新购物篮序列图。

The aggregator service first retrieves catalog items from the Catalog API. It then validates item availability and pricing. Finally, the aggregator service saves the updated shopping basket by calling the Basket API.

聚合器服务首先从Catalog  API 中检索目录项。然后,它将验证商品的是否可用(是否有效)和价格。最后,聚合器服务通过调用Basket  API 来保存更新后的购物车。

The aggregator service contains a BasketController that provides an endpoint for updating the shopping basket:

聚合器服务包含一个 BasketController ,它提供用于更新购物篮的终结点:

[Route("api/v1/[controller]")]
[Authorize]
[ApiController]
public class BasketController : ControllerBase
{
    private readonly ICatalogService _catalog;
    private readonly IBasketService _basket;

    [HttpPost]
    [HttpPut]
    public async Task<ActionResult<BasketData>> UpdateAllBasketAsync(
        [FromBody] UpdateBasketRequest data, [FromHeader] string authorization)
    {
        // Get the item details from the catalog API.
        var catalogItems = await _catalog.GetCatalogItemsAsync(
            data.Items.Select(x => x.ProductId));

        // Check item availability and prices; store results in basket object.
        var basket = CreateValidatedBasket(data, catalogItems);

        // Save the shopping basket.
        await _basket.UpdateAsync(basket, authorization);

        return basket;
    }

    // ...
}

The UpdateAllBasketAsync method gets the Authorization header of the incoming request using a FromHeader attribute. The Authorization header contains the access token that is needed to call protected back-end services.

UpdateAllBasketAsync方法使用FromHeader 特性获取传入请求的 Authorization 标头 。 Authorization 标头包含调用受保护的后端服务所需的访问令牌。

After receiving a request to update the basket, the aggregator service calls the Catalog API to get the item details. The Basket controller uses an injected ICatalogService object to make that call and communicate with the Catalog API. The original implementation of the interface used gRPC to make the call. The updated implementation uses Dapr service invocation with HttpClient support:

收到更新购物篮的请求后,聚合器服务将调用Catalog API 以获取商品详细信息。购物篮控制器使用注入的 ICatalogService 对象与Catalog API 通信。 ICatalogService 接口的原始实现使用 gRPC 进行调用。更新了的实现结合使用 Dapr 服务调用与 HttpClient 支持:

public class CatalogService : ICatalogService
{
    private readonly HttpClient _httpClient;

    public CatalogService(HttpClient httpClient)
    {
        _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
    }

    public Task<IEnumerable<CatalogItem>> GetCatalogItemsAsync(IEnumerable<int> ids)
    {
        var requestUri = $"api/v1/catalog/items?ids={string.Join(",", ids)}";

        return _httpClient.GetFromJsonAsync<IEnumerable<CatalogItem>>(requestUri);
    }

    // ...
}

Notice how no Dapr specific code is required to make the service invocation call. All communication is done using the standard HttpClient object.

请注意,不需要 Dapr 特定的代码就可以调用服务调用。所有通信都是使用标准 HttpClient 对象完成的。

The Dapr HttpClient is injected into the CatalogService class in the Startup.ConfigureServices method:

在 Startup.ConfigureServices 方法中将 Dapr HttpClient 注入 CatalogService 类:

services.AddSingleton<ICatalogService, CatalogService>(
    _ => new CatalogService(DaprClient.CreateInvokeHttpClient("catalog-api")));

The other call made by the aggregator service is to the Basket API. It only allows authorized requests. The access token is passed along in an Authorization request header to ensure the call succeeds:

聚合器服务还调用Basket API。它只允许授权的请求。在 授权 请求标头中传递访问令牌,以确保调用成功:

public class BasketService : IBasketService
{
    public Task UpdateAsync(BasketData currentBasket, string accessToken)
    {
        var request = new HttpRequestMessage(HttpMethod.Post, "api/v1/basket")
        {
            Content = JsonContent.Create(currentBasket)
        };
        request.Headers.Authorization = new AuthenticationHeaderValue(accessToken);

        var response = await _httpClient.SendAsync(request);
        response.EnsureSuccessStatusCode();
    }

    // ...
}

In this example too, only standard HttpClient functionality is used to call the service. This allows developers who are already familiar with HttpClient to reuse their existing skills. It even enables existing HttpClient code to use Dapr service invocation without making any changes.

在此示例中,仅使用标准 HttpClient 功能来调用服务。这样,已经熟悉 HttpClient 的开发人员就可以重复使用其现有技能。甚至现有的 HttpClient 代码可以用于 Dapr 服务调用,而无需进行任何更改。

Summary

总结

In this chapter, you learned about the service invocation building block. You saw how to invoke remote methods both by making direct HTTP calls to the Dapr sidecar, and by using the Dapr .NET SDK.

本章介绍了服务调用构建块。你已了解如何通过直接 HTTP 调用 Dapr 边车并使用 Dapr .NET SDK 调用远程方法。

The Dapr .NET SDK provides multiple ways to invoke remote methods. HttpClient support is great for developers wanting to reuse existing skills and is compatible with many existing frameworks and libraries. DaprClient offers support for directly using the Dapr service invocation API using either HTTP or gRPC semantics.

Dapr .NET SDK 提供了多种方法来调用远程方法。HttpClient 支持对于需要重复使用现有技能的开发人员非常有用,并且与许多现有框架和库兼容。DaprClient 提供通过 HTTP 或 gRPC 语义直接使用 Dapr 服务调用 API 的支持。

The eShopOnDapr reference architecture shows how the original eShopOnContainers solution is modernized by using Dapr service invocation. Adding Dapr to eShop provides benefits such as automatic retries, message encryption using mTLS, and improved observability.

EShopOnDapr 参考架构显示了如何使用 Dapr 服务调用优化原始 eShopOnContainers 解决方案。将 Dapr 添加到 eShop 提供了一些优点,例如自动重试、使用 mTLS 进行消息加密,以及改进的可观察性。

目录:

以上是关于面向.NET开发人员的Dapr——服务调用的主要内容,如果未能解决你的问题,请参考以下文章

面向.NET开发人员的Dapr——机密

面向.NET开发人员的Dapr——总结和前景

面向.NET开发人员的Dapr——可观察性

面向.NET开发人员的Dapr——绑定

面向.NET开发人员的Dapr——状态管理

面向.NET开发人员的Dapr——参考应用程序