开始使用 Dapr
在前两章中,你了解了 Dapr 的基本概念。 现在,我们将尝试来进行运用。 本章将指导你完成准备本地开发环境并生成两个 Dapr .NET 应用程序。
将 Dapr 安装到本地环境中
首先,在开发计算机上安装 Dapr。 完成后,可以在自托管模式下生成并运行 Dapr 应用程序。
安装 Dapr CLI。 它使你能够启动、运行并管理 Dapr 实例。 它还提供调试支持。
安装 Docker Desktop。 如果在 Windows 上运行,请确保将用于 Windows 的 Docker Desktop 配置为使用 Linux 容器。
注意
默认情况下,Dapr 使用 Docker 容器来为你提供最佳的全新体验。 若要在 Docker 外部运行 Dapr,可以跳过此步骤并执行精简初始化。 本章中的示例要求使用 Docker 容器。
初始化 Dapr。 此步骤通过安装最新的 Dapr 二进制和容器映像来设置开发环境。
安装 .NET 7 SDK。
安装完 Dapr 后,现在可以生成你的第一个 Dapr 应用程序了!
生成你的第一个 Dapr 应用程序
首先,生成一个使用 Dapr 状态管理构建块的简单 .NET 控制台应用程序。
创建应用程序
打开所选的命令行界面或终端。 可以考虑使用 Visual Studio Code 中的终端功能。 导航到要在其中生成应用程序的根文件夹。 导航到那里后,输入以下命令以创建新的 .NET 控制台应用程序:
dotnet new console -o DaprCounter
此命令将搭建一个简单“Hello World”.NET 应用程序的基架。
然后,导航到由上一个命令创建的新目录:
cd DaprCounter
使用
dotnet run
命令运行新创建的应用程序。 这样会将“Hello World!”写入到控制台屏幕:dotnet run
添加 Dapr 状态管理
接下来,将使用 Dapr 状态管理构建块在程序中实现一个有状态计数器。
可以使用 Dapr 对 HTTP 和 gRPC 的本机支持在任何开发平台上调用 Dapr API。 不过,.NET 开发人员发现使用 Dapr .NET SDK 更自然且更直观。 它提供强类型的 .NET 客户端来调用 Dapr API。 .NET SDK 还与 ASP.NET Core 紧密集成。
在终端窗口中,将
Dapr.Client
NuGet 包添加到应用程序:dotnet add package Dapr.Client
在你最喜爱的编辑器中打开
Program.cs
,将它的内容更新为以下代码:using Dapr.Client; const string storeName = "statestore"; const string key = "counter"; var daprClient = new DaprClientBuilder().Build(); var counter = await daprClient.GetStateAsync<int>(storeName, key); while (true) { Console.WriteLine($"Counter = {counter++}"); await daprClient.SaveStateAsync(storeName, key, counter); await Task.Delay(1000); }
更新的代码实现以下步骤:
- 首先,会实例化一个新的
DaprClient
实例。 此类使你能够与 Dapr 挎斗进行交互。 - 从状态存储中,
DaprClient.GetStateAsync
会提取counter
键的值。 如果该键不存在,会返回int
的默认值(即0
)。 - 然后,代码将循环访问,将
counter
值写入控制台并将递增的值保存到状态存储中。
- 首先,会实例化一个新的
Dapr CLI
run
命令将启动应用程序。 它调用基础 Dapr 运行时,并使应用程序和 Dapr 挎斗一起运行。 如果省略app-id
,Dapr 将为应用程序生成唯一的名称。 此命令的最后一段dotnet run
指示 Dapr 运行时运行 .NET 应用程序。重要
必须要注意的是,使用状态管理构建块时始终传递显式
app-id
参数。 对于每个键/值对,构建块将应用程序 ID 值用作其状态键的前缀。 如果应用程序 ID 发生了更改,则将无法再访问之前存储的状态。现在,使用以下命令运行应用程序:
dapr run --app-id DaprCounter dotnet run
尝试停止并重新启动应用程序。 你将看到计数器未重置。 相反,它会从之前保存的状态继续。 Dapr 构建块使应用程序具有状态。
重要
需要认识到的重要一点是,你的示例应用程序会与预配置的状态组件进行通信,但没有直接依赖它。 Dapr 将抽象出依赖关系。 很快你将会看到,可以通过简单的配置更新来更改基础状态存储组件。
你可能会感到疑惑,状态究竟存储在哪里?
组件配置文件
当你首次为本地环境初始化 Dapr 时,它将自动预配一个 Redis 容器。 然后,Dapr 使用一个名为 statestore.yaml
的组件配置文件将 Redis 容器配置为默认的状态存储组件。 文件的具体内容以下:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
- name: actorStateStore
value: "true"
注意
在 Linux/macOS 上,默认组件配置文件存储在 $HOME/.dapr/components
文件夹中;在 Windows 上,默认组件配置文件存储在 %USERPROFILE%\.dapr\components
文件夹中。
请注意上面所示的组件配置文件的格式:
- 每个组件都有一个名称。 在上面的示例中,组件名为
statestore
。 我们在第一个代码示例中使用了该名称,告知 Dapr 挎斗要使用的组件。 - 每个组件配置文件都有一个
spec
部分。 此部分包含指定组件类型的type
字段。version
字段指定组件版本。metadata
字段包含组件需要的信息,例如连接详细信息和其他设置。 不同类型的组件的元数据值会有所不同。
Dapr 挎斗可以使用在应用程序中配置的任何 Dapr 组件。 但是,如果你的体系结构限制了组件的可访问性会发生什么情况? 如何将 Redis 组件限制为仅在生产环境中运行的 Dapr 挎斗?
为此,可以为生产环境定义一个 namespace
。 可以将它命名为 production
。 在自托管模式下,通过设置 NAMESPACE
环境变量来指定 Dapr 挎斗的命名空间。 配置后,Dapr 挎斗将仅加载与该命名空间相匹配的组件。 对于 Kubernetes 部署,Kubernetes 命名空间确定加载的组件。 下面的示例展现了 production
命名空间中放置的 Redis 组件。 请注意 metadata
元素中的 namespace
声明:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
namespace: production
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
- name: actorStateStore
value: "true"
重要
只有在同一命名空间中运行的应用程序才能访问带命名空间的组件。 如果 Dapr 应用程序无法加载组件,请确保应用程序命名空间与组件命名空间相匹配。 在应用程序命名空间存储在 NAMESPACE
环境变量中的自托管模式下,这一点特别难以确认。
如果需要,可以进一步将组件限制到特定的应用程序。 在 production
命名空间中,可能需要将 Redis 缓存的访问限制为仅限 DaprCounter
应用程序。 为此,请在组件配置中指定 scopes
。 下面的示例演示如何将 Redis statestore
组件的访问限制为 production
命名空间中的应用程序 DaprCounter
:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
namespace: production
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
- name: actorStateStore
value: "true"
scopes:
- DaprCounter
生成多容器 Dapr 应用程序
在第一个示例中,创建了一个与 Dapr 挎斗并行运行的简单 .NET 控制台应用程序。 不过,新式分散式应用程序通常由许多移动的部件组成。 它们可以同时运行独立的微服务。 这些新式应用程序通常是容器化应用程序,并需要容器业务流程工具,例如 Docker Compose 或 Kubernetes。
在下一个示例中,你将创建一个多容器应用程序。 还将使用 Dapr 服务调用构建块在服务之间进行通信。 解决方案中将包含一个 Web 应用程序,该应用程序从 Web API 检索天气预报。 它们每个都在 Docker 容器中运行。 你将使用 Docker Compose 在本地运行容器,并启用调试功能。
请确保为 Dapr 配置了本地环境并安装了 .NET 7 开发工具(本章的开头提供了相关说明)。
此外,还需要使用安装了 ASP.NET 和 Web 开发工作负载的 Visual Studio 2022 来完成此示例。
创建应用程序
在 Visual Studio 2022 中,创建一个 ASP.NET Core Web 应用项目:
将你的项目命名为
MyFrontEnd
,将你的解决方案命名为DaprMultiContainer
:在最后一个对话框中,保留默认值。 请勿选择“启用 Docker 支持”。 稍后添加 Docker 支持。
对于后端,请将一个 ASP.NET Core Web API 项目添加到同一解决方案中:
将项目命名为
MyBackEnd
:默认情况下,Dapr 挎斗依赖于网络边界来限制对其公共 API 的访问。 因此,请清除“为 HTTPS 配置”复选框:
重要
如果使“为 HTTPS 配置”复选框保留为选中状态,生成的 ASP.NET Core API 项目将包含用于将客户端请求重定向到 HTTPS 终结点的中间件。 这将中断 Dapr 挎斗与应用程序之间的通信,除非显式配置在运行 Dapr 应用程序时使用 HTTPS。 若要使 Dapr 挎斗通过 HTTPS 进行通信,请将
--app-ssl
标记包含在 Dapr 命令中来启动应用程序。 另外,使用--app-port
参数来指定 HTTPS 端口。 本演练的其余部分在挎斗和应用程序之间使用纯 HTTP 通信,并要求清除“为 HTTPS 配置”复选框。
添加 Dapr 服务调用
现在,你将使用 Dapr 服务调用构建块配置服务之间的通信。 你将使 Web 应用能从 Web API 中检索天气预报。 服务调用构建块有许多优势。 其中包括服务发现、自动重试、消息加密(使用 mTLS)以及改进的可观测性。 你将使用 Dapr .NET SDK 在 Dapr 挎斗上调用服务调用 API。
在 Visual Studio 中,打开包管理器控制台(“工具”>“NuGet 包管理器”>“包管理器控制台”),并确保
MyFrontEnd
是默认的项目。 在控制台中,向项目添加Dapr.AspNetCore
NuGet 包:Install-Package Dapr.AspNetCore
在
MyFrontEnd
项目中,打开 Program.cs 文件并添加对builder.Services.AddDaprClient
的调用:var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddDaprClient(); builder.Services.AddRazorPages(); // ...
AddDaprClient
调用将DaprClient
类注册到 ASP.NET Core 依赖关系注入系统。 在注册了客户端后,现在可以将DaprClient
的实例注入到服务代码中,以便与 Dapr 挎斗、构建块和组件进行通信。将一个名为 WeatherForecast 的新的 C# 类文件添加到
MyFrontEnd
项目:namespace MyFrontEnd; public class WeatherForecast { public DateTime Date { get; set; } public int TemperatureC { get; set; } public int TemperatureF { get; set; } public string Summary { get; set; } = string.Empty; }
在“Pages”文件夹中打开“Index.cshtml.cs”文件,并将它的内容替换为以下代码:
using Dapr.Client; using Microsoft.AspNetCore.Mvc.RazorPages; namespace MyFrontEnd.Pages; public class IndexModel : PageModel { private readonly DaprClient _daprClient; public IndexModel(DaprClient daprClient) { _daprClient = daprClient; } public async Task OnGet() { var forecasts = await _daprClient.InvokeMethodAsync<IEnumerable<WeatherForecast>>( HttpMethod.Get, "MyBackEnd", "weatherforecast"); ViewData["WeatherForecastData"] = forecasts; } }
通过将
DaprClient
类注入到IndexModel
构造函数中,从而将 Dapr 功能添加到 Web 应用中。 在OnGet
方法中,使用 Dapr 服务调用构建块来调用后端 API 服务。 每当用户访问主页时,都会调用OnGet
方法。 使用DaprClient.InvokeMethodAsync
方法来调用MyBackEnd
服务的weatherforecast
方法。 稍后,在配置为与 Dapr 一起运行时将 Web API 配置为将MyBackEnd
作用其应用程序 ID。 最后,服务响应会以视图数据的形式保存。将“Pages”文件夹中“Index.cshtml”文件的内容替换为以下代码。 它会向用户显示视图数据中存储的天气预报:
@page @model IndexModel @{ ViewData["Title"] = "Home page"; } <div class="text-center"> <h1 class="display-4">Welcome</h1> <p>Learn about <a href="https://learn.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p> @foreach (var forecast in (IEnumerable<WeatherForecast>)ViewData["WeatherForecastData"]!) { <p>The forecast for @forecast.Date is @forecast.Summary!</p> } </div>
添加容器支持
在此示例的最后一部分,你将添加容器支持,并使用 Docker Compose 运行解决方案。
右键单击
MyFrontEnd
项目,选择“添加”>“容器业务流程协调程序支持...”。随即将显示“添加容器业务流程协调程序支持”对话框:选择“Docker Compose”。
在下一个对话框中,选择“Linux”作为目标 OS:
Visual Studio 在解决方案中的 docker-compose 文件夹中创建一个 docker-compose.yml 文件和一个 .dockerignore 文件:
docker-compose.yml 文件包含以下内容:
version: '3.4' services: myfrontend: image: ${DOCKER_REGISTRY-}myfrontend build: context: . dockerfile: MyFrontEnd/Dockerfile
.dockerignore 文件包含你不希望 Docker 在容器中包含的文件类型和扩展名。 这些文件与开发环境和源代码管理相关联,而不是与所部署的应用或服务相关联。
在 MyFrontEnd 项目目录的根目录中,创建了一个新的 Dockerfile。 Dockerfile 是用于生成映像的一系列命令。 有关详细信息,请参阅 Dockerfile 参考。
此 Dockerfile 包含以下命令:
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443 FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build WORKDIR /src COPY ["MyFrontEnd/MyFrontEnd.csproj", "MyFrontEnd/"] RUN dotnet restore "MyFrontEnd/MyFrontEnd.csproj" COPY . . WORKDIR "/src/MyFrontEnd" RUN dotnet build "MyFrontEnd.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "MyFrontEnd.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "MyFrontEnd.dll"]
在调用时,上述的 Dockerfile 将按顺序执行以下步骤:
- 拉取
mcr.microsoft.com/dotnet/aspnet:7.0
映像并将它命名为base
。 - 将工作目录设置为 /app。
- 公开端口
80
和443
。 - 拉取
mcr.microsoft.com/dotnet/sdk:7.0
映像并将它命名为build
。 - 将工作目录设置为 /src。
- 将 MyFrontEnd/MyFrontEnd.csproj 复制到名为 MyFrontEnd/ 的新目录中。
- 在项目中调用
dotnet restore
。 - 将根目录中的所有内容复制到映像的根目录中。
- 将工作目录设置为 /src/MyFrontEnd。
- 在项目中调用
dotnet build
。- 将 Release 配置和输出定位到 /app/build。
- 从现有的
build
基础映像初始化新的生成阶段,并将它命名为publish
。 - 在项目中调用
dotnet publish
。- 将 Release 配置和输出定位到 /app/publish。
- 从现有的
publish
基础映像初始化新的生成阶段,并将它命名为final
。 - 将工作目录设置为 /app。
- 将
/app/publish
目录从publish
映像复制到final
映像的根目录中。 - 将入口点设置为到
dotnet
的映像,并将MyFrontEnd.dll
作为参数传递。
- 拉取
在
MyBackEnd
Web API 项目中,右键单击项目节点,选择“添加”>“容器业务流程协调程序支持...”。选择“Docker Compose”,然后再次选择“Linux”作为目标 OS。在 MyBackEnd 项目目录的根目录中,创建了一个新的 Dockerfile。 此 Dockerfile 包含以下命令:
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base WORKDIR /app EXPOSE 80 FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build WORKDIR /src COPY ["MyBackEnd/MyBackEnd.csproj", "MyBackEnd/"] RUN dotnet restore "MyBackEnd/MyBackEnd.csproj" COPY . . WORKDIR "/src/MyBackEnd" RUN dotnet build "MyBackEnd.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "MyBackEnd.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "MyBackEnd.dll"]
再次打开 docker-compose.yml 文件,并检查它的内容。 Visual Studio 已更新了 Docker Compose 文件。 现在,这两项服务都包含在内:
version: '3.4' services: myfrontend: image: ${DOCKER_REGISTRY-}myfrontend build: context: . dockerfile: MyFrontEnd/Dockerfile mybackend: image: ${DOCKER_REGISTRY-}mybackend build: context: . dockerfile: MyBackEnd/Dockerfile
若要在容器化应用程序中使用 Dapr 构建块,需要将 Dapr 挎斗容器添加到 Compose 文件。 请谨慎地将 docker-compose.yml 文件的内容更新为与以下示例相匹配。 请密切注意格式和间距,切勿使用制表符。
version: '3.4' services: myfrontend: image: ${DOCKER_REGISTRY-}myfrontend build: context: . dockerfile: MyFrontEnd/Dockerfile ports: - "51000:50001" myfrontend-dapr: image: "daprio/daprd:latest" command: [ "./daprd", "-app-id", "MyFrontEnd", "-app-port", "80" ] depends_on: - myfrontend network_mode: "service:myfrontend" mybackend: image: ${DOCKER_REGISTRY-}mybackend build: context: . dockerfile: MyBackEnd/Dockerfile ports: - "52000:50001" mybackend-dapr: image: "daprio/daprd:latest" command: [ "./daprd", "-app-id", "MyBackEnd", "-app-port", "80" ] depends_on: - mybackend network_mode: "service:mybackend"
在更新后的文件中,我们分别为
myfrontend
和mybackend
服务添加了myfrontend-dapr
和mybackend-dapr
挎斗。 在更新后的文件中,请密切注意以下更改:- 挎斗使用了
daprio/daprd:latest
容器映像。 不建议将latest
标记用于生产方案。 对于生产,最好的做法是使用特定的版本号。 - 出于网络隔离目的,Compose 文件中定义的每个服务都有其自己的网络命名空间。 挎斗使用
network_mode: "service:..."
确保它们在与应用程序所在的同一网络命名空间中运行。 这样做能够使挎斗和应用程序使用localhost
进行通信。 - 必须公开 Dapr 挎斗在上面侦听 gRPC 通信的端口(默认为 50001),从而使挎斗能够彼此通信。
- 挎斗使用了
运行解决方案(按 F5 或 Ctrl+F5)以验证它是否按预期正常工作。 如果一切配置正确,你应该会看到天气预报数据:
通过使用 Docker Compose 和 Visual Studio 在本地运行,可以将断点和调试设置到应用程序中。 对于生产方案,建议在 Kubernetes 中托管应用程序。 本书包括随附的参考应用程序 eShopOnDapr,其中包含要部署到 Kubernetes 的脚本。
若要详细了解有关本演练中使用的 Dapr 服务调用构建块,请参阅第 6 章。
摘要
在本章中,你有机会体验了使用 Dapr。 通过使用 Dapr .NET SDK,了解了 Dapr 如何与 .NET 应用程序平台相集成。
第一个示例是一个使用 Dapr 状态管理构建块的简单的有状态 .NET 控制台应用程序。
第二个示例涉及到在 Docker 中运行的一个多容器应用程序。 通过将 Visual Studio 与 Docker Compose 结合使用,可以在所有 .NET 应用中使用熟悉的 F5 调试体验。
我们还深入介绍了 Dapr 组件配置文件。 它们配置由 Dapr 构建块使用的实际基础结构实现。 可以使用命名空间和范围将组件访问限制为特定的环境和应用程序。
在接下来的章节中,你将深入了解 Dapr 提供的构建块。