后端错误处理模式
概览
错误处理是后端开发中的关键组成部分,它使应用程序能够可靠、可维护和可调试地运行。它包括处理、记录和监控错误的模式和最佳实践。
错误处理包括:
- 错误分类 - 区分错误类型(操作性 vs 程序员错误)
- 自定义错误类 - 创建特定于领域的错误类
- 错误中间件 - 用于一致响应的全局错误处理程序
- 错误记录 - 记录错误以便于调试和监控
- 错误监控 - 外部监控服务(Sentry等)
- 错误恢复 - 重试、回退和断路器模式
为什么这很重要
- 减少停机时间:正确的错误处理可以减少40-60%的停机时间
- 改善用户体验:优雅的错误处理增强了用户体验
- 减少调试时间:结构化的错误记录减少了调试时间
- 提高可维护性:一致的错误处理提高了可维护性
- 增强可靠性:错误恢复模式增加了可靠性
核心概念
1. 错误分类
操作性错误(预期的):
- 网络超时
- 数据库连接失败
- 无效的用户输入
- 速率限制超出
- 外部服务不可用
程序员错误(意外的):
- 未定义的变量
- 类型错误
- 内存泄漏
- 逻辑错误
- 竞态条件
2. 自定义错误类
// 基础错误类
export class AppError extends Error {
constructor(
public statusCode: number,
public message: string,
public isOperational: boolean = true
) {
super(message)
this.name = this.constructor.name
Error.captureStackTrace(this, this.constructor)
}
}
// 特定错误类型
export class ValidationError extends AppError {
constructor(message: string = "验证失败") {
super(422, message)
}
}
export class NotFoundError extends AppError {
constructor(resource: string = "资源") {
super(404, `${resource} 未找到`)
}
}
3. 错误中间件
// 全局错误处理器
export function errorHandler(
error: Error,
req: Request,
res: Response,
next: NextFunction
): void {
// 记录错误
logger.error(error, { path: req.path, method: req.method })
// 处理操作性错误
if (error instanceof AppError) {
return res.status(error.statusCode).json({
success: false,
message: error.message,
})
}
// 处理程序员错误(不要在生产中暴露)
res.status(500).json({
success: false,
message: process.env.NODE_ENV === "production"
? "内部服务器错误"
: error.message,
})
}
4. 错误记录
// 使用Winston进行结构化日志记录
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || "info",
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: "logs/error.log", level: "error" }),
],
})
5. 错误监控
// Sentry集成
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV || "development",
beforeSend(event) {
// 过滤操作性错误
if (event.exception?.values?.[0]?.mechanism?.type === "express") {
const error = event.request?.error as any
if (error?.isOperational) {
return null
}
}
return event
},
})
6. 错误恢复模式
带有指数退避的重试:
export async function retry<T>(
fn: () => Promise<T>,
options: { maxAttempts?: number; delay?: number; backoff?: boolean } = {}
): Promise<T> {
const { maxAttempts = 3, delay = 1000, backoff = true } = options
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn()
} catch (error) {
if (attempt === maxAttempts) throw error
const waitTime = backoff ? delay * attempt : delay
await new Promise(resolve => setTimeout(resolve, waitTime))
}
}
throw new Error("超过最大重试次数")
}
回退策略:
export async function withFallback<T>(
primaryFn: () => Promise<T>,
fallbackFn: () => Promise<T>,
shouldFallback?: (error: Error) => boolean
): Promise<T> {
try {
return await primaryFn()
} catch (error) {
if (shouldFallback && !shouldFallback(error)) {
throw error
}
return await fallbackFn()
}
}
断路器:
export class CircuitBreaker {
private failures = 0
private state: "closed" | "open" | "half-open" = "closed"
async execute<T>(fn: () => Promise<T>): Promise<T> {
if (this.state === "open") {
throw new Error("断路器已打开")
}
try {
const result = await fn()
this.onSuccess()
return result
} catch (error) {
this.onFailure()
throw error
}
}
private onSuccess(): void {
this.failures = 0
this.state = "closed"
}
private onFailure(): void {
this.failures++
if (this.failures >= 5) {
this.state = "open"
}
}
}
快速开始
-
创建错误类:
export class AppError extends Error { constructor(public statusCode: number, public message: string) { super(message) this.name = this.constructor.name } } -
设置错误中间件:
app.use(errorHandler) -
配置日志记录:
import winston from "winston" export const logger = winston.createLogger({ ... }) -
添加监控:
npm install @sentry/node -
实现恢复模式:
await retry(() => fetchFromAPI(url), { maxAttempts: 3 })
生产清单
- [ ] 自定义错误类用于特定领域的错误
- [ ] 将错误分类为操作性 vs 程序员错误
- [ ] 实现全局错误中间件
- [ ] 使用一致的错误响应格式
- [ ] 使用结构化日志记录错误
- [ ] 使用外部服务监控错误(Sentry等)
- [ ] 实施错误恢复模式
- [ ] 对外部服务使用断路器
- [ ] 测试错误场景
- [ ] 为生产环境清理错误消息
- [ ] 实施瞬态错误的重试逻辑
- [ ] 对关键故障使用回退策略
- [ ] 记录错误代码和消息
- [ ] 设置关键错误的警报
- [ ] 定期审查错误日志
反模式
- 吞食错误:不要捕获并忽略错误
- 暴露堆栈跟踪:不要在生产中暴露堆栈跟踪
- 错误消息差:不要使用难以理解的错误消息
- 缺少日志记录:不要适当地记录错误
- 无监控:不要在没有错误监控的情况下运行
- 恢复差:不要未能实施错误恢复
集成点
- 验证:
03-backend-api/validation - 中间件:
03-backend-api/middleware - 监控:
14-monitoring-observability - 数据库:
04-database - 测试:
16-testing