写点什么

手把手教你学 Dapr - 4. 服务调用

  • 2022 年 1 月 19 日
  • 本文字数:4593 字

    阅读完需:约 15 分钟

介绍

通过使用服务调用,您的应用程序可以使用标准的 gRPC 或 HTTP 协议与其他应用程序可靠、安全地通信。

为什么不直接用 HttpClientFactory 呢

先问几个问题:


  • 如何发现和调用不同服务的方法

  • 如何安全地调用其他服务,并对方法应用访问控制

  • 如何处理重试和瞬态错误

  • 如何使用分布式跟踪指标来查看调用图来诊断生产中的问题


此时你会发现这些事情 HttpClientFactory 没有帮你完成,而在微服务中这些又是必不可少的能力,接下来看看服务调用都做了什么

服务调用如何工作的

先看一下两个服务之间的调用顺序


  1. 服务 A 向服务 B 发起一个 HTTP/gRPC 的调用。调用转到了本地的 Dapr sidecar

  2. Dapr 使用名称解析组件发现服务 B 的位置

  3. Dapr 将消息转发至服务 B 的 Dapr sidecar

  4. : Dapr sidecar 之间的所有调用都通过 gRPC 来提高性能。 仅服务与 Dapr sidecar 之间的调用可以是 HTTP 或 gRPC

  5. 服务 B 的 Dapr sidecar 将请求转发至服务 B 上的特定端点 (或方法) 。 服务 B 随后运行其业务逻辑代码

  6. 服务 B 发送响应给服务 A。 响应将转至服务 B 的 Dapr sidecar

  7. Dapr 转发响应至服务 A 的 Dapr sidecar

  8. 服务 A 接收响应

命名空间作用域

默认情况下,调用同一个命名空间的其他服务可以直接使用 AppID(假设是:nodeapp)


localhost:3500/v1.0/invoke/nodeapp/method/neworder
复制代码


服务调用也支持跨命名空间调用,在所有受支持的宿主平台上,Dapr AppID 遵循 FQDN 格式,其中包括目标命名空间。


FQDN:(Fully Qualified Domain Name)全限定域名:同时带有主机名和域名的名称。(通过符号“.”)

例如:主机名是 bigserver,域名是 mycompany.com,那么 FQDN 就是 bigserver.mycompany.com


​ **注:**FQDN 是通过符号.来拼接域名的,这也就解释了 AppID 为什么不能用符号.,这里不记住的话,应该会有不少小伙伴会踩坑


​ 比如.net 开发者习惯用 A.B.C 来命名项目,但 AppID 需要把.换成-且所有单词最好也变成小写 (a-b-c),建议把它变成约定遵守


比如调用命名空间:production,AppID:nodeapp


localhost:3500/v1.0/invoke/nodeapp.production/method/neworder
复制代码


这在 K8s 集群中的跨名称空间调用中特别有用

服务间安全性

通过托管平台上的相互(mTLS)身份验证,包括通过 Dapr Sentry 服务的自动证书转移,可以确保 Dapr 应用程序之间的所有调用的安全。 下图显示了自托管应用程序的情况。

访问控制

应用程序可以控制哪些其他应用程序可以调用它们,以及通过访问策略授权它们做什么。 这使您能够限制具有个人信息的敏感应用程序不被未经授权的应用程序访问,并结合服务到服务的安全通信,提供了软多租户部署。


具体的访问控制后续章节会介绍

重试

在调用失败和瞬态错误的情况下,服务调用执行自动重试,并在回退时间段内执行。


**注:**自动重试,默认是开启的,可以关。但如果不关且业务又不支持幂等是很危险的。建议服务的接口要设计支持幂等,这在微服务里也是一个标配的选择。


导致重试的错误有:

网络错误,包括端点不可用和拒绝连接。

由于在调用/被调用的 Dapr sidecars 上更新证书而导致的身份验证错误。

每次呼叫重试的回退间隔为 1 秒,最多为 3 次。 通过 gRPC 与目标 Sidecar 连接的超时时间为 5 秒

可插拔的服务发现

Dapr 可以在各种托管平台上运行。 为了启用服务发现和服务调用,Dapr 使用可插拔的名称解析组件。 例如,K8s 名称解析组件使用 K8s DNS 服务来解析集群中运行的其他应用程序的位置。 自托管机器可以使用 mDNS 名称解析组件。 Consul 名称解析组件可以在任何托管环境中使用,包括 K8s 或自托管环境


划重点,自托管机器使用 mDNS,在开发环境中后面文章会推荐 VS 上的无缝开发体验,就是基于 mDNS 的

