Administrator
发布于 2025-01-06 / 14 阅读
0
0

Dapr教程

参考:https://docs.dapr.io/zh-hans/concepts

安装脚手架

# Set-ExecutionPolicy
Set-ExecutionPolicy RemoteSigned
​
# 安装choco包管理工具
iwr https://chocolatey.org/install.ps1 -UseBasicParsing | iex
​
# 查看choco版本
choco -v
​
# 安装helm命令
choco install kubernetes-helm
​
# 查看helm帮助
helm -h
​
# 查看helm版本
helm version
​
# windows
powershell -Command "iwr -useb https://raw.githubusercontent.com/dapr/cli/master/install/install.ps1 | iex"
​
# linux
wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash
​
# 混合集群参考:https://docs.dapr.io/zh-hans/operations/hosting/kubernetes/kubernetes-hybrid-clusters/
​
# 验证
dapr --version
​
# 初始化
dapr init
# 初始化(k8s环境)
dapr init -k
​
# 查看dapr状态
dapr status -k
​
# 查看已注册的应用
dapr list
dapr list -k
​
# 启动仪表盘
dapr dashboard
dapr dashboard -k
​
# 运行空 sidecar
dapr run --app-id myapp
# 运行空 dotnet程序
dapr run --app-id myapp --app-port 5000 -- dotnet run
​
# 卸载
dapr uninstall
dapr uninstall --all #卸载并删除 .dapr 目录、Redis、Placement 和 Zipkin 容器
dapr uninstall -k #卸载k8s集群dapr
​
# 帮助手册
dapr help {command}
​
# 更多命令:https://docs.dapr.io/zh-hans/reference/cli/

组件

组件schema

格式

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: [COMPONENT-NAME]
  namespace: [COMPONENT-NAMESPACE]
spec:
  type: [COMPONENT-TYPE]
  version: v1
  initTimeout: [TIMEOUT-DURATION]
  ignoreErrors: [BOOLEAN]
  metadata:
  - name: [METADATA-NAME]
    value: [METADATA-VALUE]

字段说明

字段

必填

详情

Example

apiVersion

Y

您正在调用的Dapr版本(如果适用的话为 Kubernetes) API

dapr.io/v1alpha1

kind

Y

CRD的类型。 组件必须始终是 Component

Component (组件)

metadata

-

有关组件注册的信息

metadata.name

Y

组件的名称

prod-statestore

metadata.namespace

N

主机环境的命名空间

myapp-namespace

spec

-

关于组件资源的详细信息

spec.type

Y

组件类型

state.redis

spec.version

Y

组件版本

v1

spec.initTimeout

组件初始化的超时时间 默认为30秒

5m, 1h, 20s

spec.ignoreErrors

如果组件加载失败,请告诉Dapr sidecar 继续初始化。 默认为 false

false

spec.metadata

-

一个组件特定配置的键/值。 查看你的组件字段定义

存储密钥(示例)

# 创建目录
mkdir my-components
​
# 创建json文件 mysecrets.json
{
   "my-secret" : "I'm Batman"
}
​
# 在此目录内创建yml文件 localSecretStore.yaml
## type: secretstores.local.file 字段值,其告诉Dapr使用本地文件组件作为密钥存储
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: my-secret-store
  namespace: default
spec:
  type: secretstores.local.file
  version: v1
  metadata:
  - name: secretsFile
    value: <PATH TO SECRETS FILE>/mysecrets.json
  - name: nestedSeparator
    value: ":"
​
kubectl apply -f localSecretStore.yaml
​
# 运行Dapr sidecar
dapr run --app-id myapp --dapr-http-port 3500 --components-path ./my-components
​
# 获取密钥
curl http://localhost:3500/v1.0/secrets/my-secret-store/my-secret

Redis(示例)

