1. 一行代码让你的项目轻松使用Dapr

1. 一行代码让你的项目轻松使用Dapr

介绍

Dapr简化了云原生开发,让开发可以把焦点放在应用的业务逻辑上,从而让代码简单、可移植,那作为一个.Net开发者,我们也希望项目可以快速用上dapr,那究竟应该如何做呢?

Dapr提出了Sidecar(边车)的概念,在启动项目时再额外启动一个Sidecar, 通过Sidecar可以解决进程间通信,为此官方提供了两种部署方式

  1. 自托管方式下运行Dapr
  2. 在 Kubernetes 模式中部署和运行 Dapr

其中Kubernetes模式部署是通过Kubernetes来完成的,在开发中我们更多的是通过自托管模式使用Dapr,那自托管模式是怎么做的呢?

使用命令行工具,在项目根目录输入:

1
dapr run --app-id assignment-server --app-port 5038 dotnet run

详细文档参考:手把手教你学Dapr - 3. 使用Dapr运行第一个.Net程序

参考以上详细文档操作后,我们就可以在命令行工具中执行dapr invoke --app-id assignment-server --method hello或者Http请求来调用对应的应用的方法

看似好像也不是很复杂,但如果你需要调试dotnet项目呢?再复杂一点的需要启动多个项目进行调试呢?端口一多起来的确会显得很麻烦。

有没有什么办法可以解决呢?有,docker-compose。

但我还不想用这么重的东西,我想像平时开发项目一样直接在windows上运行可不可以?

Masa.Contrib.Development.DaprStarter.AspNetCore 它来了

协助管理dapr进程,用于开发时减少对docker compose的依赖

瞌睡了就有人送枕头,一句话让我们了解到了它的作用,正好解决了我们需要通过命令行来启动dapr的问题,那下面我们看看这个怎么用:

入门

本着绝对不多写一行代码的心态,我们准备出发了……

  1. 从大佬doddgu的博客的链接中发现一份源码地址,为防止后期文档调整,先fork一份到自己仓库

    1
    git clone https://github.com/zhenlei520/dapr-study-room.git
  2. 使用命令行工具打开目录dapr-study-room\Assignment03,然后执行命令

    1
    dotnet add package Masa.Contrib.Development.DaprStarter.AspNetCore --version 0.7.0-preview.12

    或使用Visual Studio打开解决方案Assignment03,选中Assignment.Server并安装Masa.Contrib.Development.DaprStarter.AspNetCore

  3. 打开Program.cs,并添加DaprStarter(注意看有注释的那一行)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    using Masa.Contrib.Development.DaprStarter.AspNetCore;

    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddDaprStarter();//添加DaprStarter即可
    var app = builder.Build();

    app.Map("/hello", () => Console.WriteLine("Hello World!"));

    app.Run();
  4. 使用命令行工具执行命令验证dapr是否启动成功

    1
    dapr invoke --app-id Assignment-Server-00D861D0C0B7 --method hello

此时会有小伙伴问了,为什么app-id是Assignment-Server-00D861D0C0B7?

查看文档后发现Masa.Contrib.Development.DaprStarter.AspNetCore的app-id生成规则为:AppId + AppIdDelimiter + AppIdSuffix,其中

  • AppId默认:项目名.Replace(“.”,”-“)
  • AppIdSuffix默认:网卡地址

由于我们的项目名为Assignment.Server,当前电脑的网卡地址是00D861D0C0B7,所以dapr最终的appid为Assignment-Server-00D861D0C0B7,到这里,Masa.Contrib.Development.DaprStarter.AspNetCore的使用讲解已经完成了

冷知识,为什么 . 要换成 - ?

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

为什么要加网卡地址作为后缀?

因为目前自托管默认采用mDNS,会导致局域网内用户的AppId互相污染。你的同事和你一起在开发,都启动了A应用,你俩就自动负载了,那后果自然就是请求也到处跑了。

进阶

如果我希望自己指定AppId而不是使用默认的规则怎么办呢?目前支持三种写法:

  • 根据规则(配置默认装配)
  • 根据规则 + 代码指定(配置自定义装配)
  • 根据配置文件(根据IConfiguration配置生成)

配置默认装配(也是上面介绍的一行代码的方式)

修改Program.cs文件

1
2
// 省略上述代码
builder.Services.AddDaprStarter();

根据规则 + 代码指定(配置自定义装配)

修改Program.cs文件

1
2
3
4
5
6
7
8
9
// 省略上述代码
builder.Services.AddDaprStarter(opt =>
{
opt.AppId = "masa-dapr-test";
opt.AppPort = 5001;
opt.AppIdSuffix = "";
opt.DaprHttpPort = 8080;
opt.DaprGrpcPort = 8081;
});

