Использование Redis Streams с NestJS: Часть 1 — Настройка

Использование Redis Streams с NestJS: Часть 1 — Настройка

22 февраля 2023 г.

Это первая часть серии из трех частей, в которой мы рассмотрим, как использовать потоки Redis с NestJS.

Он состоит из 3 частей:

  1. Настройка приложения NestJS и подключение к Redis
  2. Заполнение потоков Redis и чтение из них в режиме разветвления
  3. Использование групп потребителей для обработки одного потока от нескольких участников таким образом, что одно сообщение отправляется и обрабатывается только одним участником (потребителем)

К концу этой серии вы будете обладать знаниями и инструментами, необходимыми для создания собственного приложения NestJS, использующего потоки Redis для обработки данных в реальном времени.

Полный код доступен на github

.

Предполагаемые предварительные знания

Прежде чем углубляться в этот пост, предполагается, что вы имеете общее представление о NestJS и Redis. Также рекомендуется ознакомиться с генераторами JavaScript, так как они будут использоваться в примерах для непрерывного прослушивания потока Redis.

Мы также будем использовать Docker и Docker Compose для создания и запуска нашего сервера и приложения Redis.

NestJS — это фреймворк машинописного текста, поэтому вы тоже должны с ним ознакомиться.

Чтобы ознакомиться с этими вещами, я рекомендую:

:::подсказка Также важно отметить, что, хотя в этом посте явно рассматривается интеграция потоков Redis с NestJS, обсуждаемые здесь концепции и методы также могут применяться к другим платформам и языкам. Потоки Redis — это универсальный инструмент, который можно использовать в самых разных приложениях и архитектурах. Поэтому, даже если вы не используете NestJS, общие концепции и подходы, изложенные в этом посте, могут применяться к вашим проектам и стеку.

:::

Настройка

Создание нового приложения NestJS

Для начала нам нужно создать новое приложение NestJS. Это можно сделать с помощью интерфейса командной строки NestJS, который можно установить через npm, запустив npm i -g @nestjs/cli. После установки вы можете создать новое приложение, запустив nest new redis-streams, где «redis-streams» — это имя вашего приложения.

При этом будет создан новый каталог с тем же именем, содержащий базовую структуру приложения NestJS.

Настройка Docker

Для упрощения мы собираемся использовать файл docker-compose.yml, чтобы включить как наше приложение, так и сервер Redis:

# docker-compose.yml

version: '3'

services:
  app:
    container_name: app
    image: node:18
    environment:
      HTTPS_METHOD: noredirect
    ports:
      - 8081:3000
    volumes:
      - ./:/usr/src/app/
    working_dir: /usr/src/app
    command: npm run start:dev

  redis:
    container_name: redis
    image: redis:7
    restart: always

Теперь вы можете запустить docker-compose up -d для сборки и запуска ваших сервисов. Чтобы просмотреть вывод консоли, используйте журналы создания докеров

.

Вы должны увидеть следующее сообщение:

 LOG [NestApplication] Nest application successfully started +7ms

Подключение к Redis

Настройка клиентской библиотеки node-redis

Для вызова Redis мы будем использовать официальную клиентскую библиотеку NodeJS Redis node-redis

$ npm i redis

Есть и другие библиотеки, например. ioredis — достойная внимания альтернатива. Вы можете увидеть список клиентов на веб-сайте Redis

Модуль Redis

Наконец-то мы можем начать работать с Redis из нашего приложения.

Сначала будет создан новый модуль для сервисов, связанных с Redis.

$ nest g module redis

Вы должны увидеть это:

// redis.module.ts

import { Module } from '@nestjs/common';

@Module({
  providers: [],
  exports: [],
})
export class RedisModule {}

Фабрика RedisClient

Чтобы использовать RedisClient из библиотеки node-redis, мы создадим для него фабричный провайдер:

// redis-client.factory.ts

