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

第 12 課:事件、計時器和診斷

EventEmitter 模式、自訂事件、記憶體洩漏偵測。中止控制器/中止訊號。診斷通道,async_hooks。 Node.js 檢查器,堆疊快照。

💻 程式設計 — 第 12 課 第 12 課:事件、計時器和診斷

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

第 3 部分:並發與網絡

亞洲開發網

1.EventEmitter模式

import { EventEmitter } from 'node:events'

// Typed EventEmitter
interface OrderEvents {
  created: [order: { id: string; total: number }]
  paid: [orderId: string, amount: number]
  shipped: [orderId: string]
  error: [error: Error]
}

class OrderService extends EventEmitter<OrderEvents> {
  async createOrder(data: { items: string[]; total: number }) {
    const order = { id: crypto.randomUUID(), total: data.total }
    this.emit('created', order)
    return order
  }
}

const service = new OrderService()

service.on('created', (order) => {
  console.log(`Order ${order.id} created: $${order.total}`)
})

// Once — lắng nghe 1 lần
service.once('paid', (orderId, amount) => {
  console.log(`${orderId} paid $${amount}`)
})

// Async iterator
const ac = new AbortController()
for await (const [order] of EventEmitter.on(service, 'created', { signal: ac.signal })) {
  console.log(`New order: ${order.id}`)
}

2.AbortController 和 AbortSignal

// Timeout signal
const signal = AbortSignal.timeout(5000)

// Combine signals
const controller = new AbortController()
const combined = AbortSignal.any([
  controller.signal,
  AbortSignal.timeout(10000),
])

// Sử dụng với EventEmitter
const emitter = new EventEmitter()
const promise = EventEmitter.once(emitter, 'data', { signal: combined })

// Cancel
controller.abort('User cancelled')

3. 診斷通道

import diagnostics_channel from 'node:diagnostics_channel'

// Subscribe to HTTP requests
diagnostics_channel.subscribe('http.client.request.start', (message) => {
  const { request } = message as any
  console.log(`HTTP → ${request.method} ${request.host}${request.path}`)
})

// Custom channel
const channel = diagnostics_channel.channel('myapp:db:query')

channel.subscribe((message) => {
  const { query, duration } = message as { query: string; duration: number }
  if (duration > 100) {
    console.warn(`Slow query (${duration}ms): ${query}`)
  }
})

// Publish
channel.publish({ query: 'SELECT * FROM users', duration: 150 })

4.內存洩漏檢測

import v8 from 'node:v8'
import { writeFileSync } from 'node:fs'

// Heap snapshot
function takeHeapSnapshot() {
  const snapshotStream = v8.writeHeapSnapshot()
  console.log(`Heap snapshot: ${snapshotStream}`)
}

// Memory monitoring
setInterval(() => {
  const { heapUsed, heapTotal, rss } = process.memoryUsage()
  console.log({
    heapUsed: `${(heapUsed / 1e6).toFixed(1)}MB`,
    heapTotal: `${(heapTotal / 1e6).toFixed(1)}MB`,
    rss: `${(rss / 1e6).toFixed(1)}MB`,
  })

  // Alert if memory exceeds threshold
  if (heapUsed > 500 * 1e6) {
    takeHeapSnapshot()
    console.error('Memory threshold exceeded!')
  }
}, 10000)

// EventEmitter leak warning
const emitter = new EventEmitter()
emitter.setMaxListeners(20) // Default: 10

5.Node.js 檢查器 API

import inspector from 'node:inspector/promises'

// Programmatic profiling
const session = new inspector.Session()
session.connect()

await session.post('Profiler.enable')
await session.post('Profiler.start')

// ... run code to profile ...

const { profile } = await session.post('Profiler.stop')
// Save profile for Chrome DevTools

下一篇: 從頭開始建立 HTTP 框架 — 路由器、中介軟體管道。