基于默认装配的升级版,在默认装配基础上通过指定特殊参数完成特殊需求,未配置的参数将使用默认值

根据IConfiguration配置生成

  1. 修改appsettings.json
1
2
3
4
5
6
7
8
9
{
"DaprOptions": {
"AppId": "masa-dapr-test",
"AppPort": 5001,
"AppIdSuffix": "",
"DaprHttpPort": 8080,
"DaprGrpcPort": 8081
}
}
  1. 修改Program.cs
1
builder.Services.AddDaprStarter(builder.Configuration.GetSection("DaprOptions"));

优势:更改appsettings.json配置后,dapr sidecar会自动更新,项目无需重启

Masa.Contrib.Development.DaprStarter.AspNetCore的设计思路

设计思路基于两个方面,其一本机自动启动dapr sidecar还可以正常调试.Net项目,其二简化配置

技术选型

我们有两种启动dapr sidecar的方式:

  1. dapr run
  2. daprd

两者之间的差别如下所示:image.png

完整的对比可查看:https://docs.dapr.io/reference/arguments-annotations-overview/

通过对比我们发现,我们的目标使用daprd与Dapr CLI都可以实现,那为什么Masa.Contrib.Development.DaprStarter选择的是Dapr CLI,而不是daprd呢?

核心的原因是dapr可以通过dapr list命令很简单的就获取到当前运行的所有dapr程序,而daprd无法获取。如果使用daprd,那我们就需要使用C#代码通过操作dll获取具体执行的dapr命令,且多平台支持不好,所以暂时用了Dapr CLI

更优秀的Dapr管理需要做到什么?

  • 使用简单
  • 参数可配置
  • 功能支持选择性启动
  • Dapr保活
  • 配置支持动态更新

为了能更方便的使用,我们做了以下约定:

  1. 针对dapr的非必填项,默认关闭不启用,手动配置参数后开启
  2. 针对dapr的必填项: app-id、app-port、dapr-http-port、dapr-grpc-port 自动生成并配置

app-id 生成规则

其中dapr的app-id默认生成规则为:AppId + AppIdDelimiter + AppIdSuffix,其中

  • AppId默认:项目名.Replace(“.”,”-“)
  • AppIdDelimiter默认:-
  • AppIdSuffix默认:当前机器网卡地址

当AppIdSuffix赋值为空字符串,dapr的AppId的生成规则为:AppId

app-port 获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private ushort GetAppPort(DaprOptions options)
{
var server = _serviceProvider.GetRequiredService<IServer>();
var addresses = server.Features.Get<IServerAddressesFeature>()?.Addresses;
if (addresses is { IsReadOnly: false, Count: 0 })
throw new Exception("Failed to get the startup port, please specify the port manually");

return addresses!
.Select(address => new Uri(address))
.Where(address
=> (options.EnableSsl is true && address.Scheme.Equals(Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase))
|| address.Scheme.Equals(Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase))
.Select(address => (ushort)address.Port).FirstOrDefault();
}

为了防止启动过程中修改端口,过早的获取到被弃用的端口。因此我们使用后台任务启动dapr sidecar

dapr-http-port、dapr-grpc-port获取

因为支持用户配置,所以我们遵循下面的顺序

  1. 如果指定端口,被占用则自动kill port所在进程,保证可以sidecar可以正常启动

    为什么端口占用就要先kill,复用不行吗?

    因为sidecar有初始化配置,程序调整的代码影响到sidecar配置变更我们无法检测,所以启动时保证是最新的是比较合适的选择

  2. 如果未指定端口,则交还给dapr,通过Dapr CLI的规则生成对应的http-port或grpc-port

Dapr保活

为了保证dapr进程是活跃的,我们在库中建立了一个心跳检查任务用来检测当前的dapr进程是否是活跃的,当dapr进程意外停止后会被重启,且配置信息与上一次成功的dapr配置保持不变

如果不需要保活机制的话可以将EnableHeartBeat改为false,则不启用dapr保活机制

配置支持动态更新

我们通过IOptionsMonitor的OnChange方法来监听配置的变更,当配置变更后我们会通过IDaprProcess 提供的Refresh方法来重启dapr进程,并重新调整环境变量信息

本章源码

Assignment03

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

开源地址

MASA.Framework:https://github.com/masastack/MASA.Framework

MASA.EShop:https://github.com/masalabs/MASA.EShop

MASA.Blazor:https://github.com/BlazorComponent/MASA.Blazor

如果你对我们的 MASA Framework 感兴趣,无论是代码贡献、使用、提 Issue,欢迎联系我们

16373211753064.png

作者

MASA

发布于

2022-04-17

更新于

2023-05-26

许可协议