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


    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"
    }'
    
  • 相关阅读:
    Android逆向学习之Frida逆向与抓包实战学习笔记(持续更新中)
    程序员的护城河:从代码到智慧的跨越
    跑步耳机哪种好,推荐五款在运动中值得佩戴的耳机推荐
    Debian | Vscode 安装与配置 C 环境
    十三、【分布式微服务企业快速架构】SpringCloud分布式、微服务、云架构之Eclipse 创建 XML 文件
    谷歌Chrome 100正式版发布:启用全新图标,修复28个安全漏洞
    Arcgis 定义投影、投影变换、导出栅格为tif、矢量转tif
    Word脚注如何插入?1分钟学会!
    阻止事件冒泡
    SpringCloud 02 Rest学习环境搭建(DeptProvider)
  • 原文地址:https://www.cnblogs.com/hacker-linner/p/16539272.html