但它有点点小问题,我们已经解决了。你只需要像开发一个控制台程序一样,基于 Minimal API 开心的 F5 就可以了

建议还没有了解 Minimal API 的小伙伴可以研究起来了,真香

使用 mDNS 进行轮询负载均衡

一图胜千言,就使用 mDNS 轮着调用


可观测性的跟踪和指标

默认情况下,将跟踪应用程序之间的所有调用,并收集指标,以提供应用程序的洞察力和诊断,这在生产场景中尤其重要。 这为您提供了服务之间调用的调用图和指标。

服务调用 API 和 gRPC 代理

pythonapp 通过 Dapr sidecar 调用 nodeapp,通过服务调用的 API 及 gRPC 代理依然是上面见到的那个调用流程,做到了语言无关



使用 HTTP 调用服务

创建 Assignment.Server

创建ASP.NET Core空项目,并修改launchSettings.json,让启动 HTTP 的启动端口变为 5000


profiles.Assignment.Server.applicationUrl 的值改为 "https://localhost:6000;http://localhost:5000"


修改Program.cs文件


var builder = WebApplication.CreateBuilder(args);var app = builder.Build();
app.MapPost("/", () => Console.WriteLine("Hello!"));
app.MapGet("/Hello1", () =>{ Console.WriteLine("Hello World1!"); return $"\"Hello World1!\"";});
app.MapPost("/Hello2", () => Console.WriteLine("Hello World2!"));
app.Map("/Hello3", () => Console.WriteLine("Hello World3!"));
app.Run();
复制代码


此时一共有 4 个服务


  • / :Post 方法,打印 Hello!

  • /Hello1:Get 方法,打印 Hello World1!,返回 Hello World1!

  • **注:**返回的类型要是 Json 字符串,方便 SDK 反序列化

  • /Hello2:Post 方法,打印 Hello World2!

  • /Hello3:不带后缀表示适配所有方法,打印 Hello World3!

先使用 Dapr CLI 来验证一下

运行Assignment.Server:在目录dapr-study-room\Assignment04\Assignment.Server打开命令行工具,并执行下面命令


dapr run --app-id assignment-server --app-port 5000 dotnet watch
复制代码


细心的小伙伴应该可以发现与上一篇的命令有一点点不同,dontet run 变成了 dotnet watch,这样会开启热重载,方便调试


调用服务:再打开一个新的命令行工具,并执行下面命令


dapr invoke --app-id assignment-server --method /dapr invoke --app-id assignment-server --method Hello1dapr invoke --app-id assignment-server --method Hello2dapr invoke --app-id assignment-server --method Hello3
复制代码


可以发现 4 个命令都调用成功了,但是Assignment.Server输出结果有点意外


== APP == Hello!== APP == Hello World2!== APP == Hello World3!
复制代码


是的,没有Hello World1!,那怎么办呢?我们把 Hello1 的命令改一下


dapr invoke --app-id assignment-server --method Hello1 --verb GET
复制代码


invoke 调用的输出除了App invoked successfully以外还多了一行Hello World1!


与此同时Assignment.Server的输出正确了


== APP == Hello World1!
复制代码


除此之外invoke还有一些参数,比如--data,--data-file,喜欢研究 Dapr CLI 的小伙伴可以继续尝试。不过一般情况下用 SDK 就可以了

创建 Assignment.Client

HTTP 服务调用

创建控制台应用程序项目,使用 NuGet 包管理器添加Dapr.Client SDK,并修改Program.cs文件


using Dapr.Client;
var appId = "assignment-server";
var client = new DaprClientBuilder().Build();
await client.InvokeMethodAsync(appId, "/");
var resp = await client.InvokeMethodAsync<string>(HttpMethod.Get, appId, "Hello1");Console.WriteLine($"Hello1 Response: {resp}");
await client.InvokeMethodAsync(appId, "Hello2");
await client.InvokeMethodAsync(appId, "Hello3");
复制代码


看几个细节


  • DaprClient 是从DaprClinetBuilder Build 出来的

  • 还有一种方式使用 DaprClient,通过 DI

  • 首先都是需要添加Dapr.AspNetCore NuGet 包

  • 然后开始有分支了,如果是以前 Web API 的方式可以在 Startup.cs 文件ConfigureServices方法加入一行代码


  services.AddControllers().AddDapr();
复制代码


如果使用 Minimal API 默认是没有 Controllers 的,那可以在var builder = WebApplication.CreateBuilder(args); 之后加入一行代码


  builder.Services.AddDaprClient();
复制代码


成功的注入进来了,在构造函数或者[FromServices]里愉快的玩耍吧


  • HttpMethod.Post 的我都没有指定,默认就是 Post

  • HttpMethod.Get 的时候,返回值会自动用 Json 反序列化

  • 不喜欢 Json?可以通过 DaprClient.CreateInvokeHttpClient 构造 HttpClient,聪明的你肯定知道后面怎么办了


注:


1. Minimal API虽香,但新,所以不是所有功能都支持,比如从参数中直接映射状态管理,要等Minimal API支持Model Binder以后且SDK也同步支持了才可以2. DaprClient是TCP的,也是线程安全的,可以大胆的复用,如果不用DI的话不需要频繁构建DaprClient
复制代码

验证调用成功

使用命令行工具打开目录dapr-study-room\Assignment04\Assignment.Client,然后执行命令


dotnet run
复制代码


如果你不是用 VS Code 终端的 PowerShell 执行 dapr run 就可能遇到下面的错误


即便你没有遇到也建议了解一下如何支持非默认端口


An exception occurred while invoking method: '/' on app-id: 'assignment-server' ---> System.Net.Http.HttpRequestException: 由于目标计算机积极拒绝,无法连接。 (127.0.0.1:3500) ---> System.Net.Sockets.SocketException (10061): 由于目标计算机积极拒绝,无法连接。
复制代码


因为上面使用 dapr run 的时候没有指定 dapr http port,而默认 client 访问的是 3500 端口


解决的办法有两种:


  1. 修改Assignment.Server启动参数,增加--dapr-http-port 3500,这个方法治标不治本,因为将来我们可能启动多个服务


   dapr run --app-id assignment-server --app-port 5000 --dapr-http-port 3500 dotnet watch
复制代码


  1. 修改Assignment.Client的环境变量

  2. 首先执行dapr list查看端口,以下面为例,HTTP PORT 是51460

  3. APP ID HTTP PORT GRPC PORT APP PORT COMMAND AGE CREATED PID

    assignment-server 51460 51461 5000 dotnet watch 7s 2021-10-29 14:13.49 11676

  4. 修改Assignment.Client的启动参数,注意把51460换成你自己的端口,使用PowerShell执行下面命令


   $Env:DAPR_HTTP_PORT = 51460   dotnet run
复制代码


再执行一次dotnet run就可以看到正确的输出结果了


Hello1 Response: Hello World1!
复制代码

gRPC 服务调用

篇幅太长了,举一反三吧。就是调用InvokeMethodGrpcAsync,然后 dapr-http-port 换成 dapr-grpc-port,DAPR_HTTP_PORT 换成 DAPR_GRPC_PORT

查看跟踪

还记得 dapr init 的时候 docker 里有个 zipkin 吧,通过 zipkin 可以看一下调用跟踪,通过浏览器打开下面地址


http://localhost:9411/


此时页面是空的



根据步骤操作一下就可以看到了



随便点开一行数据尾部的 SHOW,就可以看到调用详情


本章源码

Assignment04


https://github.com/doddgu/dapr-study-room

我们正在行动,新的框架、新的生态

我们的目标是自由的易用的可塑性强的功能丰富的健壮的


所以我们借鉴 Building blocks 的设计理念,正在做一个新的框架MASA Framework,它有哪些特点呢?


  • 原生支持 Dapr,且允许将 Dapr 替换成传统通信方式

  • 架构不限,单体应用、SOA、微服务都支持

  • 支持.Net 原生框架,降低学习负担,除特定领域必须引入的概念,坚持不造新轮子

  • 丰富的生态支持,除了框架以外还有组件库、权限中心、配置中心、故障排查中心、报警中心等一系列产品

  • 核心代码库的单元测试覆盖率 90%+

  • 开源、免费、社区驱动

  • 还有什么?我们在等你,一起来讨论


经过几个月的生产项目实践,已完成 POC,目前正在把之前的积累重构到新的开源项目中


目前源码已开始同步到 Github(文档站点在规划中,会慢慢完善起来):


MASA.BuildingBlocks


MASA.Contrib


MASA.Utils


MASA.EShop


BlazorComponent


MASA.Blazor


QQ 群:7424099


微信群:加技术运营微信(MasaStackTechOps),备注来意,邀请进群



MASA 技术团队:(鬼谷子)

发布于: 刚刚阅读数: 2
用户头像

还未添加个人签名 2021.10.26 加入

还未添加个人简介

评论

发布
暂无评论
手把手教你学Dapr - 4. 服务调用