import { FactoryProvider } from '@nestjs/common';
import { createClient } from 'redis';
import { RedisClient, REDIS_CLIENT } from './redis-client.type';

export const redisClientFactory: FactoryProvider<Promise<RedisClient>> = {
  provide: REDIS_CLIENT,
  useFactory: async () => {
    const client = createClient({ url: 'redis://redis:6379/0' });
    await client.connect();
    return client;
  },
};

Давайте сломаем это.

Мы создаем FactoryProvider, который будет вызывать асинхронную функцию, предоставленную в useFactory:

// redis-client.factory.ts
// --snip--
  useFactory: async () => {
    const client = createClient({ url: 'redis://redis:6379/0' });
    await client.connect();
    return client;
  },
// --snip--

  1. Мы вызываем функцию createClient из библиотеки redis и передаем ей URL, состоящий из {protocol}://{host}:{port}/{ база данных, где:

  2. протокол= redis

  3. host = redis — это указано в docker-compose.yml с помощью container_host: redis. Обычно вы создаете переменную среды с вашим IP-адресом экземпляра Redis и используете ее здесь.
  4. port = 6379 — порт Redis по умолчанию
  5. база данных = 0 база данных по умолчанию

  6. Подключаемся к серверу Redis await client.connect();

  7. Вернуть созданный & подключенный клиент.

Возможно, вы заметили, что мы предоставили не экземпляр типа RedisClient, а REDIS_CLIENT, который является нашим токеном внедрения. Кроме того, RedisClient — это наш собственный тип, а не redis. Это связано с тем, что node-redis в v4 не экспортирует тип RedisClient, поэтому нам нужно создать свой собственный в /redis-client.type.ts файл:

export type RedisClient = ReturnType<typeof createClient>;
export const REDIS_CLIENT = Symbol('REDIS_CLIENT');

Осталось только добавить это в наш модуль:

// redis.module.ts

//  --snip--
@Module({
  providers: [redisClientFactory],
})
export class RedisModule {}

Создание RedisService

Затем добавим RedisService, который создаст уровень абстракции от RedisClient.

$ nest g service redis

Туда мы вставим наш RedisClient

// redis.service.ts

// --snip--
@Injectable()
export class RedisService implements OnModuleDestroy {
  public constructor(
    @Inject(REDIS_CLIENT) private readonly redis: RedisClient,
  ) {}

  onModuleDestroy() {
    this.redis.quit();
  }
}

Чтобы не оставлять зависшие соединения, мы собираемся закрыть соединение с Redis, вызвав this.redis.quit() в событии жизненного цикла OnModuleDestroy.

Ping для сервера Redis

Чтобы убедиться, что мы успешно подключились к Redis, добавим конечную точку API, которая вызывает call Redis ping

// redis.service.ts

  ping() {
    return this.redis.ping();
  }

Давайте экспортируем RedisService, чтобы мы могли использовать его в других модулях:

// redis.module.ts

// --snip--
  exports: [RedisService],
// --snip--

Теперь мы импортируем его в AppService и пропустим через наш вызов ping redis:

// app.service.ts

@Injectable()
export class AppService {
// --snip--
  constructor(
    private readonly redisService: RedisService,
  ) {}

  redisPing() {
    return this.redisService.ping();
  }
// --snip--

Наконец, мы можем добавить новую конечную точку в AppController, которая будет выполнять эхо-запрос на сервер Redis и отправлять ответ пользователю:

// app.controller.ts

// --snip--

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get('redis-ping')
  redisPing() {
    return this.appService.redisPing();
  }
}

Теперь, когда наше приложение и сервер Redis запущены, мы можем открыть нашу конечную точку /redis-ping http://localhost: 8081/redis-ping, и вы должны увидеть ответ:

Поздравляем! Мы завершили первую часть серии из трех частей и создали приложение NestJS с рабочим подключением к нашему серверу Redis! Во второй части мы собираемся создавать, заполнять и читать потоки Redis.


Оригинал