Dapr(分布式应用程序运行时)介绍

Dapr 是一个可移植的、事件驱动的运行时,它使任何开发人员能够轻松构建出弹性的、无状态和有状态的应用程序,并可运行在云平台或边缘计算中,它同时也支持多种编程语言和开发框架。

Dapr 官网:https://dapr.io/

实战 Dapr 的 Redis 发布/订阅应用

1. 创建项目

首先,我们将创建我们的项目根文件夹来托管我们将在后续步骤中创建的所有服务。

  1. mkdir dapr-nestjs-redis-pub-sub

2. 创建 Dapr Placement 服务

由于我们将创建多个服务,我们将使用 docker-compose 来运行这些服务。

让我们在项目的根文件夹中创建 docker-compose.yml 文件

  1. cd dapr-nestjs-redis-pub-sub
  2. touch docker-compose.yml
  1. version: "3.5"
  2. services:
  3. dapr-placement:
  4. image: "daprio/dapr"
  5. command: ["./placement", "-port", "50006"]

Dapr placement 服务将负责管理 Dapr actors(我们的服务)之间的所有通信。

简单来说,它负责将所有通信路由到假设接收通信的相应 actor。它充当 message broker(消息代理)。

3. 创建 Redis Publish 服务

让我们继续通过添加我们的 Redis 服务来修改我们的 docker-compose.yml 文件。

将以下代码添加到 docker-compose.yml 的服务部分:

  1. redis-publisher:
  2. image: redis
  3. restart: always
  4. depends_on:
  5. - dapr-placement

4. 创建 Dapr Pub-Sub 组件

创建一个 dapr/components 文件夹。然后创建组件文件 redis-pubsub.yaml

  1. mkdir -p dapr/components
  2. cd dapr/components
  3. touch redis-pubsub.yaml

然后打开文件并插入我们的 Dapr pub/sub 组件的详细信息

  1. apiVersion: dapr.io/v1alpha1
  2. kind: Component
  3. metadata:
  4. name: redis-pubsub
  5. namespace: default
  6. spec:
  7. type: pubsub.redis
  8. version: v1
  9. metadata:
  10. - name: redisHost
  11. value: redis-publisher:6379
  12. - name: redisPassword
  13. value: ""

redisHost 是我们的 Redis 服务 redis-pub 的名称,默认 Redis 端口为 6379

5. 创建 Redis Dapr Sidecar

正如前面部分反复提到的,服务直接与 Dapr 通信,而不是直接与其他服务通信。Dapr 充当所有服务的中间人。

服务通过它们自己的 Dapr sidecar 直接与 Dapr 通信,Dapr sidecar 将通信传递给 Dapr placement,该 placement 再次将其传递给假设接收通信的服务的 Dapr sidecar

redis-dapr-sidecar 服务添加到我们的 docker-compose.yml

  1. redis-dapr-sidecar:
  2. image: "daprio/daprd:edge"
  3. command: [
  4. "./daprd",
  5. "-app-id",
  6. "redis-publisher",
  7. "-app-port",
  8. "6379",
  9. "-dapr-http-port",
  10. "5000",
  11. "-components-path",
  12. "/components",
  13. "-placement-host-address",
  14. "dapr-placement:50006"
  15. ]
  16. volumes:
  17. - "./dapr/components/:/components"
  18. depends_on:
  19. - redis-publisher
  20. network_mode: "service:redis-publisher"

在这里,我们使用 app-idDapr sidecar 分配给 redis-publisher,同时我们使用 redis 端口 6379

我们还必须将 dapr/components(redis-pubsub.yaml) 文件夹挂载到 docker 容器中。

不要忘记声明 dapr-http-port。这是我们的 Dapr sidecarapi,允许我们调用各种 HTTP 方法。

定义您的 dapr-http-port 很重要,因为您将在此处调用各种 HTTP 调用/方法/请求

最后,注意将 redis-dapr-sidecar 附加到 redis-publisher 网络命名空间。

6. 创建 NestJS Server

我们将使用 NestJS 作为我们的 node server 作为我们的 Redis subscriber(订阅者)

进入到项目文件夹

  1. cd dapr-nestjs-redis-pub-sub

然后执行以下命令设置一个 NestJS node server:

  1. npm i -g @nestjs/cli
  2. nest new nest-subscriber

对于这个项目,我们将选择 yarn 作为包管理器。

接下来,我们将设置一个 post API 端点。

Dapr 将调用这个端点,一旦它收到我们的 Redis 服务发布,它就被调用。

转到 nest-subscriber/src/app.controller.ts

将此文件中的代码替换为以下内容:

  1. import { Controller, Post, Body } from '@nestjs/common';
  2. import { AppService } from './app.service';
  3. @Controller()
  4. export class AppController {
  5. constructor(private readonly appService: AppService) {}
  6. @Post('/redis-publisher')
  7. async postRedisPub(@Body() reqBody) {
  8. console.log(`Redis 发布了 ${JSON.stringify(reqBody)} `);
  9. return `NestJS 订阅者收到的 ${reqBody} 发布`;
  10. }
  11. }

7. 为 NestJS 订阅服务器创建 Dockerfile

我们将 NestJS 服务器作为 Docker 容器运行。需要创建一个 Dockerfile

  1. cd nest-subscriber
  2. touch Dockerfile

然后打开文件并粘贴以下代码:

  1. FROM node:16.13.0-alpine
  2. WORKDIR "/app"
  3. COPY ./nest-subscriber/package.json ./
  4. RUN yarn install
  5. COPY ./nest-subscriber .
  6. RUN yarn run build
  7. EXPOSE 3000
  8. CMD ["yarn","start:prod"]

构建镜像:

  1. docker build -f ./nest-subscriber/Dockerfile -t nest-subscriber:latest . --no-cache

8. 将 NestJS 订阅服务添加到 docker-compose 文件

在创建了我们的 NestJS 服务器和 Dockerfile 之后,我们创建了 nest-subscriber docker 服务。

将以下内容添加到 docker-compose.yml

  1. nest-subscriber:
  2. image: "nest-subscriber:latest"
  3. depends_on:
  4. - redis-publisher
  5. - dapr-placement
  6. restart: always

9. 创建 Dapr 订阅

我们将为我们的 pub/sub 订阅定义配置。

创建一个 dapr/subscriptions 文件夹。然后创建组件文件 redis-subscription.yaml

  1. mkdir -p dapr/subscriptions
  2. cd dapr/subscriptions
  3. touch redis-subscription.yaml

然后打开文件并插入我们的 Dapr 订阅组件的详细信息

  1. apiVersion: dapr.io/v1alpha1
  2. kind: Subscription
  3. metadata:
  4. name: nest-redis-sub
  5. spec:
  6. topic: nest-redis-pub-topic
  7. route: /redis-publisher
  8. pubsubname: redis-pubsub
  9. scopes:
  10. - nest-subscriber

路由是发布 topicDapr 将调用的 API

scope 是订阅该 topic 的服务。

pubsubnameredis-pubsub,它等于我们的 redis-pubsub.yaml 文件中定义的元数据名称。

在这个项目中,如果发布了一个 topic nest-redis-pub-topic,Dapr 将在我们的 nest-subscriber 服务中调用 API /redis-publisher

10. 创建 NestJS 服务器 Dapr Sidecar

我们需要为我们的 NestJS 服务创建一个 sidecar,就像 redis-publisher 服务一样。

nest-subscriber-dapr-sidecar 服务添加到我们的 docker-compose.yml

  1. nest-subscriber-dapr-sidecar:
  2. image: "daprio/daprd:edge"
  3. command: [
  4. "./daprd",
  5. "-app-id",
  6. "nest-subscriber",
  7. "-app-port",
  8. "3000",
  9. "-components-path",
  10. "/components",
  11. "-placement-host-address",
  12. "dapr-placement:50006",
  13. ]
  14. volumes:
  15. - "./dapr/components/:/components"
  16. depends_on:
  17. - nest-subscriber
  18. network_mode: "service:nest-subscriber"

11. 测试它是否有效

通常 Dapr Docker 容器会在 Docker 网络中进行通信。

但是为了我们做测试,我们将打开映射暴露端口 5000 到我们的本地机器 5001

  1. redis-publisher:
  2. image: redis
  3. depends_on:
  4. - dapr-placement
  5. restart: always
  6. ports:
  7. - 5001:5000

然后在您的终端中执行以下命令:

  1. curl --location --request POST 'http://localhost:5001/v1.0/publish/redis-pubsub/nest-redis-pub-topic' \
  2. --header 'Content-Type: application/json' \
  3. --data-raw '{
  4. "hello": "world"
  5. }'

Dapr 的优点之一是它遵循特定的 URL 格式。这里我们只使用 Dapr sidecar HTTP 端口(5001),然后是版本号(v1.0),然后是 action(publish)。然后是我们 redis-pubsub.yaml 配置文件中定义的 pubsubnameredis-pubsub)和 topicnest-redis-pub-topic)。

一旦发出 HTTP post 请求。我们的 NestJS 服务器应该在 /redis-publisher 收到一个 post 请求,这将导致以下日志:

