新文章 网摘 文章 随笔 日记

Dapr for .NET-3 入门示例

文章转自:https://zhuanlan.zhihu.com/p/455475697

Dapr for .NET-3 入门示例

Dapr for .NET-3 入门示例

 
目录
收起
在本地环境安装Dapr
1、安装Dapr CLI
2、安装 Docker Desktop
3、初始化Dapr:Initialize Dapr
4、安装 .NET 6 SDK
构建第一个Dapr应用
创建一个Console应用
使用Dapr的状态管理构建块
组件配置文件
构建多容器的dapr应用
创建应用
添加dapr服务调用
添加容器支持
总结

本章指引你在本地环境安装Dapr并且开发两个应用程序。

在本地环境安装Dapr

从这一步开始,将逐步指导你在本地环境安装Dapr,并且,运行两个Dapr应用,在self-hosted mode这种模式下。

我的本地开发环境是一个Win11的操作系统,所以下面的所有操作都是假设你的开发环境是Win10/11。另外,假设你会使用Docker。

1、安装Dapr CLI

通过该链接的介绍来安装Dapr。安装过程会产生两个目录。第一个目录是Dapr Cli的目录,默认是在C:/dapr下面,由于这个目录里面有Dapr cli,所以需要将这个目录放到环境变量path中:

不过在安装的过程中会自动将这个路径放到Path环境变量下。如果你安装好之后Dapr命令还是不能识别,那你就要检查一下看看是否需要手动来讲它放到Path环境变量下面了。

第二个目录是%USERPROFILE%/.dapr。%USERPROFILE%这个路径比较特别,它是用户配置的根目录,假如在你的电脑cmd中输入start %userprofile%/.dapr的话,则会打开这个目录,你会看到

bin路径下保存着两个dapr的可执行文件,一个是daprd,是dapr的runtime,另一个是dashboard,用于开启dapr的仪表板。

components目录下放置组件等内容的配置,默认有一个statesstore.yaml和一个pubsub.yaml。

config.yaml文件是dapr的配置文件,默认会配置zipkin作为链路追踪。

2、安装 Docker Desktop

这个属于Docker的安装和使用了。不会的同学应该先学习这个。Windows下面的话需要确保Docker使用的是Linux Container。

3、初始化Dapr:Initialize Dapr

这个过程会帮助你自动安装Dapr的最新的二进制文件并运行必要的Docker容器。

4、安装 .NET 6 SDK

上述过程执行完成之后,我们就来进行下一步,开发我们的Dapr应用。

构建第一个Dapr应用

我们的第一个Dapr应用是一个Console应用,在这个Console应用中,我们操纵Dapr的状态管理构建块,下面我们来一步一步的开始:

创建一个Console应用

打开一个命令终端,输入下面的命令来创建一个Console应用:

dotnet new console -n DaprCounter

这行命令会创建一个Console应用,接着,我们cd到DaprCounter下面:

cd DaprCounter

使用Dapr的状态管理构建块

我们的目标是在第一个Console应用上面使用Dapr状态管理构建块,使我们的Console应用变成一个具有状态的应用。为了达成这一目标,我们需要在Console应用中引入适用于.net的Dapr SDK:

dotnet add package dapr.client

引入dapr sdk后,打开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);
}

更新后的代码包含了如下的步骤:

  1. 首先初始化了一个DaprClient的实例,这个实例能够让你的代码与Dapr Sidecar进行沟通
  2. DaprClient.GetStateAsync从状态存储中用counter这个key来获取一个值。如果这个key不存在,那么会返回一个默认的值(int的默认值是0)
  3. 之后的操作进入一个循环中,不断的打印从状态存储中拿到的这个值,并将这个最新值保存在状态存储中。

Dapr CLI的run命令启动应用程序。 它调用底层的Dapr运行时,并允许应用程序和Dapr sidecar一起运行。 如果省略app-id, Dapr将为应用程序生成一个惟一的名称。 命令的最后一部分,dotnet run,指示Dapr运行时运行.net应用程序。

确保一定不要省略--app-id这个参数。否则,下次重启应用时,又会重新自动生成一个id,导致你不能访问到之前保存的状态。

然后用下面这个命令启动应用:

dapr run --app-id DaprCounter dotnet run

启动后你将会看到控制台的输出:

故意将应用停止并重新启动后,你会看到状态的值不是从0开始,而是继续之前停止的时候进行显示:

理解样例应用程序与预配置的状态组件通信,但对它没有直接依赖关系,这一点很重要。 Dapr抽象出依赖项。 你很快就会看到,底层的状态存储组件可以通过简单的配置更新进行更改。

你也许很好奇state store是个啥?

组件配置文件

当你第一次在本地环境下面初始化dapr后,会自动提供一个Redis的docker容器,然后dapr会将这个Redis容器使用组件配置文件(component configration file)配置成为一个默认的状态存储。这个组件配置文件名字我们刚才提到过,叫做statestore.yaml。内容如下

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文件夹。

