昨天我們建立了完整的測試策略和品質保證架構,確保我們的程式品質和穩定性。今天我們要來看另一個關鍵問題:應用效能監控與最佳化。
在現代軟體開發中,效能不僅影響使用者體驗,也直接關係到系統的擴展性和運營成本:
我們的效能監控採用多層次策略:
📱 Frontend (Browser)
↓ Real User Monitoring
🖥️ Application Layer
↓ APM & Custom Metrics
🐳 Container Layer
↓ Resource Monitoring
☁️ Infrastructure Layer
↓ System Metrics
首先整合 APM 解決方案:
// packages/kyo-core/src/monitoring/performance.ts
import { performance } from 'perf_hooks';
import { EventEmitter } from 'events';
interface PerformanceMetric {
name: string;
value: number;
unit: string;
timestamp: Date;
labels?: Record<string, string>;
}
interface TimingResult {
duration: number;
timestamp: Date;
}
export class PerformanceMonitor extends EventEmitter {
private metrics: Map<string, PerformanceMetric[]> = new Map();
private activeTimers: Map<string, number> = new Map();
// 記錄計時器
startTimer(name: string, labels?: Record<string, string>): string {
const timerId = `${name}_${Date.now()}_${Math.random()}`;
this.activeTimers.set(timerId, performance.now());
return timerId;
}
endTimer(timerId: string, labels?: Record<string, string>): TimingResult {
const startTime = this.activeTimers.get(timerId);
if (!startTime) {
throw new Error(`Timer ${timerId} not found`);
}
const endTime = performance.now();
const duration = endTime - startTime;
const timestamp = new Date();
this.activeTimers.delete(timerId);
// 提取計時器名稱
const name = timerId.split('_')[0];
// 記錄指標
this.recordMetric({
name: `${name}_duration`,
value: duration,
unit: 'ms',
timestamp,
labels,
});
const result = { duration, timestamp };
this.emit('timing', { name, ...result, labels });
return result;
}
// 直接記錄指標
recordMetric(metric: PerformanceMetric): void {
if (!this.metrics.has(metric.name)) {
this.metrics.set(metric.name, []);
}
const metrics = this.metrics.get(metric.name)!;
metrics.push(metric);
// 保持最近 1000 個記錄
if (metrics.length > 1000) {
metrics.shift();
}
this.emit('metric', metric);
}
// 計數器
increment(name: string, value: number = 1, labels?: Record<string, string>): void {
this.recordMetric({
name,
value,
unit: 'count',
timestamp: new Date(),
labels,
});
}
// 儀表板
gauge(name: string, value: number, unit: string = 'unit', labels?: Record<string, string>): void {
this.recordMetric({
name,
value,
unit,
timestamp: new Date(),
labels,
});
}
// 獲取指標統計
getMetricStats(name: string, timeWindow: number = 60000): {
count: number;
min: number;
max: number;
avg: number;
p95: number;
p99: number;
} | null {
const metrics = this.metrics.get(name);
if (!metrics || metrics.length === 0) return null;
const now = Date.now();
const recentMetrics = metrics.filter(
m => now - m.timestamp.getTime() <= timeWindow
);
if (recentMetrics.length === 0) return null;
const values = recentMetrics.map(m => m.value).sort((a, b) => a - b);
const count = values.length;
const min = values[0];
const max = values[count - 1];
const avg = values.reduce((sum, val) => sum + val, 0) / count;
const p95 = values[Math.floor(count * 0.95)] || max;
const p99 = values[Math.floor(count * 0.99)] || max;
return { count, min, max, avg, p95, p99 };
}
// 獲取所有指標名稱
getMetricNames(): string[] {
return Array.from(this.metrics.keys());
}
// 清理舊數據
cleanup(maxAge: number = 3600000): void { // 1 小時
const cutoff = Date.now() - maxAge;
for (const [name, metrics] of this.metrics.entries()) {
const filtered = metrics.filter(m => m.timestamp.getTime() > cutoff);
if (filtered.length === 0) {
this.metrics.delete(name);
} else {
this.metrics.set(name, filtered);
}
}
}
}
// 全域效能監控實例
export const performanceMonitor = new PerformanceMonitor();
// 自動清理
setInterval(() => {
performanceMonitor.cleanup();
}, 300000); // 每 5 分鐘清理一次
// apps/kyo-otp-service/src/plugins/monitoring.ts
import fp from 'fastify-plugin';
import { performanceMonitor } from '@kyong/kyo-core';
declare module 'fastify' {
interface FastifyRequest {
timer?: string;
}
}
export default fp(async function (fastify) {
// 請求計時中間件
fastify.addHook('onRequest', async (request, reply) => {
const labels = {
method: request.method,
route: request.routerPath || 'unknown',
};
request.timer = performanceMonitor.startTimer('http_request', labels);
// 記錄請求數
performanceMonitor.increment('http_requests_total', 1, labels);
});
fastify.addHook('onResponse', async (request, reply) => {
if (request.timer) {
const labels = {
method: request.method,
route: request.routerPath || 'unknown',
status_code: reply.statusCode.toString(),
};
performanceMonitor.endTimer(request.timer, labels);
// 記錄回應狀態
performanceMonitor.increment('http_responses_total', 1, labels);
// 記錄錯誤率
if (reply.statusCode >= 400) {
performanceMonitor.increment('http_errors_total', 1, labels);
}
}
});
// 暴露指標端點
fastify.get('/metrics', async (request, reply) => {
const metrics = performanceMonitor.getMetricNames().map(name => {
const stats = performanceMonitor.getMetricStats(name);
return { name, stats };
}).filter(m => m.stats !== null);
return {
timestamp: new Date().toISOString(),
metrics,
uptime: process.uptime(),
memoryUsage: process.memoryUsage(),
cpuUsage: process.cpuUsage(),
};
});
});
// packages/kyo-core/src/monitoring/database.ts
import { performanceMonitor } from './performance.js';
export interface DatabaseMetrics {
queryCount: number;
slowQueries: number;
connectionPoolSize: number;
connectionPoolUsed: number;
}
export class DatabaseMonitor {
private static instance: DatabaseMonitor;
private slowQueryThreshold: number = 1000; // 1 秒
static getInstance(): DatabaseMonitor {
if (!DatabaseMonitor.instance) {
DatabaseMonitor.instance = new DatabaseMonitor();
}
return DatabaseMonitor.instance;
}
// 包裝資料庫查詢
async wrapQuery<T>(
queryName: string,
queryFn: () => Promise<T>,
params?: any[]
): Promise<T> {
const labels = { query: queryName };
const timer = performanceMonitor.startTimer('db_query', labels);
try {
const result = await queryFn();
const timing = performanceMonitor.endTimer(timer, labels);
// 記錄慢查詢
if (timing.duration > this.slowQueryThreshold) {
performanceMonitor.increment('db_slow_queries_total', 1, {
...labels,
duration: timing.duration.toString(),
});
console.warn(`Slow query detected: ${queryName} took ${timing.duration.toFixed(2)}ms`, {
params: params?.slice(0, 3), // 只記錄前 3 個參數
});
}
performanceMonitor.increment('db_queries_total', 1, {
...labels,
status: 'success',
});
return result;
} catch (error) {
performanceMonitor.endTimer(timer, { ...labels, status: 'error' });
performanceMonitor.increment('db_queries_total', 1, {
...labels,
status: 'error',
});
throw error;
}
}
// 監控連接池
recordConnectionPoolMetrics(metrics: DatabaseMetrics): void {
performanceMonitor.gauge('db_connection_pool_size', metrics.connectionPoolSize, 'connections');
performanceMonitor.gauge('db_connection_pool_used', metrics.connectionPoolUsed, 'connections');
performanceMonitor.gauge('db_connection_pool_utilization',
metrics.connectionPoolUsed / metrics.connectionPoolSize * 100, 'percent');
}
setSlowQueryThreshold(threshold: number): void {
this.slowQueryThreshold = threshold;
}
}
export const dbMonitor = DatabaseMonitor.getInstance();
// packages/kyo-core/src/monitoring/redis.ts
import { performanceMonitor } from './performance.js';
import { Redis } from 'ioredis';
export class RedisMonitor {
constructor(private redis: Redis) {
this.setupMonitoring();
}
private setupMonitoring(): void {
// 監控 Redis 命令
this.redis.on('monitor', (time, args, source, database) => {
const command = args[0]?.toLowerCase();
if (command) {
performanceMonitor.increment('redis_commands_total', 1, {
command,
database: database.toString(),
});
}
});
// 監控連接狀態
this.redis.on('connect', () => {
performanceMonitor.increment('redis_connections_total', 1, {
event: 'connect',
});
});
this.redis.on('error', (error) => {
performanceMonitor.increment('redis_errors_total', 1, {
error: error.name,
});
});
// 定期收集 Redis 資訊
setInterval(() => {
this.collectRedisInfo();
}, 30000); // 每 30 秒
}
private async collectRedisInfo(): Promise<void> {
try {
const info = await this.redis.info();
const lines = info.split('\r\n');
const metrics: Record<string, number> = {};
for (const line of lines) {
if (line.includes(':') && !line.startsWith('#')) {
const [key, value] = line.split(':');
const numValue = parseFloat(value);
if (!isNaN(numValue)) {
metrics[key] = numValue;
}
}
}
// 記錄關鍵指標
if (metrics.used_memory) {
performanceMonitor.gauge('redis_memory_used_bytes', metrics.used_memory, 'bytes');
}
if (metrics.connected_clients) {
performanceMonitor.gauge('redis_connected_clients', metrics.connected_clients, 'count');
}
if (metrics.total_commands_processed) {
performanceMonitor.gauge('redis_commands_processed_total', metrics.total_commands_processed, 'count');
}
if (metrics.keyspace_hits && metrics.keyspace_misses) {
const hitRate = metrics.keyspace_hits / (metrics.keyspace_hits + metrics.keyspace_misses) * 100;
performanceMonitor.gauge('redis_hit_rate', hitRate, 'percent');
}
} catch (error) {
console.error('Failed to collect Redis info:', error);
}
}
// 包裝 Redis 命令以進行監控
async wrapCommand<T>(
commandName: string,
commandFn: () => Promise<T>
): Promise<T> {
const labels = { command: commandName.toLowerCase() };
const timer = performanceMonitor.startTimer('redis_command', labels);
try {
const result = await commandFn();
performanceMonitor.endTimer(timer, { ...labels, status: 'success' });
return result;
} catch (error) {
performanceMonitor.endTimer(timer, { ...labels, status: 'error' });
performanceMonitor.increment('redis_command_errors_total', 1, labels);
throw error;
}
}
}
// packages/kyo-core/src/monitoring/system.ts
import { performanceMonitor } from './performance.js';
import { promisify } from 'util';
import { exec } from 'child_process';
const execAsync = promisify(exec);
export class SystemMonitor {
private interval: NodeJS.Timeout | null = null;
start(intervalMs: number = 10000): void { // 每 10 秒
if (this.interval) {
return; // 已經在運行
}
this.interval = setInterval(() => {
this.collectSystemMetrics();
}, intervalMs);
// 立即收集一次
this.collectSystemMetrics();
}
stop(): void {
if (this.interval) {
clearInterval(this.interval);
this.interval = null;
}
}
private async collectSystemMetrics(): Promise<void> {
try {
// Node.js 內建指標
const memUsage = process.memoryUsage();
const cpuUsage = process.cpuUsage();
// 記憶體指標
performanceMonitor.gauge('nodejs_memory_rss_bytes', memUsage.rss, 'bytes');
performanceMonitor.gauge('nodejs_memory_heap_used_bytes', memUsage.heapUsed, 'bytes');
performanceMonitor.gauge('nodejs_memory_heap_total_bytes', memUsage.heapTotal, 'bytes');
performanceMonitor.gauge('nodejs_memory_external_bytes', memUsage.external, 'bytes');
// CPU 指標(微秒)
performanceMonitor.gauge('nodejs_cpu_user_microseconds', cpuUsage.user, 'microseconds');
performanceMonitor.gauge('nodejs_cpu_system_microseconds', cpuUsage.system, 'microseconds');
// 事件循環延遲
const eventLoopDelay = await this.measureEventLoopDelay();
performanceMonitor.gauge('nodejs_eventloop_delay_ms', eventLoopDelay, 'ms');
// 作業系統指標(如果可用)
await this.collectOSMetrics();
} catch (error) {
console.error('Failed to collect system metrics:', error);
}
}
private async measureEventLoopDelay(): Promise<number> {
return new Promise((resolve) => {
const start = process.hrtime.bigint();
setImmediate(() => {
const delay = Number(process.hrtime.bigint() - start) / 1000000; // 轉換為毫秒
resolve(delay);
});
});
}
private async collectOSMetrics(): Promise<void> {
try {
// 獲取系統負載(Linux/macOS)
if (process.platform !== 'win32') {
const { stdout: loadavg } = await execAsync('cat /proc/loadavg 2>/dev/null || uptime | grep -o "load average.*" | cut -d: -f2');
const loads = loadavg.trim().split(/\s+/);
if (loads.length >= 3) {
performanceMonitor.gauge('system_load_1m', parseFloat(loads[0]) || 0, 'load');
performanceMonitor.gauge('system_load_5m', parseFloat(loads[1]) || 0, 'load');
performanceMonitor.gauge('system_load_15m', parseFloat(loads[2]) || 0, 'load');
}
}
// 獲取記憶體使用情況(Linux)
if (process.platform === 'linux') {
const { stdout: meminfo } = await execAsync('cat /proc/meminfo 2>/dev/null || echo ""');
const memData = this.parseMeminfo(meminfo);
if (memData.MemTotal && memData.MemAvailable) {
const usedMemory = memData.MemTotal - memData.MemAvailable;
const memoryUtilization = (usedMemory / memData.MemTotal) * 100;
performanceMonitor.gauge('system_memory_total_bytes', memData.MemTotal * 1024, 'bytes');
performanceMonitor.gauge('system_memory_used_bytes', usedMemory * 1024, 'bytes');
performanceMonitor.gauge('system_memory_utilization', memoryUtilization, 'percent');
}
}
} catch (error) {
// 忽略系統指標收集錯誤
console.debug('Could not collect OS metrics:', error.message);
}
}
private parseMeminfo(meminfo: string): Record<string, number> {
const data: Record<string, number> = {};
const lines = meminfo.split('\n');
for (const line of lines) {
const match = line.match(/^(\w+):\s+(\d+)\s+kB$/);
if (match) {
data[match[1]] = parseInt(match[2], 10);
}
}
return data;
}
}
export const systemMonitor = new SystemMonitor();
// packages/kyo-core/src/monitoring/profiler.ts
import { performanceMonitor } from './performance.js';
interface BottleneckAnalysis {
endpoint: string;
averageResponseTime: number;
p95ResponseTime: number;
errorRate: number;
requestCount: number;
severity: 'low' | 'medium' | 'high' | 'critical';
}
interface ResourceAnalysis {
type: 'cpu' | 'memory' | 'database' | 'redis';
utilization: number;
trend: 'increasing' | 'stable' | 'decreasing';
recommendation: string;
}
export class PerformanceAnalyzer {
// 分析API端點效能
analyzeEndpointPerformance(timeWindow: number = 300000): BottleneckAnalysis[] { // 5 分鐘
const results: BottleneckAnalysis[] = [];
const requestMetrics = performanceMonitor.getMetricStats('http_request_duration', timeWindow);
if (!requestMetrics) return results;
// 這裡應該按路由分組分析,簡化版本使用總體數據
const errorStats = performanceMonitor.getMetricStats('http_errors_total', timeWindow);
const totalRequests = requestMetrics.count;
const errorCount = errorStats?.count || 0;
const errorRate = totalRequests > 0 ? (errorCount / totalRequests) * 100 : 0;
let severity: BottleneckAnalysis['severity'] = 'low';
if (requestMetrics.p95 > 2000 || errorRate > 5) {
severity = 'critical';
} else if (requestMetrics.p95 > 1000 || errorRate > 2) {
severity = 'high';
} else if (requestMetrics.p95 > 500 || errorRate > 1) {
severity = 'medium';
}
results.push({
endpoint: 'overall',
averageResponseTime: requestMetrics.avg,
p95ResponseTime: requestMetrics.p95,
errorRate,
requestCount: totalRequests,
severity,
});
return results;
}
// 分析資源使用情況
analyzeResourceUsage(): ResourceAnalysis[] {
const results: ResourceAnalysis[] = [];
// 記憶體分析
const memoryStats = performanceMonitor.getMetricStats('nodejs_memory_heap_used_bytes');
if (memoryStats) {
const memoryUtilization = (memoryStats.avg / (1024 * 1024 * 1024)) * 100; // GB 轉百分比
results.push({
type: 'memory',
utilization: memoryUtilization,
trend: 'stable', // 簡化版本,實際應該分析趨勢
recommendation: memoryUtilization > 80
? 'Consider increasing memory allocation or optimize memory usage'
: 'Memory usage is within acceptable range',
});
}
// 資料庫分析
const dbStats = performanceMonitor.getMetricStats('db_query_duration');
if (dbStats) {
results.push({
type: 'database',
utilization: dbStats.p95,
trend: 'stable',
recommendation: dbStats.p95 > 1000
? 'Database queries are slow, consider adding indexes or optimizing queries'
: 'Database performance is acceptable',
});
}
// Redis 分析
const redisStats = performanceMonitor.getMetricStats('redis_command_duration');
if (redisStats) {
results.push({
type: 'redis',
utilization: redisStats.p95,
trend: 'stable',
recommendation: redisStats.p95 > 100
? 'Redis commands are slow, check network latency or Redis server performance'
: 'Redis performance is good',
});
}
return results;
}
// 生成效能報告
generatePerformanceReport(): {
timestamp: string;
bottlenecks: BottleneckAnalysis[];
resources: ResourceAnalysis[];
recommendations: string[];
} {
const bottlenecks = this.analyzeEndpointPerformance();
const resources = this.analyzeResourceUsage();
const recommendations: string[] = [];
// 基於分析結果生成建議
bottlenecks.forEach(bottleneck => {
if (bottleneck.severity === 'critical') {
recommendations.push(`URGENT: ${bottleneck.endpoint} has critical performance issues (P95: ${bottleneck.p95ResponseTime.toFixed(0)}ms, Error Rate: ${bottleneck.errorRate.toFixed(1)}%)`);
} else if (bottleneck.severity === 'high') {
recommendations.push(`HIGH: ${bottleneck.endpoint} needs optimization (P95: ${bottleneck.p95ResponseTime.toFixed(0)}ms)`);
}
});
resources.forEach(resource => {
if (resource.utilization > 80) {
recommendations.push(`RESOURCE: ${resource.type} utilization is high (${resource.utilization.toFixed(1)}%) - ${resource.recommendation}`);
}
});
return {
timestamp: new Date().toISOString(),
bottlenecks,
resources,
recommendations,
};
}
}
export const performanceAnalyzer = new PerformanceAnalyzer();
// apps/kyo-dashboard/src/utils/performance.ts
interface WebVitalsMetric {
name: string;
value: number;
rating: 'good' | 'needs-improvement' | 'poor';
timestamp: number;
}
export class FrontendPerformanceMonitor {
private metrics: WebVitalsMetric[] = [];
private observer: PerformanceObserver | null = null;
init(): void {
// 監控 Web Vitals
this.initWebVitals();
// 監控資源載入
this.initResourceTiming();
// 監控導航
this.initNavigationTiming();
}
private initWebVitals(): void {
if ('PerformanceObserver' in window) {
// Largest Contentful Paint (LCP)
this.observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1] as any;
if (lastEntry) {
this.recordMetric({
name: 'LCP',
value: lastEntry.startTime,
rating: this.rateLCP(lastEntry.startTime),
timestamp: Date.now(),
});
}
});
this.observer.observe({ entryTypes: ['largest-contentful-paint'] });
// Cumulative Layout Shift (CLS)
let clsValue = 0;
const clsObserver = new PerformanceObserver((list) => {
for (const entry of list.getEntries() as any[]) {
if (!entry.hadRecentInput) {
clsValue += entry.value;
}
}
this.recordMetric({
name: 'CLS',
value: clsValue,
rating: this.rateCLS(clsValue),
timestamp: Date.now(),
});
});
clsObserver.observe({ entryTypes: ['layout-shift'] });
}
// First Input Delay (FID) - 使用事件監聽器模擬
let isFirstInput = true;
const firstInputHandler = (event: Event) => {
if (isFirstInput) {
isFirstInput = false;
const fid = performance.now() - (event as any).timeStamp;
this.recordMetric({
name: 'FID',
value: fid,
rating: this.rateFID(fid),
timestamp: Date.now(),
});
// 移除監聽器
['click', 'keydown', 'touchstart'].forEach(type => {
document.removeEventListener(type, firstInputHandler, true);
});
}
};
['click', 'keydown', 'touchstart'].forEach(type => {
document.addEventListener(type, firstInputHandler, true);
});
}
private initResourceTiming(): void {
if ('PerformanceObserver' in window) {
const resourceObserver = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
const resourceEntry = entry as PerformanceResourceTiming;
// 記錄大型資源載入時間
if (resourceEntry.transferSize > 100000) { // > 100KB
this.recordMetric({
name: 'resource_load_time',
value: resourceEntry.responseEnd - resourceEntry.startTime,
rating: 'good', // 簡化評級
timestamp: Date.now(),
});
}
}
});
resourceObserver.observe({ entryTypes: ['resource'] });
}
}
private initNavigationTiming(): void {
window.addEventListener('load', () => {
const navigation = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming;
if (navigation) {
// Time to First Byte (TTFB)
const ttfb = navigation.responseStart - navigation.requestStart;
this.recordMetric({
name: 'TTFB',
value: ttfb,
rating: this.rateTTFB(ttfb),
timestamp: Date.now(),
});
// DOM Content Loaded
const dcl = navigation.domContentLoadedEventEnd - navigation.navigationStart;
this.recordMetric({
name: 'DCL',
value: dcl,
rating: 'good', // 簡化評級
timestamp: Date.now(),
});
// Page Load
const loadTime = navigation.loadEventEnd - navigation.navigationStart;
this.recordMetric({
name: 'page_load',
value: loadTime,
rating: 'good', // 簡化評級
timestamp: Date.now(),
});
}
});
}
private recordMetric(metric: WebVitalsMetric): void {
this.metrics.push(metric);
// 保持最近 100 個指標
if (this.metrics.length > 100) {
this.metrics.shift();
}
// 發送到後端
this.sendMetricToBackend(metric);
}
private async sendMetricToBackend(metric: WebVitalsMetric): Promise<void> {
try {
await fetch('/api/metrics/frontend', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
...metric,
userAgent: navigator.userAgent,
url: window.location.href,
viewport: {
width: window.innerWidth,
height: window.innerHeight,
},
}),
});
} catch (error) {
console.debug('Failed to send metric to backend:', error);
}
}
// Web Vitals 評級函數
private rateLCP(value: number): WebVitalsMetric['rating'] {
if (value <= 2500) return 'good';
if (value <= 4000) return 'needs-improvement';
return 'poor';
}
private rateFID(value: number): WebVitalsMetric['rating'] {
if (value <= 100) return 'good';
if (value <= 300) return 'needs-improvement';
return 'poor';
}
private rateCLS(value: number): WebVitalsMetric['rating'] {
if (value <= 0.1) return 'good';
if (value <= 0.25) return 'needs-improvement';
return 'poor';
}
private rateTTFB(value: number): WebVitalsMetric['rating'] {
if (value <= 800) return 'good';
if (value <= 1800) return 'needs-improvement';
return 'poor';
}
getMetrics(): WebVitalsMetric[] {
return [...this.metrics];
}
getMetricSummary(): Record<string, { avg: number; rating: WebVitalsMetric['rating'] }> {
const summary: Record<string, { values: number[]; ratings: WebVitalsMetric['rating'][] }> = {};
this.metrics.forEach(metric => {
if (!summary[metric.name]) {
summary[metric.name] = { values: [], ratings: [] };
}
summary[metric.name].values.push(metric.value);
summary[metric.name].ratings.push(metric.rating);
});
const result: Record<string, { avg: number; rating: WebVitalsMetric['rating'] }> = {};
Object.entries(summary).forEach(([name, data]) => {
const avg = data.values.reduce((sum, val) => sum + val, 0) / data.values.length;
const poorCount = data.ratings.filter(r => r === 'poor').length;
const goodCount = data.ratings.filter(r => r === 'good').length;
let overallRating: WebVitalsMetric['rating'] = 'needs-improvement';
if (poorCount / data.ratings.length > 0.25) {
overallRating = 'poor';
} else if (goodCount / data.ratings.length > 0.75) {
overallRating = 'good';
}
result[name] = { avg, rating: overallRating };
});
return result;
}
destroy(): void {
if (this.observer) {
this.observer.disconnect();
this.observer = null;
}
}
}
// 全域實例
export const frontendPerformanceMonitor = new FrontendPerformanceMonitor();
// 自動初始化
if (typeof window !== 'undefined') {
frontendPerformanceMonitor.init();
}
// packages/kyo-core/src/monitoring/optimizer.ts
import { performanceAnalyzer } from './profiler.js';
import { performanceMonitor } from './performance.js';
interface OptimizationSuggestion {
category: 'database' | 'cache' | 'api' | 'frontend' | 'infrastructure';
priority: 'low' | 'medium' | 'high' | 'critical';
title: string;
description: string;
implementation: string;
estimatedImpact: string;
estimatedEffort: 'low' | 'medium' | 'high';
}
export class PerformanceOptimizer {
generateOptimizationSuggestions(): OptimizationSuggestion[] {
const suggestions: OptimizationSuggestion[] = [];
const report = performanceAnalyzer.generatePerformanceReport();
// 分析資料庫效能
const dbBottlenecks = report.bottlenecks.filter(b =>
b.averageResponseTime > 500 || b.p95ResponseTime > 1000
);
if (dbBottlenecks.length > 0) {
suggestions.push({
category: 'database',
priority: 'high',
title: '資料庫查詢優化',
description: '檢測到資料庫查詢延遲較高,建議優化慢查詢',
implementation: '1. 添加適當的索引\n2. 優化複雜查詢\n3. 考慮使用查詢快取\n4. 分析查詢執行計畫',
estimatedImpact: '回應時間可改善 30-50%',
estimatedEffort: 'medium',
});
}
// 分析快取使用
const redisHitRate = performanceMonitor.getMetricStats('redis_hit_rate');
if (redisHitRate && redisHitRate.avg < 80) {
suggestions.push({
category: 'cache',
priority: 'medium',
title: '快取策略優化',
description: `Redis 快取命中率較低 (${redisHitRate.avg.toFixed(1)}%),建議改善快取策略`,
implementation: '1. 增加快取TTL\n2. 預熱常用資料\n3. 優化快取鍵設計\n4. 實施多層快取',
estimatedImpact: '減少 20-30% 資料庫負載',
estimatedEffort: 'low',
});
}
// 分析記憶體使用
const memoryStats = performanceMonitor.getMetricStats('nodejs_memory_heap_used_bytes');
if (memoryStats && memoryStats.avg > 500 * 1024 * 1024) { // > 500MB
suggestions.push({
category: 'infrastructure',
priority: 'medium',
title: '記憶體使用優化',
description: '應用程式記憶體使用量較高,建議進行記憶體優化',
implementation: '1. 分析記憶體洩漏\n2. 優化物件生命週期\n3. 使用記憶體池\n4. 考慮垂直擴展',
estimatedImpact: '提升應用穩定性,降低 OOM 風險',
estimatedEffort: 'high',
});
}
// API 效能分析
const highErrorRate = report.bottlenecks.filter(b => b.errorRate > 1);
if (highErrorRate.length > 0) {
suggestions.push({
category: 'api',
priority: 'high',
title: 'API 錯誤率優化',
description: '檢測到較高的 API 錯誤率,建議改善錯誤處理',
implementation: '1. 加強輸入驗證\n2. 改善錯誤處理邏輯\n3. 添加重試機制\n4. 實施斷路器模式',
estimatedImpact: '降低錯誤率,提升使用者體驗',
estimatedEffort: 'medium',
});
}
// 前端效能建議
suggestions.push({
category: 'frontend',
priority: 'low',
title: '前端效能優化',
description: '持續優化前端效能,提升使用者體驗',
implementation: '1. 程式碼分割\n2. 懶載入\n3. 圖片優化\n4. CDN 使用\n5. Bundle 大小優化',
estimatedImpact: '改善載入時間 20-40%',
estimatedEffort: 'medium',
});
return suggestions.sort((a, b) => {
const priorityOrder = { critical: 4, high: 3, medium: 2, low: 1 };
return priorityOrder[b.priority] - priorityOrder[a.priority];
});
}
// 生成效能儀表板數據
generateDashboardData(): {
overview: {
avgResponseTime: number;
errorRate: number;
throughput: number;
uptime: number;
};
trends: {
responseTime: number[];
errorRate: number[];
throughput: number[];
};
alerts: {
level: 'info' | 'warning' | 'error';
message: string;
timestamp: Date;
}[];
} {
const report = performanceAnalyzer.generatePerformanceReport();
const requestStats = performanceMonitor.getMetricStats('http_request_duration');
const errorStats = performanceMonitor.getMetricStats('http_errors_total');
const totalStats = performanceMonitor.getMetricStats('http_requests_total');
const overview = {
avgResponseTime: requestStats?.avg || 0,
errorRate: totalStats && errorStats ? (errorStats.count / totalStats.count) * 100 : 0,
throughput: totalStats?.count || 0,
uptime: process.uptime(),
};
// 簡化的趨勢數據(實際應該從時間序列資料庫獲取)
const trends = {
responseTime: Array(10).fill(0).map(() => Math.random() * 1000 + 200),
errorRate: Array(10).fill(0).map(() => Math.random() * 5),
throughput: Array(10).fill(0).map(() => Math.random() * 100 + 50),
};
const alerts = report.recommendations.map(rec => ({
level: rec.includes('URGENT') ? 'error' as const :
rec.includes('HIGH') ? 'warning' as const : 'info' as const,
message: rec,
timestamp: new Date(),
}));
return { overview, trends, alerts };
}
}
export const performanceOptimizer = new PerformanceOptimizer();
我們建立了全方位的效能監控和優化架構:
✅ 多層監控:應用、資料庫、Redis、系統資源全覆蓋
✅ 即時指標:自動收集和分析關鍵效能指標
✅ 智能分析:自動識別效能瓶頸和優化機會
✅ 前端監控:Web Vitals 和使用者體驗指標
✅ 優化建議:基於數據的具體優化建議
✅ 可視化監控:效能儀表板和趨勢分析