# 参考:https://zhuanlan.zhihu.com/p/611915800
​
# 部署redis
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm install redis bitnami/redis --namespace store --create-namespace --set replica.replicaCount=0 --set auth.password=******
​
# 创建yml文件 redis-state.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"
​
​
# 在密钥存储中创建密钥,并在组件定义中引用它
## 创建密钥
kubectl create secret generic redis-secret --from-literal=redis-password='******'
​
## 使用密钥
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
spec:
  type: state.redis
  version: v1
  metadata:
  # These settings will work out of the box if you use `helm install
  # bitnami/redis`.  If you have your own setup, replace
  # `redis-master:6379` with your own Redis master address, and the
  # Redis password with your own Secret's name. For more information,
  # see https://docs.dapr.io/operations/components/component-secrets .
  - name: redisHost
    value: redis-master:6379
  - name: redisPassword
    secretKeyRef:
      name: redis
      key: redis-password
  - name: actorStateStore
    value: "true"
auth:
  secretStore: kubernetes  # 指定秘钥存储的类型是kubernetes
  
# 创建Component
kubectl apply -f redis-state.yaml

状态管理

# 监听空应用程序 myapp
dapr run --app-id myapp --dapr-http-port 3500
​
# 保存状态
curl -X POST -H "Content-Type: application/json" -d '[{ "key": "name", "value": "Bruce Wayne"}]' http://localhost:3500/v1.0/state/statestore
​
# 获取状态
curl http://localhost:3500/v1.0/state/statestore/name
"Bruce Wayne"
​
# 查看redis数据
docker exec -it dapr_redis redis-cli
hgetall "myapp||name"
​
# dapr-dotnet-sdk
## 保存状态
public abstract Task<bool> TrySaveStateAsync(string storeName, string key, TValue value, string etag, StateOptions stateOptions = null, IReadOnlyDictionary<string, string> metadata = null, CancellationToken cancellationToken = default(CancellationToken));
## 获取状态
public abstract Task<TValue> GetStateAsync<TValue>(string storeName, string key, ConsistencyMode? consistencyMode = default, IReadOnlyDictionary<string, string> metadata = default, CancellationToken cancellationToken = default)

发布订阅

参考:https://blog.csdn.net/ChaITSimpleLove/article/details/123791744 
​
// 发布
 [HttpPost("DemoDaprEventPub")]
 public async Task DemoDaprEventPubAsync()
 {
    CancellationTokenSource source = new CancellationTokenSource();
    CancellationToken cancellationToken = source.Token;
    using var client = new DaprClientBuilder().Build();
    //Using Dapr SDK to publish a topic
    var data = new
    {
        ...
    };
    await client.PublishEventAsync("pubsub", "DemoDaprEvent", data, cancellationToken);
 }
 
 
 // 订阅
 app.MapSubscribeHandler(); //Program.cs添加
​
 // 订阅代码(编码式订阅)
 [Topic("pubsub", "DemoDaprEvent")]
 [HttpPost("DemoDaprEvent")]
 public async Task<IActionResult> DemoDaprEventAsync([FromBody] JsonObject data)
 {
    ...
 }
​
//dapr-cli发布消息
dapr publish --publish-app-id b-api --topic DemoDaprEvent --pubsub pubsub --data '{"msg":"hello"}'
​
//消息格式如下(发布的消息在data字段)
{
    "data": {
        "msg": "hello"
    },
    "datacontenttype": "application/json",
    "id": "982954ae-295b-4365-8b61-93eba80aed3b",
    "pubsubname": "pubsub",
    "source": "a-api",
    "specversion": "1.0",
    "time": "2023-08-02T10:28:44+08:00",
    "topic": "DemoDaprEvent",
    "traceid": "00-f8a9e5b07502462d63448e23d8309269-e14b2b2721ded571-01",
    "traceparent": "00-f8a9e5b07502462d63448e23d8309269-e14b2b2721ded571-01",
    "tracestate": "",
    "type": "com.dapr.event.sent"
}

注意:多个实例订阅时,同一条消息只会发送某一个,而不是所有的实例(亲测), redis:publish之后所有客户端都会收到同一条消息

分布式锁