我们注意看一下组件配置文件的格式:

  1. 每一个组件都有一个名字,比如上面yaml中的statestore,在我们文章开始的Program.cs的代码中,使用了这个名字告诉Sidecar要使用哪一个组件。
  2. 每一个组件配置文件都有一个spec,它包含一个type字段用于指定组件的类型,version字段指定组件的版本,metadata字段包含了组件所必须的信息,例如连接字符串或其他设置。metadata字段的值将因组件的不同类型而不同。

一个dapr sidecar能够消费任何一个在dapr中配置好的组件。但是,如果您有一个架构上的理由来限制组件的可访问性,那会怎么样呢? 如何将Redis组件限制为只在生产环境中运行的Dapr sidecars才能消费 ?

要做到这一点,你需要为生产环境定义一个namespace。你可以将它命名为production。在自托管模式(self-hosted)中,可以通过设置namespace环境变量来指定Dapr sidecar的名称空间。 配置后,Dapr sidecar将只加载与名称空间匹配的组件。 对于Kubernetes部署,Kubernetes名称空间决定加载的组件。 下面的示例显示了放置在生产命名空间中的Redis组件。 注意元数据元素中的命名空间声明:

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"
具有namespace定义的组件只能被运行在相同名称空间中的应用程序访问。 如果Dapr应用程序无法加载组件,请确保应用程序名称空间与组件名称空间匹配。 在自托管模式(应用程序名称空间存储在NAMESPCE环境变量中)中,这可能特别棘手。

如果有需要,你可能会将一个组件仅仅由几个特定的应用访问,比如在production的namespace下,你可能仅仅想要DaprCounter这个应用来访问Redis cache,你可以通过指定scopes来达到这个目的。下面的例子展示了如何在production的命名空间下限制Redis状态存储组件只能被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应用

在第一个示例中,创建了一个简单的. net控制台应用程序,它与一个Dapr sidecar并行运行。 然而,现代的分布式应用程序通常由许多活动部件组成。 它们可以同时运行独立的微服务。 这些现代应用程序通常是容器化的,需要容器编排工具,如Docker Compose或Kubernetes。

下面的例子创建了一个多容器的应用,你将在服务间使用dapr的服务调用(service invoke)构建块进行通信,该解决方案将由一个web应用程序组成,该应用程序可以从web API中检索天气预报。 它们(web应用和web api应用)将各自在Docker容器中运行。 你将使用Docker Compose在本地运行容器并启用调试功能。

创建应用

1、前端应用

创建一个 core web app应用,我们要使用razor模板

项目名称为MyFrontEnd,解决方案名称为DaprMultiContainer。

然后点击下一步,创建,我把HTTPS和Docker支持全部取消勾选了。

2、后端应用

继续创建一个 core web api应用,当作后端应用。

将项目命名为MyBackEnd.

默认情况下,Dapr sidecar依赖于网络边界来限制对其公共API的访问。 因此,清除“配置HTTPS”的复选框:

如果选中了“Configure for HTTPS”复选框,则生成的 Core API项目包括中间件来重定向客户端请求到HTTPS端点。 这将中断Dapr sidecar和应用程序之间的通信,除非你在运行Dapr应用程序时显式地配置HTTPS的使用。 要使Dapr sidecar能够通过HTTPS进行通信,请在Dapr run命令中包含--app-ssl标志以启动应用程序。 还要使用--app-port参数指定HTTPS端口。 本演练的其余部分使用sidecar和应用程序之间的纯HTTP通信,并要求你清除Configure for HTTPS复选框。

这一步完成后,你的整体项目看起来是这样的:

添加dapr服务调用

现在,你需要在服务中添加dapr的服务调用构建块。使其能够从MyFrontEnd项目中检索MyBackEnd项目中的天气预报数据。dapr的服务调用构建块带来了很多好处,如服务发现、自动重试、消息加密以及改进的可观测性。

你将会使用Dapr的.net SDK在sidecar上进行服务调用。

1、首先,为MyFrontEnd项目添加dapr sdk包:

dotnet add package dapr.aspnetcore

2、在MyFrontEnd项目的Program文件中,添加对DaprClient的服务注册:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddDaprClient();
builder.Services.AddRazorPages();

// ...

AddDaprClient的调用将DaprClient注入到asp.net core的依赖注入容器中,然后,你可以在你的代码中注入DaprClient,用来和dapr sidecar、构建块以及组件进行通讯。

3、在MyFrontEnd项目中创建一个新的类WeatherForecast:

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;
} 

4、打开MyFrontEnd项目的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;
    }
}

通过将DaprClent注入到IndexModel的构造函数中,你获取了Dapr的能力。在OnGet方法中,你通过dapr的服务调用构建块调用了后台api服务。而OnGet方法会在用户访问首页的时候被调用。