我们可以看到它正在通过 Dapr 接收 Redis 发布。但是我们的 NestJS 服务器无法正确处理消息。

只有 {} 被发布,而不是我们发布的消息。

我们将在下一步中解决这个问题。

注意:我们通过 redis-dapr-sidecardapr-http-port 调用发布服务。通常会有一个单独的 Docker 服务(例如另一个服务器),它有自己的 Dapr sidecar,它将调用 redis 发布服务。 在这种情况下,我们将使用该 Docker 服务的 Dapr sidecar http-port。该请求将由 sidecar 发送到 Dapr placement 服务,然后该服务将确定将请求转发到的正确 Dapr sidecar

12. 允许 NestJS 解析 application/cloudevents+json

我们的 nest-subscriber-dapr-sidecar 向我们的 nest-subscriber 服务器发出的 post 请求的 Content-Type 将是 application/cloudevents+json 而不是 application/json

目前我们的 NestJS 服务器无法解析 application/cloudevents+json

为了解决这个问题,我们首先需要安装 body-parser

  1. cd nest-subscriber
  2. yarn add body-parser

接下来我们需要修改我们的 NestJS 服务器 main.ts

  1. import { NestFactory } from '@nestjs/core';
  2. import { AppModule } from './app.module';
  3. import * as bodyParser from 'body-parser';
  4. async function bootstrap() {
  5. const app = await NestFactory.create(AppModule);
  6. app.use(bodyParser.json({ type: 'application/cloudevents+json' }));
  7. app.use(bodyParser.json());
  8. await app.listen(3000);
  9. }
  10. bootstrap();

当我们再次发送 post 请求时,我们的 NestJS 服务器将能够处理请求正文并显示以下日志:

好了,我们现在有一个基于 Dapr 工作的 Redis Pub/Sub 分布式应用。

13. 完整 docker-compose.yaml

  1. version: "3.5"
  2. services:
  3. dapr-placement:
  4. image: "daprio/dapr"
  5. command: ["./placement", "-port", "50006"]
  6. redis-publisher:
  7. image: redis
  8. depends_on:
  9. - dapr-placement
  10. restart: always
  11. ports:
  12. - 5001:5000
  13. redis-dapr-sidecar:
  14. image: "daprio/daprd:edge"
  15. command: [
  16. "./daprd",
  17. "-app-id",
  18. "redis-publisher",
  19. "-app-port",
  20. "6379",
  21. "-dapr-http-port",
  22. "5000",
  23. "-components-path",
  24. "/components",
  25. "-placement-host-address",
  26. "dapr-placement:50006"
  27. ]
  28. volumes:
  29. - "./dapr/components/:/components"
  30. depends_on:
  31. - redis-publisher
  32. network_mode: "service:redis-publisher"
  33. nest-subscriber:
  34. image: "nest-subscriber:latest"
  35. depends_on:
  36. - redis-publisher
  37. - dapr-placement
  38. restart: always
  39. nest-subscriber-dapr-sidecar:
  40. image: "daprio/daprd:edge"
  41. command: [
  42. "./daprd",
  43. "-app-id",
  44. "nest-subscriber",
  45. "-app-port",
  46. "3000",
  47. "-components-path",
  48. "/components",
  49. "-placement-host-address",
  50. "dapr-placement:50006",
  51. ]
  52. volumes:
  53. - "./dapr/components/:/components"
  54. depends_on:
  55. - nest-subscriber
  56. network_mode: "service:nest-subscriber"

14. 源码

https://github.com/Hacker-Linner/dapr-nestjs-redis-pub-sub.git

更多

使用 Dapr JS SDK 让 Nest.js 集成 Dapr

