Skip to content

Latest commit

 

History

History
359 lines (293 loc) · 12.3 KB

cluster.md

File metadata and controls

359 lines (293 loc) · 12.3 KB

Usage

First, we need to import the ClusterModule into our root module:

import { Module } from '@nestjs/common';
import { ClusterModule } from '@liaoliaots/nestjs-redis';

@Module({
  imports: [
    ClusterModule.forRoot({
      config: {
        nodes: [{ host: 'localhost', port: 16380 }],
        redisOptions: { password: 'authpassword' }
      }
    })
  ]
})
export class AppModule {}

Now, we can use cluster in two ways.

via decorator:

import { Injectable } from '@nestjs/common';
import { InjectCluster, DEFAULT_CLUSTER_NAMESPACE } from '@liaoliaots/nestjs-redis';
import { Cluster } from 'ioredis';

@Injectable()
export class AppService {
  constructor(
    @InjectCluster() private readonly cluster: Cluster // or // @InjectCluster(DEFAULT_CLUSTER_NAMESPACE) private readonly cluster: Cluster
  ) {}

  async set() {
    return await this.cluster.set('key', 'value', 'EX', 10);
  }
}

via service:

import { Injectable } from '@nestjs/common';
import { ClusterService, DEFAULT_CLUSTER_NAMESPACE } from '@liaoliaots/nestjs-redis';
import { Cluster } from 'ioredis';

@Injectable()
export class AppService {
  private readonly cluster: Cluster;

  constructor(private readonly clusterService: ClusterService) {
    this.cluster = this.clusterService.getClient();
    // or
    // this.cluster = this.clusterService.getClient(DEFAULT_CLUSTER_NAMESPACE);
  }

  async set() {
    return await this.cluster.set('key', 'value', 'EX', 10);
  }
}

HINT: By default, the ClusterModule is a Global module.

HINT: If you don't set the namespace for a client, its namespace is set to "default". Please note that you shouldn't have multiple client without a namespace, or with the same namespace, otherwise they will get overridden.

Configuration

Name Type Default Required Description
closeClient boolean true false If set to true, all clients will be closed automatically on nestjs application shutdown. To use closeClient, you must enable listeners by calling app.enableShutdownHooks(). Read more about the application shutdown.
readyLog boolean false false If set to true, then ready logging will be displayed when the client is ready.
errorLog boolean true false If set to true, then errors that occurred while connecting will be displayed by the built-in logger.
config ClusterClientOptions | ClusterClientOptions[] undefined true Used to specify single or multiple clients.
Name Type Default Required Description
namespace string | symbol 'default' false Client name. If client name is not given then it will be called "default". Different clients must have different names. You can import DEFAULT_CLUSTER_NAMESPACE to use it.
nodes { host?: string; port?: number }[] | string[] | number[] undefined true List of cluster nodes.
onClientCreated function undefined false Function to be executed as soon as the client is created.
... ClusterOptions ClusterOptions - false Inherits from ClusterOptions.

Asynchronous configuration

via useFactory:

import { Module } from '@nestjs/common';
import { ClusterModule, ClusterModuleOptions } from '@liaoliaots/nestjs-redis';
import { ConfigService, ConfigModule } from '@nestjs/config';

@Module({
  imports: [
    ClusterModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: async (configService: ConfigService): Promise<ClusterModuleOptions> => {
        await somePromise();

        return {
          config: {
            nodes: [{ host: 'localhost', port: 16380 }],
            redisOptions: { password: 'authpassword' }
          }
        };
      }
    })
  ]
})
export class AppModule {}

via useClass:

import { Module, Injectable } from '@nestjs/common';
import { ClusterModule, ClusterOptionsFactory, ClusterModuleOptions } from '@liaoliaots/nestjs-redis';

@Injectable()
export class ClusterConfigService implements ClusterOptionsFactory {
  async createClusterOptions(): Promise<ClusterModuleOptions> {
    await somePromise();

    return {
      config: {
        nodes: [{ host: 'localhost', port: 16380 }],
        redisOptions: { password: 'authpassword' }
      }
    };
  }
}

@Module({
  imports: [
    ClusterModule.forRootAsync({
      useClass: ClusterConfigService
    })
  ]
})
export class AppModule {}

via extraProviders:

// just a simple example

import { Module, ValueProvider } from '@nestjs/common';
import { ClusterModule, ClusterModuleOptions } from '@liaoliaots/nestjs-redis';

