Transporter Redis triển khai mô hình messaging publish/subscribe và tận dụng tính năng Pub/Sub của Redis. Message được publish sẽ được phân loại vào các channel, mà không cần biết subscriber nào (nếu có) sẽ nhận được message. Mỗi microservice có thể subscribe vào bất kỳ số lượng channel nào. Ngoài ra, có thể subscribe nhiều channel cùng một lúc. Các message trao đổi qua channel là fire-and-forget, nghĩa là nếu một message được publish nhưng không có subscriber nào quan tâm đến nó, message đó sẽ bị loại bỏ và không thể khôi phục lại. Do đó, bạn không có gì đảm bảo rằng message hay event sẽ được xử lý bởi ít nhất một service. Một message duy nhất có thể được subscribe (và nhận) bởi nhiều subscriber.

Ghi chú người dịch Đặc điểm “fire-and-forget” này khác hẳn với queue kiểu RabbitMQ hay Kafka: Redis Pub/Sub không lưu lại message nếu không có ai đang lắng nghe tại đúng thời điểm đó — không có buffer, không có replay. Nếu bạn cần độ tin cậy cao hơn (message không bị mất khi consumer offline), cần cân nhắc dùng Redis Streams hoặc chuyển sang transporter khác như Kafka/RabbitMQ. Tương tự khái niệm channel của Redis Pub/Sub gần giống với EventEmitter trong Node hoặc pub/sub trong Go bằng channel — không có gì đảm bảo message được lưu lại nếu không ai đang lắng nghe.
Cài đặt
Để bắt đầu xây dựng microservice dựa trên Redis, trước tiên hãy cài package cần thiết:
$ npm i --save ioredis
Tổng quan
Để dùng transporter Redis, truyền đối tượng options sau vào phương thức createMicroservice():
const app = await NestFactory.createMicroservice<MicroserviceOptions>(AppModule, {
transport: Transport.REDIS,
options: {
host: 'localhost',
port: 6379,
},
});
const app = await NestFactory.createMicroservice(AppModule, {
transport: Transport.REDIS,
options: {
host: 'localhost',
port: 6379,
},
});
Gợi ý Enum Transport được import từ package @nestjs/microservices.
Options
Thuộc tính options đặc thù theo transporter được chọn. Transporter Redis expose các thuộc tính được mô tả dưới đây.
host |
URL kết nối |
port |
Port kết nối |
retryAttempts |
Số lần retry message (mặc định: 0) |
retryDelay |
Khoảng thời gian chờ giữa các lần retry message (ms) (mặc định: 0) |
wildcards |
Kích hoạt subscription dạng wildcard của Redis, chỉ định transporter dùng psubscribe/pmessage bên dưới. (mặc định: false) |
Tất cả thuộc tính được hỗ trợ bởi client chính thức ioredis cũng được transporter này hỗ trợ.
Client
Giống các transporter microservice khác, bạn có nhiều cách để tạo instance ClientProxy cho Redis.
Một cách để tạo instance là dùng ClientsModule. Để tạo client instance với ClientsModule, import nó và dùng phương thức register() để truyền một đối tượng options với các thuộc tính giống như trong phương thức createMicroservice() ở trên, cùng với thuộc tính name dùng làm injection token. Đọc thêm về ClientsModule tại đây.
@Module({
imports: [
ClientsModule.register([
{
name: 'MATH_SERVICE',
transport: Transport.REDIS,
options: {
host: 'localhost',
port: 6379,
}
},
]),
]
...
})
Các cách khác để tạo client (ClientProxyFactory hoặc @Client()) cũng có thể dùng được. Bạn có thể đọc thêm về chúng tại đây.
Context
Trong các tình huống phức tạp hơn, bạn có thể cần truy cập thêm thông tin về request đến. Khi dùng transporter Redis, bạn có thể truy cập object RedisContext.
@MessagePattern('notifications')
getNotifications(@Payload() data: number[], @Ctx() context: RedisContext) {
console.log(`Channel: ${context.getChannel()}`);
}
@Bind(Payload(), Ctx())
@MessagePattern('notifications')
getNotifications(data, context) {
console.log(`Channel: ${context.getChannel()}`);
}
Gợi ý @Payload(), @Ctx() và RedisContext được import từ package @nestjs/microservices.
Wildcard
Để kích hoạt hỗ trợ wildcard, đặt tùy chọn wildcards thành true. Điều này chỉ định transporter dùng psubscribe và pmessage bên dưới.
const app = await NestFactory.createMicroservice(AppModule, {
transport: Transport.REDIS,
options: {
// Các tùy chọn khác
wildcards: true,
},
});
Đảm bảo cũng truyền tùy chọn wildcards khi tạo client instance.
Khi tùy chọn này được kích hoạt, bạn có thể dùng wildcard trong message pattern và event pattern của mình. Ví dụ, để subscribe vào mọi channel bắt đầu bằng notifications, bạn có thể dùng pattern sau:
@EventPattern('notifications.*')
Cập nhật trạng thái instance
Để nhận cập nhật thời gian thực về kết nối và trạng thái của driver instance bên dưới, bạn có thể subscribe vào stream status. Stream này cung cấp các cập nhật trạng thái đặc thù cho driver đã chọn. Với driver Redis, stream status phát ra các event connected, disconnected, và reconnecting.
this.client.status.subscribe((status: RedisStatus) => {
console.log(status);
});
Gợi ý Type RedisStatus được import từ package @nestjs/microservices.
Tương tự, bạn có thể subscribe vào stream status của server để nhận thông báo về trạng thái của server.
const server = app.connectMicroservice<MicroserviceOptions>(...);
server.status.subscribe((status: RedisStatus) => {
console.log(status);
});
Lắng nghe event của Redis
Trong một số trường hợp, bạn có thể muốn lắng nghe các event nội bộ được phát ra bởi microservice. Ví dụ, bạn có thể lắng nghe event error để kích hoạt các hành động bổ sung khi xảy ra lỗi. Để làm điều này, dùng phương thức on(), như sau:
this.client.on('error', (err) => {
console.error(err);
});
Tương tự, bạn có thể lắng nghe các event nội bộ của server:
server.on<RedisEvents>('error', (err) => {
console.error(err);
});
Gợi ý Type RedisEvents được import từ package @nestjs/microservices.
Truy cập driver bên dưới
Đối với các use case phức tạp hơn, bạn có thể cần truy cập trực tiếp driver instance bên dưới. Điều này có thể hữu ích cho các tình huống như tự đóng kết nối thủ công hoặc dùng các phương thức đặc thù của driver. Tuy nhiên, hãy nhớ rằng trong hầu hết trường hợp, bạn không nên cần truy cập trực tiếp driver.
Để làm điều này, bạn có thể dùng phương thức unwrap(), phương thức này trả về driver instance bên dưới. Tham số generic type nên chỉ định kiểu của driver instance bạn mong đợi.
const [pub, sub] =
this.client.unwrap<[import('ioredis').Redis, import('ioredis').Redis]>();
Tương tự, bạn có thể truy cập driver instance bên dưới của server:
const [pub, sub] =
server.unwrap<[import('ioredis').Redis, import('ioredis').Redis]>();
Ghi chú người dịch Việc unwrap() trả về một tuple [pub, sub] phản ánh đúng cách ioredis (và Redis nói chung) hoạt động: một connection đang ở chế độ subscribe (SUBSCRIBE/PSUBSCRIBE) không thể dùng để chạy lệnh thường (như GET, SET) trên cùng connection đó. Vì vậy driver luôn cần tối thiểu hai connection riêng biệt — một để publish, một để subscribe — khác với transporter khác (ví dụ Kafka) chỉ trả về một instance driver duy nhất.
Lưu ý rằng, ngược lại với các transporter khác, transporter Redis trả về một tuple gồm hai instance ioredis: instance đầu tiên dùng để publish message, và instance thứ hai dùng để subscribe nhận message.