你使用了DaprClient的InvokeMethodAsync来调用了MyBackEnd服务的weatherforecast方法,你需要将MyBackEnd这个名称配置成为你的web api在Dapr中的app id(下面会演示)。

5、在MyFrontEnd项目中将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://docs.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>

添加容器支持

1、右键点击MyFrontEnd项目,添加>>>容器业务流程协调程序支持...,过程中间选择Docker Compose以及linux作为目标OS。

然后vs会为你在解决方案中创建一个docker-compose.yml:

docker-compose.yml内容如下:

version: '3.4'

services:
myfrontend:
    image: ${DOCKER_REGISTRY-}myfrontend
    build:
    context: .
    dockerfile: MyFrontEnd/Dockerfile

.dockerignore文件包含了一些你不想要docker添加到容器中的文件类型和扩展名。这些文件通常是和开发环境相关的,不需要部署到生产环境中。

而在MyFrontEnd项目根目录中,生成了一个新的DockerFile文件。一个DockerFile文件由一些列用于构建镜像的命令组成。你可以参考这里:Dockerfile reference

DockerFile文件包含如下内容:

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:6.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调用时依次执行以下步骤:

a)拉取镜像并将它命名为base

b)将工作目录设置为/app

c)暴露80和443端口

d)拉取镜像并将其命名为build

e)将工作目录设置为/src。注意workdir需要在多阶段构件中重复设置,因为前一阶段构建声明的变量在另一个阶段是用不了的。

f)将MyFrontEnd/MyFrontEnd.csproj拷贝到/src/MyFrontEnd/这个目录下。

g)在/src/MyFrontEnd/MyFrontEnd.csproj工程文件上执行dotnet restore。

h)将所有内容拷贝到/src下

i)将工作目录设定为/src/MyFrontEnd。

j)在工程文件上执行dotnet build,将构建好的文件输出到/app/build

k)通过现有的build基础镜像初始化一个新的构建阶段,并将其命名为publish。

l)执行dotnet publish,并将发布后的内容存放到/app/publish

m)从现有的base基础镜像初始化一个新的构建阶段,并将其命名为final

n)将工作目录设定为/app

o)从publish基础镜像的/app/publish目录中将内容拷贝到final镜像中的根目录

p)将镜像入口点设置为dotnet,并将MyFrontEnd.dll作为参数传入。

2、右键点击MyBackEnd的web api项目,以同样的方式添加>>>容器业务流程协调程序支持...,过程中间选择Docker Compose以及linux作为目标OS。

这个操作同样会在MyBackEnd项目中产生一个DockerFIle:

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80

FROM mcr.microsoft.com/dotnet/sdk:6.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文件,检查它的内容。发现vs已经更新了它的内容:

version: '3.4'

services:
  myfrontend:
    image: ${DOCKER_REGISTRY-}myfrontend
    build:
      context: .
      dockerfile: MyFrontEnd/Dockerfile

  mybackend:
    image: ${DOCKER_REGISTRY-}mybackend
    build:
      context: .
      dockerfile: MyBackEnd/Dockerfile

3、要想在一个容器化的应用中使用dapr的构建块,你需要在docker-compose.yml文件中添加dapr sidecar的容器支持。

按照如下方式更新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"

在更新的文件中,我们分别向myfronend和mybackend这两个服务添加了myfronend-dapr和mybackend-dapr这两个sidecar。我们需要注意以下几个事项:

  • sidecar使用了daprio/daprd:latest这个镜像。latest在生产环境中不推荐使用,应该使用一个特定的版本号。
  • 作为网络隔离的目的,每一个定义在docker-compose文件中的服务都有自己的network namespace。sidecar使用network_mode: "service:....."来保证自己的network和相关联的应用存在于一个network namespace中。这样做就可以保证sidecar和应用能够基于localhost进行通讯。
  • Dapr sidecars监听gRPC通信(默认情况下为50001)的端口必须公开,以允许sidecars彼此通信。

点击运行(或者F5),如果没有问题,将会显示如下页面:

总结

通过使用Dapr . net SDK,了解了Dapr如何与. net应用程序平台集成。

第一个示例是一个简单的、有状态的. net控制台应用程序,它使用了Dapr状态管理构建块。

第二个例子涉及一个在Docker中运行的多容器应用程序。 通过使用Visual Studio和Docker Compose,可以在所有.net应用程序中体验到熟悉的F5调试体验。

此外,还进一步了解了Dapr组件配置文件。 它们配置Dapr构建块使用的实际基础设施实现。 可以使用名称空间和作用域来限制组件对特定环境和应用程序的访问。

在接下来的章节中,将深入研究Dapr提供的构建模块。

编辑于 2022-01-11 15:50
C#
Dapr
分布式系统
posted @ 2023-05-24 15:53  岭南春  阅读(41)  评论(0)    收藏  举报