本地使用 Docker Compose 与 Nestjs 快速构建基于 Dapr 的 Redis 发布/订阅分布式应用的更多相关文章

  1. 用前端姿势玩docker【五】快速构建中类Unix系统与Windows系统的差异化处理

    目录 用前端姿势玩docker[一]Docker通俗理解常用功能汇总与操作埋坑 用前端姿势玩docker[二]dockerfile定制镜像初体验 用前端姿势玩docker[三]基于nvm的前端环境构建 ...

  2. 使用PowerApps快速构建基于主题的轻业务应用 —— 进阶篇

    作者:陈希章 发表于 2017年12月14日 在上一篇 使用PowerApps快速构建基于主题的轻业务应用 -- 入门篇 中,我用了三个实际的例子演示了如何快速开始使用PowerApps构建轻业务应用 ...

  3. Docker Compose部署项目到容器-基于Tomcat和mysql的项目yml配置文件代码

    场景 Docker-Compose简介与Ubuntu Server 上安装Compose: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/deta ...

  4. Docker Compose + Traefik v2 快速安装, 自动申请SSL证书 http转https 初次尝试

    前言 昨晚闲得无聊睡不着觉,拿起服务器尝试部署了一下Docker + Traefik v2.1.6 ,以下是一些配置的总结,初次接触,大佬勿喷. 我的系统环境是 Ubuntu 18.04.3 LTS ...

  5. NodeJS 基于 Dapr 构建云原生微服务应用,从 0 到 1 快速上手指南

    Dapr 是一个可移植的.事件驱动的运行时,它使任何开发人员能够轻松构建出弹性的.无状态和有状态的应用程序,并可运行在云平台或边缘计算中,它同时也支持多种编程语言和开发框架.Dapr 确保开发人员专注 ...

  6. 使用PowerApps快速构建基于主题的轻业务应用 —— 入门篇

    作者:陈希章 发表于 2017年12月12日 前言 在上一篇文章 基于Office 365的随需应变业务应用平台 中我提到,随着随需应变的业务需要,以及技术的发展,业务应用的开发的模式也有了深刻的变化 ...

  7. Docker Compose部署项目到容器-基于Tomcat和mysql的商城项目(附源码和sql下载)

    场景 Docker-Compose简介与Ubuntu Server 上安装Compose: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/deta ...

  8. 使用Spring Boot快速构建基于SQLite数据源的应用

    为了提供一个单包易部署的服务器应用,考虑使用Spring Boot,因为其集成了Apache Tomcat,易于运行,免去绝大部分了服务器配置的步骤. 项目初始化 首先从mvn archetype:g ...

  9. ActiveMQ 快速入门教程系列 第二章 发布-订阅者模式实现

    第二章我们会介绍怎样实现一个发布者对多个订阅者的消息传递 Topic和queue的最大区别在于topic是以广播的形式,通知所有在线监听的客户端有新的消息,没有监听的客户端将收不到消息:而queue则 ...

随机推荐

  1. Jx.Cms开发笔记(六)-重写Compiler

    我们在Jx.Cms开发笔记(三)-Views主题动态切换中说了如何切换主题.但是这里有一个问题,就是主题切换时,会报错 这是由于asp.net core在处理Views的信息的时候是在构造函数中处理的 ...

  2. .NET宝藏API之:OutputFormatter,格式化输出对象

    相信大家在项目中都用过统一响应参数模板. 先声明一个响应模板类: public class ResponseDto { public int code { get; set; } public str ...

  3. 攻防世界-MISC:坚持60s

    这是攻防世界新手练习区的第六题,题目如下: 点击附件1下载,是一个java文件,点击运行一下: 绿帽子满天飞不知道是怎么回事(还是老老实实去看WP吧),WP说这是编译过的Java代码,但我手里没有反编 ...

  4. SQL安装

    安装教程 点击传送 遇到的问题 解决方案1:

  5. MeteoInfo-Java解析与绘图教程(八)_java解析卫星FY-4A一级产品文件(HDF举例)

    MeteoInfo-Java解析与绘图教程(八)_java解析卫星一级产品文件(HDF举例) 最近解析卫星数据遇到了一级产品,它的解析方式与之前文章说的有些不同,特此补充一下 卫星的一级产品,里面是没 ...

  6. Nginx实战|Nginx健康检查

    开源Linux 长按二维码加关注~ 上一篇:盘点提高国内访问Github的速度的9种方案 服务治理的一个重要任务是感知服务节点变更,完成服务自动注册及异常节点的自动摘除.这就需要服务治理平台能够:及时 ...

  7. 中间件漏洞之Apache

    中间件之Apache漏洞 我们常见的中间件有IIS.Apache.Nginx,其中Apache中间件有什么漏洞呢? Apache 解析漏洞: 漏洞原因:该解析漏洞属于用户配置问题(mime.types ...

  8. 一文彻底搞懂JavaScript中的prototype

    prototype初步认识 在学习JavaScript中,遇到了prototype,经过一番了解,知道它是可以进行动态扩展的 function Func(){}; var func1 = new Fu ...

  9. kali linux安装后乱码的解决方法

    操作系统是5.3 解决方法是在终端执行命令: sudo apt-get install ttf-wqy-zenhei

  10. Go基础3:函数、结构体、方法、接口

    目录 1. 函数 1.1 函数返回值 同一种类型返回值 带变量名的返回值 函数中的参数传递 函数变量 1.2 匿名函数--没有函数名字的函数 在定义时调用匿名函数 将匿名函数赋值给变量 匿名函数用作回 ...