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

レッスン 15: キャッシュ、キュー、バックグラウンド ジョブ

Redis クライアント (ioredis)、キャッシュ パターン。 BullMQ ジョブ キュー、優先キュー、レート制限。 node-cron を使用した Cron ジョブ。インメモリ キャッシュ (LRU キャッシュ)。

💻 プログラミング — レッスン 15 レッスン 15: キャッシュ、キュー、バックグラウンド ジョブ

Node.js コア: 基本から高度まで

パート 4: フレームワークを使用しない構築

xdev.asia

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. Cron ジョブ

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 Pub/Sub

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 バインディング。