const MyOptionsSymbol = Symbol('options');
const MyOptionsProvider: ValueProvider<ClusterModuleOptions> = {
  provide: MyOptionsSymbol,
  useValue: {
    config: {
      nodes: [{ host: 'localhost', port: 16380 }],
      redisOptions: { password: 'authpassword' }
    }
  }
};

@Module({
  imports: [
    ClusterModule.forRootAsync({
      useFactory(options: ClusterModuleOptions) {
        return options;
      },
      inject: [MyOptionsSymbol],
      extraProviders: [MyOptionsProvider]
    })
  ]
})
export class AppModule {}

... or via useExisting, if you wish to use an existing configuration provider imported from a different module.

ClusterModule.forRootAsync({
  imports: [ConfigModule],
  useExisting: ConfigService
});

readyLog

import { Module } from '@nestjs/common';
import { ClusterModule } from '@liaoliaots/nestjs-redis';

@Module({
  imports: [
    ClusterModule.forRoot({
      readyLog: true,
      config: {
        nodes: [{ host: 'localhost', port: 16380 }],
        redisOptions: { password: 'authpassword' }
      }
    })
  ]
})
export class AppModule {}

The ClusterModule will display a message when CLUSTER INFO reporting the cluster is able to receive commands.

[Nest] 18886  - 09/16/2021, 6:19:56 PM     LOG [ClusterModule] default: connected successfully to the server

Single client

import { Module } from '@nestjs/common';
import { ClusterModule } from '@liaoliaots/nestjs-redis';

@Module({
  imports: [
    ClusterModule.forRoot({
      config: {
        nodes: [{ host: 'localhost', port: 16380 }],
        redisOptions: { password: 'authpassword' }

        // or with URL
        // nodes: ['redis://:authpassword@localhost:16380']
      }
    })
  ]
})
export class AppModule {}

Multiple clients

import { Module } from '@nestjs/common';
import { ClusterModule } from '@liaoliaots/nestjs-redis';

@Module({
  imports: [
    ClusterModule.forRoot({
      config: [
        {
          nodes: [{ host: 'localhost', port: 16380 }],
          redisOptions: { password: 'authpassword' }
        },
        {
          namespace: 'cluster2',
          nodes: [{ host: 'localhost', port: 16480 }],
          redisOptions: { password: 'authpassword' }
        }
      ]
    })
  ]
})
export class AppModule {}

with URL:

import { Module } from '@nestjs/common';
import { ClusterModule } from '@liaoliaots/nestjs-redis';

@Module({
  imports: [
    ClusterModule.forRoot({
      config: [
        {
          nodes: ['redis://:authpassword@localhost:16380']
        },
        {
          namespace: 'cluster2',
          nodes: ['redis://:authpassword@localhost:16480']
        }
      ]
    })
  ]
})
export class AppModule {}

onClientCreated

For example, we can listen to some events of the cluster instance.

import { Module } from '@nestjs/common';
import { ClusterModule } from '@liaoliaots/nestjs-redis';

@Module({
  imports: [
    ClusterModule.forRoot({
      config: {
        nodes: [{ host: 'localhost', port: 16380 }],
        redisOptions: { password: 'authpassword' },
        onClientCreated(client) {
          client.on('error', err => {});
          client.on('ready', () => {});
        }
      }
    })
  ]
})
export class AppModule {}

Non-Global

By default, the ClusterModule is a Global module, ClusterService and all cluster instances are registered in the global scope. Once defined, they're available everywhere.

You can change this behavior by isGlobal parameter:

// cats.module.ts
import { Module } from '@nestjs/common';
import { ClusterModule } from '@liaoliaots/nestjs-redis';
import { CatsService } from './cats.service';
import { CatsController } from './cats.controller';

@Module({
  imports: [
    ClusterModule.forRoot(
      {
        config: {
          nodes: [{ host: 'localhost', port: 16380 }],
          redisOptions: { password: 'authpassword' }
        }
      },
      false // <-- providers are registered in the module scope
    )
  ],
  providers: [CatsService],
  controllers: [CatsController]
})
export class CatsModule {}

Testing

This package exposes getClusterToken() function that returns an internal injection token based on the provided context. Using this token, you can provide a mock implementation of the cluster instance using any of the standard custom provider techniques, including useClass, useValue, and useFactory.

import { Test, TestingModule } from '@nestjs/testing';
import { getRedisToken } from '@liaoliaots/nestjs-redis';

const module: TestingModule = await Test.createTestingModule({
  providers: [{ provide: getClusterToken('namespace'), useValue: mockedInstance }, YourService]
}).compile();

A working example is available here.