参考:http://img.tnblog.net/hb/article/details/7647
​
// components 目录下创建 lockstore.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: lockstore
spec:
  type: lock.redis
  metadata:
  - name: redisHost
    value: localhost:6379
  - name: redisPassword
    value: ""
​
// 创建锁
abstract Task<TryLockResponse> Lock(string storeName,string resourceId,string lockOwner,Int32 expiryInSeconds,CancellationToken cancellationToken = default);
​
// 释放锁
abstract Task<UnlockResponse> Unlock(string storeName,string resourceId,string lockOwner,CancellationToken cancellationToken = default);
​
// 启动项目(指定components目录)
dapr run --app-id locker --dapr-grpc-port 50001 --components-path ./components -- dotnet run

Actor

// 定义一个actor
public class UserActor : Dapr.Actors.Runtime.Actor, IDemoActor
{
    public UserActor(ActorHost host) : base(host){}
    ...
}
​
// 使用actor
public class Demo
{
    private readonly IActorProxyFactory _actorProxyFactory;
    public Demo(IActorProxyFactory actorProxyFactory)
    {
        _actorProxyFactory = actorProxyFactory;
    }
    var actorId = new ActorId(userId);
    var actor = _actorProxyFactory.CreateActorProxy<IUserActor>(actorId, UserActor);
    actor.XXX();
}
​
// 注册Timer(用于当前actor的job任务)
async Task<ActorTimer> RegisterTimerAsync(string timerName,string callback,byte[] callbackParams,TimeSpan dueTime,TimeSpan period)
async Task<ActorTimer> RegisterTimerAsync(string timerName,string callback,byte[] callbackParams,TimeSpan dueTime,TimeSpan period,TimeSpan ttl)
// 取消Timer
async Task UnregisterTimerAsync(string timerName)
    
// Reminders 是一种在指定时间内触发 persistent 回调的机制。
// 它们的功能类似于 timer。
// 但与 timer 不同,在所有情况下 reminders 都会触发,
// 直到 actor 显式取消注册 reminders 或删除 actor 。
// 具体而言, reminders 会在所有 actor 失活和故障时也会触发触发,
// 因为Dapr Actors 运行时会将 reminders 信息持久化到 Dapr Actors 状态提供者中。
interface IRemindable
Task ReceiveReminderAsync(string reminderName, byte[] state, TimeSpan dueTime, TimeSpan period);
​
// 只有当actor被激活时,计时器(timer)才会保持活动状态。 计时器不会重置空闲计时器[2],因此它们不能让参与者自己处于活动状态。
// 提醒器比actor活得长[3]。 如果actor被禁用,一个提醒器可以重新激活该actor。 提醒器将重置空闲计时器。

服务间通信

# 启动服务A(调用方)
dapr run --app-id a-api --app-port 5084 --app-protocol http --log-level debug -- dotnet run --no-build
# 启动服务B(被调用方)
dapr run --app-id b-api --app-port 5264 --app-protocol http --log-level debug -- dotnet run --no-build
​
# 服务B暴露的方法
[HttpGet("/User/{userId}/balance")]
public async Task<IActionResult> Index(string userId)
{
    var actorId = new ActorId(userId);
    var actor = _actorProxyFactory.CreateActorProxy<IUserActor>(actorId, nameof(UserActor));//_actorProxyFactory为ActorProxyFactory注入的私有变量
    var result = await actor.GetBalance();
    return Ok(result);
}
​
# dapr-cli调用
dapr invoke --app-id b-api --method /User/1/balance --verb GET
​
​
# 服务A请求服务B的方法
[HttpGet("{userId}")]
public async Task<IActionResult> Index(string userId)
{
    var result = await _daprClient.InvokeMethodAsync<decimal>(HttpMethod.Get, "b-api", $"/User/{userId}/balance");
    return Ok(result);
}
​
# dapr-cli调用
dapr invoke --app-id a-api --method /User/1/balance --verb GET

调试

VS调试

