2

Dapr 与 NestJs ,实战编写一个 Pub & Sub 装饰器 - 为少

 2 years ago
source link: https://www.cnblogs.com/hacker-linner/p/16539272.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
image

Dapr 是一个可移植的、事件驱动的运行时,它使任何开发人员能够轻松构建出弹性的、无状态和有状态的应用程序,并可运行在云平台或边缘计算中,它同时也支持多种编程语言和开发框架。Dapr 确保开发人员专注于编写业务逻辑,不必分神解决分布式系统难题,从而显著提高了生产力。Dapr 降低了构建微服务架构类现代云原生应用的门槛。

Dapr JavaScript SDK

用于在 JavaScript 和 TypeScript 中构建 Dapr 应用程序的客户端库。 该客户端抽象了公共 Dapr API,例如服务到服务调用、状态管理、发布/订阅、Secret 等,并为构建应用程序提供了一个简单、直观的 API。

要开始使用 Javascript SDK,请从 NPM 安装 Dapr JavaScript SDK 包:

npm install --save @dapr/dapr

⚠️ dapr-client 现在已弃用。 请参阅#259 了解更多信息。

Dapr Javascript SDK 包含两个主要组件:

  • DaprServer: 管理所有 Dapr sidecar 到应用程序的通信。
  • DaprClient: 管理所有应用程序到 Dapr sidecar 的通信。

上述通信可以配置为使用 gRPC 或 HTTP 协议。

创建一个小应用程序来生成有关网站中用户行为的统计信息。

Demo 源码

https://github.com/Hacker-Linner/dapr-nestjs-jssdk-decorator

准备环境和项目结构

npm install -g @nestjs/cli
nest new api
mv api nest-dapr
cd nest-dapr
nest generate app page-view
npm install dapr-client
wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash

创建一个 decorators.ts 文件(apps/shared/decorators.ts),这样所有微服务都可以从我们即将编写的基础架构中受益。

注入 Dapr 赖项

注入 DaprClient 和 DaprServer,我们需要提供它们到 nest.js

在 app.module.ts 中让我们注册 DaprClient:

providers: [
...
  {
    provide: DaprClient,
    useValue: new DaprClient()
  }
]

在 page-view.module.ts 中以同样的方式添加 DaprServer:

providers: [
...
  {
    provide: DaprServer,
    useValue: new DaprServer()
  }
]

配置 Dapr 组件(rabbitMQ)

用 docker compose 启动 rabbitmq:

version: '3.9'
services:
  pubsub:
    image: rabbitmq:3-management-alpine
    container_name: 'pubsub'
    ports:
      - 5674:5672
      - 15674:15672 #web port

我们还需要配置 Dapr 组件。在根文件夹中创建一个 component/pubsub.yml:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: pubsub
  namespace: default
spec:
  type: pubsub.rabbitmq
  version: v1
  metadata:
    - name: host
      value: 'amqp://guest:guest@localhost:5674'

每个 Dapr 微服务都需要自己的 config。

api/dapr/config.yml:

apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
  name: api
  namespace: default

page-view/dapr/config.yml:

apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
  name: page-view
  namespace: default

API/Gateway 服务

app.controller.ts 中,我们将公开一个简单的 API/add-page-view

 @Post('/add-page-view')
 async prderAdd(@Body() pageViewDto: PageViewDto): Promise<void> {
   try {
      console.log(pageViewDto);
      await this.daprClient.pubsub.publish('pubsub', 'page-view-add', pageViewDto);
    } catch (e) {
      console.log(e);
    }
  }

内部监听微服务

在我们将数据发布到队列之后,我们需要监听它并调用它:

在 page-view.controller.ts 添加:

@DaprPubSubSubscribe('pubsub', 'add')
addPageView(data: PageViewDto) {
    console.log(`addPageView executed with data: ${JSON.stringify(data)}`);
    this.data.push(data);
}

注意我们现在需要创建的新装饰器:@DaprPubSubscribe

@DaprPubSubscribe 装饰器

在 shared/decorators.ts 中:

import { INestApplication } from '@nestjs/common';
import { DaprServer } from 'dapr-client';

export type PubsubMap = {
  [pubSubName: string]: {
    topic: string;
    target: any;
    descriptor: PropertyDescriptor;
  };
};
export const DAPR_PUB_SUB_MAP: PubsubMap = {};
export const DaprPubSubSubscribe = (
  pubSubName: string,
  topic: string,
): MethodDecorator => {
  return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
    DAPR_PUB_SUB_MAP[pubSubName] = {
      topic,
      target,
      descriptor,
    };
    return descriptor;
  };
};

export const useDaprPubSubListener = async (app: INestApplication) => {
  const daprServer = app.get(DaprServer);

  for (const pubSubName in DAPR_PUB_SUB_MAP) {
    const item = DAPR_PUB_SUB_MAP[pubSubName];
    console.log(
      `Listening to the pubsub name - "${pubSubName}" on topic "${item.topic}"`,
    );
    await daprServer.pubsub.subscribe(
      pubSubName,
      item.topic,
      async (data: any) => {
        const targetClassImpl = app.get(item.target.constructor);
        await targetClassImpl[item.descriptor.value.name](data);
      },
    );
  }
};

运行应用程序

docker-compose up -d # 启动 rabbitmq
npm run dapr:api:dev # 启动 api/gateway
npm run page-view:dev # 启动内部微服务监听 dapr rabbitmq 队列

执行请求:

curl --location --request POST 'http://localhost:7001/v1.0/invoke/api/method/statistics/add-page-view' \
--header 'Content-Type: application/json' \
--data-raw '{
    "pageUrl" : "https://test.com/some-page",
    "userAgent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0"
}'

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK