Implementasi Redis sebagai Publish Subscribe
Selain sebagai sebuah cache manager, redis juga bisa digunkan sebagai Redis Pub/Sub. Publish/Subscribe
adalah fitur Redis yang memungkinkan komunikasi real-time antar sistem atau aplikasi seperti kafka yang kita pelajari sebelumnya. Ini digunakan untuk pengiriman pesan secara asyncronous
antara publisher
(pengirim pesan) dan subscriber
(penerima pesan).
Bagaimana Redis Pub/Sub Bekerja:
Publisher
akan mngirim pesan ke channel tertentu, kemudian
Subscriber
akan Mendengarkan (subscribe
) channel tertentu dan menerima pesan yang diterbitkan olehpublisher
ke channel tersebut.
Ketika sebuah pesan dikirimkan oleh publisher
ke channel Redis, Redis akan mengirimkan pesan tersebut ke semuasubscriber
yang listen channel yang sama.
Penggunaan Redis Pub/Sub:
Real-time communication
: Digunakan untuk membangun sistem real-time seperti notifikasi, chat aplikasi, atau streaming data.Event-driven architecture
: Cocok untuk aplikasi yang menggunakan arsitektur berbasisevent
, di mana satu layanan dapat menerbitkanevent
dan layanan lain mendengarkannya.
Perbedaan Redis Pub/Sub
dan Redis Cache
Fitur | Redis Pub/Sub | Redis Cache |
---|---|---|
Fungsi utama | Komunikasi real-time antar sistem (publish/subscribe) | Penyimpanan sementara data untuk akses cepat (caching) |
Cara kerja | Pesan dikirim ke channel dan diterima oleh subscriber | Data disimpan dengan kunci dan dapat diambil kembali melalui cache |
Penyimpanan data | Tidak menyimpan pesan, pesan hanya diteruskan ke subscriber | Data disimpan dalam memori untuk diambil kembali dalam waktu tertentu |
Contoh penggunaan | Chat aplikasi, notifikasi real-time, event-driven systems | Menyimpan hasil kueri database untuk mengurangi beban akses |
Skalabilitas | Terbatas dalam skala besar, lebih cocok untuk skenario kecil | Bisa digunakan di aplikasi besar untuk mengurangi latensi |
NestJS sebagai Redis Subscribe
Untuk membuatRedis Subscribe
kita harus konfigurasi terlebih dahulu seperti pada kafka sebelumnya. Pertama kita buat sebuah file redis.config.ts
untuk menyimpan konfigurasi global untuk redis di nestjs.
import { RedisOptions, Transport } from '@nestjs/microservices';
import { config as dotenv } from 'dotenv';
dotenv();
export const redisConfig: RedisOptions = {
transport: Transport.REDIS,
options: {
host: `${process.env.REDIS_HOST}`,
port: Number(process.env.REDIS_PORT),
password: `${process.env.REDIS_PASSWORD}`,
},
};
Kemudian kita implementasi config tersebut untuk menghubungkan nestjs ke dalam microservice
yang menggunakan redis.
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
import { useContainer } from 'class-validator';
import { kafkaConfig } from './config/kafka.config';
import { MicroserviceOptions } from '@nestjs/microservices';
import { redisConfig } from './config/redis.config';
async function bootstrap() {
const app = await NestFactory.create(AppModule, {
cors: true,
});
app.enableCors();
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
forbidUnknownValues: true,
transform: true,
validateCustomDecorators: true,
transformOptions: {
enableImplicitConversion: true,
},
}),
);
app.connectMicroservice<MicroserviceOptions>(redisConfig);
app.connectMicroservice<MicroserviceOptions>(kafkaConfig);
app.startAllMicroservices();
useContainer(app.select(AppModule), { fallbackOnErrors: true });
await app.listen(process.env.APP_PORT);
}
bootstrap();
Info
app.connectMicroservice
: Ini adalah method dari NestJS yang digunakan untuk menghubungkan aplikasi ke sebuah microservice.<MicroserviceOptions>
: Ini adalah generic type yang menandakan bahwa kita akan memberikan opsi konfigurasi untuk microservice. Opsi ini harus sesuai dengan interface MicroserviceOptions yang disediakan oleh @nestjs/microservices.- r
edisConfig
: Ini adalah objek yang berisi konfigurasi untuk menghubungkan ke Redis. Objek ini biasanya berisi informasi seperti host Redis, port, dan mungkin juga password jika Redis Anda diproteksi dengan password.
Setelah kita menjadikan nest sebagai subscribe
, selanjutkan kita buat controller untuk mendengar dan menerima apabila ada message yang dikirim dari publish. Pada contoh ini kita buat channel bernama notifikasi
@MessagePattern("notifikasi")
async handleOrderCrated(data) {
const pesan = {
to: data.id,
message: `Update berhasil`,
};
console.log(pesan);
}
NestJS sebagai Redis Publisher
Untuk membaut redis sebagai publisher , kita daftarkan redis config tersebut di redis.module.ts
import { Global, Module } from '@nestjs/common';
import { RedisService } from './redis.service';
import { CacheModule } from '@nestjs/cache-manager';
import { ClientsModule } from '@nestjs/microservices';
import { redisConfig } from 'src/config/redis.config';
@Global()
@Module({
imports: [
ClientsModule.register([
{
name: 'LATIHAN_REDIS_PUBSUB',
...redisConfig,
},
]),
CacheModule.registerAsync({
isGlobal: true,
useFactory: async () => {
const { config } = await import('../config/cache.config');
return config;
},
}),
],
providers: [RedisService],
exports: [RedisService],
})
export class RedisModule {}
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Inject, Injectable } from '@nestjs/common';
import { ClientProxy } from '@nestjs/microservices';
import { Cache } from 'cache-manager';
@Injectable()
export class RedisService {
@Inject(CACHE_MANAGER) private cacheManager: Cache;
@Inject('LATIHAN_REDIS_PUBSUB') private client: ClientProxy;
async setCacheKey({
key: key,
data: data,
ttl = 60,
}: {
key: string | number;
data: any;
ttl?: number;
}) {
return await this.cacheManager.set(`${key}`, data, ttl);
}
async getCacheKey(key: string) {
return await this.cacheManager.get(`${key}`);
}
async deleteCacheKey(key: string) {
return await this.cacheManager.del(`${key}`);
}
async sendNotification(channel: string, data: any) {
this.client.emit(channel, { ...data });
return data;
}
}
import {
HttpException,
HttpStatus,
Inject,
Injectable,
NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import BaseResponse from 'src/utils/response/base.response';
import { Between, Like, Repository } from 'typeorm';
import { Order } from './order.entity';
import { ResponsePagination, ResponseSuccess } from 'src/interface/response';
import { CreateOrderDto, UpdateOrderDto, findAllOrderDto } from './order.dto';
import { REQUEST } from '@nestjs/core';
import { Workbook } from 'exceljs';
import { Response } from 'express';
import { KafkaService } from 'src/kafka/kafka.service';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Cache } from 'cache-manager';
import { RedisService } from 'src/redis/redis.service';
@Injectable()
export class OrderService extends BaseResponse {
constructor(
@InjectRepository(Order)
private readonly orderRepository: Repository<Order>,
@Inject(REQUEST) private req: any,
private readonly kafkaService: KafkaService,
private readonly redisService: RedisService,
) {
super();
}
...
async updateOrder(
id: number,
payload: UpdateOrderDto,
): Promise<ResponseSuccess> {
const check = await this.orderRepository.findOne({
where: {
id: id,
},
});
if (!check) {
throw new HttpException('Data tidak ditemukan', HttpStatus.NOT_FOUND);
}
payload.order_detail &&
payload.order_detail.forEach((item) => {
item.created_by = this.req.user.id;
});
const order = await this.orderRepository.save({ ...payload, id: id });
this.redisService.deleteCacheKey(`order_${id}`);
const hasil = await this.redisService.sendNotification('notifikasi', {
id: id,
});
return this._success('OK', order;
}
}
Testing pada Postman