参考:https://www.cnblogs.com/bhfdz/p/16649283.html

安装扩展

命令: dotnet tool install --global PowerShell

Microsoft Child Process Debugging Power ToolMicrosoft Child Process Debugging Power Tool 2022

修改launchSettings.json

"dapr": {
    "commandName": "Executable",
    "executablePath": "pwsh",
    "commandLineArgs": "-Command \"dapr run --app-id a-api --app-port 5084 --app-protocol http --log-level debug -- dotnet run --no-build\"",
    "workingDirectory": ".",
    "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
    },
    "nativeDebugging": true,
    "dotnetRunMessages": true,
    "applicationUrl": "http://localhost:5084"
}

注意:app-protocol 要设置成 http,否则调试dapr通信失败

配置子进程调试

断点调试

VS Code调试

本人没有实际操作,请参考:https://docs.dapr.io/zh-hans/developing-applications/ides/vscode/

idea调试

本人没有实际操作,请参考:https://docs.dapr.io/zh-hans/developing-applications/ides/intellij/

Kubernetes 发布Dapr应用

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.serviceName }}
  namespace: {{ .Values.nameSpace }}
  labels:
    app: {{ .Values.appLabel }}
    service: {{ .Values.serviceName }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      service: {{ .Values.serviceName }}
  template:
    metadata:
      labels:
        app: {{ .Values.appLabel }}
        service: {{ .Values.serviceName }}
        logging: "true"  # 一定要具有该标签才会被采集日志
      annotations:
        dapr.io/enabled: "true"
        dapr.io/app-id: {{ .Values.serviceName }}
        dapr.io/app-port: "80"
        dapr.io/config: "daprConfig"
    spec:
      containers:
        - ...

注意:annotations的配置是关键

Kubernetes annotations规范

Annotation

说明

dapr.io/enabled

设定此参数为 true 注入Dapr sidecar到pod

dapr.io/app-port

这个参数告诉Dapr你的应用程序正在监听哪个端口。

dapr.io/app-id

应用程序唯一 ID。 用于服务发现、状态封装 和 发布/订阅 消费者ID

dapr.io/log-level

为 Dapr sidecar设置日志级别。 允许的值是debuginfowarnerror。 默认是 info

dapr.io/config

告诉 Dapr 要使用哪个配置 CRD

dapr.io/log-as-json

将此参数设置为true以JSON格式输出日志。 默认值为 false.

dapr.io/enable-profiling

设置此参数为 true 在端口 7777 上启动Dapr分析服务器。 默认值为 false.

dapr.io/api-token-secret

告诉Dapr使用哪个Kubernetes密钥来进行基于令牌的API认证。 默认情况下未设置。

dapr.io/app-protocol

告诉 Dapr 你的应用程序正在使用哪种协议。 有效选项是 http and grpc。 Default is http

dapr.io/app-max-concurrency

限制应用程序的并发量。 有效的数值是大于 0

dapr.io/app-ssl

告诉Dapr通过不安全的SSL连接调用应用程序。 同时适用于HTTP和gRPC。 Traffic between your app and the Dapr sidecar is encrypted with a certificate issued by a non-trusted certificate authority, which is considered insecure. 默认值为 false.

dapr.io/metrics-port

设置 sidecar 度量服务器的端口。 默认值为 9090

dapr.io/sidecar-cpu-limit

Dapr sidecar可以使用的最大CPU数量。 请参阅 此处 的有效值。 默认情况下未设置

dapr.io/sidecar-memory-limit

Dapr sidecar可以使用的最大内存量。 请参阅 此处 的有效值。 默认情况下未设置

dapr.io/sidecar-cpu-request

Dapr sidecar要求的 CPU 数量。 请参阅 此处 的有效值。 默认情况下未设置

dapr.io/sidecar-memory-request

Dapr sidecar 请求的内存数量。请参阅 此处 的有效值。 默认情况下未设置

dapr.io/sidecar-liveness-probe-delay-seconds

Sidecar容器启动后的秒数,然后才启动活度探测。 在 此处 阅读更多 默认值为 3

dapr.io/sidecar-liveness-probe-timeout-seconds

Sidecar 存活探针超时的秒数。 在 此处 阅读更多 默认值为 3

dapr.io/sidecar-liveness-probe-period-seconds

每隔多长时间(以秒为单位)进行一次 sidecar 存活探针。 在 此处 阅读更多 默认值为 6

dapr.io/sidecar-liveness-probe-threshold

当 sidecar 存活探针失败时,Kubernetes会在放弃之前尝试N次。 在这种情况下,Pod 将被标记为不健康。 在 此处 阅读更多关于 failureThreshold 。 默认值为 3

dapr.io/sidecar-readiness-probe-delay-seconds

Sidecar 容器启动后,启动准备就绪探针前的秒数。 在 此处 阅读更多 默认值为 6 默认值为 3

dapr.io/sidecar-readiness-probe-timeout-seconds

Sidecar 准备就绪探针超时的秒数。 在 此处 阅读更多 默认值为 3

dapr.io/sidecar-readiness-probe-period-seconds

每个多长时间(以秒为单位)进行一次 sidecar 准备就绪探针。 在 此处 阅读更多 默认值为 6

dapr.io/sidecar-readiness-probe-threshold

当 sidecar 准备就绪探针失败时,Kubernetes会在放弃之前尝试N次。 在这种情况下,Pod 将被标记为未就绪。 在 此处 阅读更多关于 failureThreshold 。 默认值为 3

dapr.io/http-max-request-size

增加http和grpc服务器请求正文参数的最大大小,单位为MB,以处理大文件的上传。 默认值为 4 MB

dapr.io/env

List of environment variable to be injected into the sidecar. Strings consisting of key=value pairs separated by a comma.

日志(Fluentd、Elastic、Kibana)

参考:https://docs.dapr.io/zh-hans/operations/monitoring/logging/fluentd/

SDK支持

语言

状态

客户端 SDK

服务扩展

Actor SDK

.NET

Stable

ASP.NET Core

Python

Stable

gRPC

FastAPI Flask

Java

Stable

Spring Boot

Go

Stable

PHP

Stable

C++

In development

Rust

In development

Javascript

In development

tye启动

说明:tye工具主要是为了方便一次性启动多个微服务,提升工作效率

参考:

# 安装tye
dotnet tool install -g Microsoft.Tye --version "0.11.0-alpha.22111.1"
​
# 解决方案目录创建 tye.yaml
name: test
extensions:
- name: dapr
services:
- name: a-api
  project: test.dapr.WebApiA/test.dapr.WebApiA.csproj
  replicas: 1
  bindings:
  - port: 5084
- name: b-api
  project: test.dapr.WebApiB/test.dapr.WebApiB.csproj
  replicas: 1
  bindings:
  - port: 5264
  
# 启动项目
tye run
​
# 查看dapr中的服务注册情况
dapr list
​
# APP ID  HTTP PORT  GRPC PORT  APP PORT  COMMAND  AGE  CREATED              DAPRD PID  CLI PID  APP PID  RUN TEMPLATE PATH 
# b-api   63754      63753      63748              1h   2023-08-12 12:00.58  24476      32412    0
# a-api   63751      63750      63746              1h   2023-08-12 12:00.58  32828      32396    0

注意:非k8s环境replicas不能大于1,否则抛错

Kubernetes Job

apiVersion: batch/v1
kind: Job
metadata:
  name: job-with-shutdown
spec:
  template:
    metadata:
      annotations:
        dapr.io/enabled: "true"
        dapr.io/app-id: "with-shutdown"
    spec:
      containers:
      - name: job
        image: alpine
        command: ["/bin/sh", "-c", "apk --no-cache add curl && sleep 20 && curl -X POST localhost:3500/v1.0/shutdown"]
      restartPolicy: Never

Kubernetes Job参考https://zhuanlan.zhihu.com/p/382830573



评论