Chuyển đến nội dung chính

第 15 課:快取、佇列和後台作業

Redis 用戶端(ioredis),快取模式。 BullMQ 作業佇列、優先權佇列、速率限制。使用 node-cron 進行 Cron 作業。記憶體中快取(LRU 快取)。

💻 程式設計 — 第 15 課 第 15 課:快取、佇列和後台作業

Node.js 核心:從基礎到高級

第 4 部分:無框架構建

亞洲開發網

1.Redis客戶端(ioredis)

import Redis from 'ioredis'

const redis = new Redis({
  host: 'localhost',
  port: 6379,
  maxRetriesPerRequest: 3,
  retryStrategy: (times) => Math.min(times * 50, 2000),
})

// Basic operations
await redis.set('user:1', JSON.stringify({ name: 'A' }), 'EX', 3600)
const user = JSON.parse(await redis.get('user:1') || 'null')

// Hash
await redis.hset('user:1', { name: 'A', email: '[email protected]' })
const data = await redis.hgetall('user:1')

// Sorted set (leaderboard)
await redis.zadd('scores', 100, 'player1', 200, 'player2')
const top10 = await redis.zrevrange('scores', 0, 9, 'WITHSCORES')

2. 快取模式

// Cache-aside pattern
async function getUser(id: string) {
  const cacheKey = `user:${id}`
  const cached = await redis.get(cacheKey)
  if (cached) return JSON.parse(cached)

  const user = await db.query('SELECT * FROM users WHERE id = $1', [id])
  await redis.set(cacheKey, JSON.stringify(user), 'EX', 300) // 5 phút
  return user
}

// Cache invalidation
async function updateUser(id: string, data: Partial<User>) {
  await db.query('UPDATE users SET name = $1 WHERE id = $2', [data.name, id])
  await redis.del(`user:${id}`) // Invalidate cache
}

// LRU Cache (in-memory)
const { LRUCache } = await import('lru-cache')

const cache = new LRUCache<string, any>({
  max: 500,            // Max entries
  ttl: 1000 * 60 * 5,  // 5 phút
  ttlAutopurge: true,
})

3.BullMQ作業佇列

import { Queue, Worker, QueueScheduler } from 'bullmq'

const connection = { host: 'localhost', port: 6379 }

// Tạo queue
const emailQueue = new Queue('emails', { connection })

// Add job
await emailQueue.add('welcome', {
  to: '[email protected]',
  subject: 'Welcome!',
}, {
  attempts: 3,
  backoff: { type: 'exponential', delay: 1000 },
  priority: 1,
  removeOnComplete: 100,
})

// Worker xử lý jobs
const worker = new Worker('emails', async (job) => {
  console.log(`Processing ${job.name}: ${job.data.to}`)
  await sendEmail(job.data)
}, {
  connection,
  concurrency: 5,
  limiter: { max: 10, duration: 1000 }, // Rate limit: 10/s
})

worker.on('completed', (job) => console.log(`Done: ${job.id}`))
worker.on('failed', (job, err) => console.error(`Failed: ${job?.id}`, err))

4. 計劃任務

import cron from 'node-cron'

// Chạy mỗi 5 phút
cron.schedule('*/5 * * * *', async () => {
  console.log('Running cleanup...')
  await db.query('DELETE FROM sessions WHERE expires_at < NOW()')
})

// Chạy lúc 2:00 AM hàng ngày
cron.schedule('0 2 * * *', async () => {
  await generateDailyReport()
})

// Chạy đầu mỗi tháng
cron.schedule('0 0 1 * *', async () => {
  await archiveOldData()
})

5.Redis 發布/訂閱

const subscriber = new Redis()
const publisher = new Redis()

// Subscribe
await subscriber.subscribe('notifications')
subscriber.on('message', (channel, message) => {
  console.log(`[${channel}] ${message}`)
})

// Publish
await publisher.publish('notifications', JSON.stringify({
  type: 'order_created',
  orderId: '123',
}))

下一篇: 原生插件和 N-API —napi-rs、node-gyp、Rust 綁定。