15 个真实场景案例,每个案例包含问题描述、排查步骤、解决方案和预防措施
某电商平台在用户提交订单时,前端调用 POST /api/orders 接口,偶尔返回 400 错误,用户无法完成下单。
// 修复前端代码,确保正确设置 Content-Type 和转义特殊字符
async function submitOrder(orderData) {
// 转义特殊字符
const sanitizedData = JSON.stringify(orderData)
.replace(/\n/g, '\\n')
.replace(/"/g, '\\"');
const response = await fetch('/api/orders', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: sanitizedData
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.details || '订单提交失败');
}
return response.json();
}
某 SaaS 平台用户反馈登录成功后,访问受保护页面时仍提示"未授权",需要重新登录。
前端代码中 Authorization 头的 schema 写错,使用了"JWT"而非标准的"Bearer"。
// 修复 Authorization 头格式
function getAuthHeaders() {
const token = localStorage.getItem('access_token');
return {
'Authorization': `Bearer ${token}`, // 使用 Bearer 而非 JWT
'Content-Type': 'application/json'
};
}
// 添加 token 刷新逻辑
async function refreshToken() {
const refreshToken = localStorage.getItem('refresh_token');
const response = await fetch('/api/auth/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refresh_token: refreshToken })
});
if (response.ok) {
const data = await response.json();
localStorage.setItem('access_token', data.access_token);
return data.access_token;
}
// Token 刷新失败,跳转到登录页
window.location.href = '/login';
}
某后台管理系统,管理员账号登录后访问用户管理接口 DELETE /api/users/:id,返回 403 Forbidden。
生产环境的 RBAC 权限配置与测试环境不一致,管理员账号缺少"admin:write"权限声明。
// 后端权限配置修复(Node.js + Express 示例)
const permissions = {
'admin:read': ['GET'],
'admin:write': ['GET', 'POST', 'PUT', 'DELETE'],
'user:read': ['GET'],
'user:write': ['GET', 'PUT']
};
function checkPermission(requiredPermission, method) {
const allowedMethods = permissions[requiredPermission];
if (!allowedMethods || !allowedMethods.includes(method)) {
return false;
}
return true;
}
// 前端请求时确保携带正确的权限 scope
// 或在登录时请求包含完整权限的 token
某 SPA 应用使用 React Router,用户直接访问 https://example.com/users/123 或刷新页面时返回 404。
SPA 应用的前端路由(如 React Router 的 BrowserRouter)需要后端服务器将所有未知路径重写到 index.html,由前端 JavaScript 处理路由。nginx 缺少此配置导致直接访问非根路径时返回 404。
# nginx 配置修复
server {
listen 80;
server_name example.com;
root /var/www/html;
index index.html;
location / {
# 尝试文件,不存在则返回 index.html 让前端路由处理
try_files $uri $uri/ /index.html;
}
# API 请求代理到后端
location /api/ {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
某 API 网关配置后,客户端发送 POST 请求到/api/data 返回 405 Method Not Allowed。
nginx 网关配置中使用了 limit_except 指令限制了允许的方法,但未包含 POST。
# 修复前的 nginx 配置(错误)
location /api/ {
limit_except GET HEAD OPTIONS {
deny all;
}
proxy_pass http://backend;
}
# 修复后的 nginx 配置
location /api/ {
# 允许所有常用方法
limit_except GET POST PUT DELETE PATCH HEAD OPTIONS {
deny all;
}
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# 或者完全移除 limit_except,由后端控制方法限制
某文件管理系统,用户上传超过 50MB 的文件时,上传过程中连接被断开,返回 408 Request Timeout。
nginx 的 client_body_timeout 设置过短(60 秒),慢速网络用户上传大文件时超过此时间导致连接被断开。
# nginx 配置优化
http {
# 增加客户端请求体超时时间(针对大文件上传)
client_body_timeout 600s; # 10 分钟
# 增加客户端请求头超时时间
client_header_timeout 60s;
# 增加发送响应超时时间
send_timeout 600s;
# 增加客户端请求体最大大小
client_max_body_size 500M; # 允许最大 500MB
server {
location /upload {
# 针对上传路径的单独配置
client_body_timeout 1800s; # 30 分钟
client_max_body_size 500M;
proxy_pass http://backend;
}
}
}
# 前端添加上传进度和重试逻辑
function uploadFile(file) {
const formData = new FormData();
formData.append('file', file);
fetch('/upload', {
method: 'POST',
body: formData,
// 注意:fetch 本身不支持上传进度,需使用 XMLHttpRequest
});
}
// 使用 XMLHttpRequest 获取上传进度
function uploadFileWithProgress(file) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', '/upload', true);
xhr.upload.onprogress = (event) => {
if (event.lengthComputable) {
const percent = (event.loaded / event.total) * 100;
console.log(`上传进度:${percent.toFixed(2)}%`);
}
};
xhr.onload = () => {
if (xhr.status === 200) {
resolve(xhr.response);
} else {
reject(new Error(`上传失败:${xhr.status}`));
}
};
xhr.onerror = () => reject(new Error('网络错误'));
xhr.ontimeout = () => reject(new Error('上传超时'));
xhr.timeout = 1800000; // 30 分钟超时
xhr.send(file);
});
}
某数据同步服务在批量导入数据时,调用第三方 API 频繁收到 429 错误,导致同步失败。
代码中未实现速率限制和正确的重试策略,并发请求超过第三方 API 的限流阈值,且收到 429 后立即重试导致问题恶化。
// 实现带速率限制和指数退避重试的请求函数
class RateLimitedClient {
constructor(requestsPerMinute = 100) {
this.requestsPerMinute = requestsPerMinute;
this.interval = 60000 / requestsPerMinute; // 毫秒
this.lastRequestTime = 0;
this.queue = [];
}
async request(url, options) {
// 等待速率限制
await this.throttle();
try {
const response = await fetch(url, options);
if (response.status === 429) {
// 从 Retry-After 头获取等待时间
const retryAfter = response.headers.get('Retry-After') || 60;
await this.sleep(retryAfter * 1000);
return this.request(url, options); // 重试
}
return response;
} catch (error) {
// 网络错误,指数退避重试
throw error;
}
}
async throttle() {
const now = Date.now();
const waitTime = this.lastRequestTime + this.interval - now;
if (waitTime > 0) {
await this.sleep(waitTime);
}
this.lastRequestTime = Date.now();
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
// 使用示例
const client = new RateLimitedClient(100); // 100 次/分钟
async function batchImport(data) {
const results = [];
for (const item of data) {
const response = await client.request('/api/import', {
method: 'POST',
body: JSON.stringify(item)
});
results.push(await response.json());
}
return results;
}
某电商网站在高峰期频繁出现 500 错误,用户无法访问商品详情页和下单。
-- 1. 添加数据库索引
CREATE INDEX idx_products_category_status ON products(category_id, status);
CREATE INDEX idx_products_created_at ON products(created_at DESC);
-- 2. 优化查询,使用 JOIN 替代 N+1 查询
-- 优化前(N+1 查询)
SELECT * FROM products WHERE category_id = 1;
-- 然后对每个 product 查询 category
SELECT * FROM categories WHERE id = ?;
-- 优化后(单次 JOIN 查询)
SELECT p.*, c.name as category_name
FROM products p
JOIN categories c ON p.category_id = c.id
WHERE p.category_id = 1;
-- 3. 调整连接池配置(Node.js + pg 示例)
const pool = new Pool({
host: '10.0.0.5',
port: 5432,
database: 'ecommerce',
user: 'app_user',
password: process.env.DB_PASSWORD,
max: 50, // 最大连接数从 20 增加到 50
min: 10, // 最小空闲连接
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 5000,
});
某微服务架构应用,在后端服务部署重启期间,用户访问返回 502 Bad Gateway。
部署时先停止旧服务再启动新服务,导致短暂的服务不可用窗口。nginx 未配置健康检查和故障转移,所有请求都发往已停止的服务。
# 1. nginx upstream 配置优化(多实例 + 健康检查)
upstream backend {
least_conn; # 最少连接负载均衡
server 10.0.0.10:8080 max_fails=3 fail_timeout=30s;
server 10.0.0.11:8080 max_fails=3 fail_timeout=30s;
server 10.0.0.12:8080 max_fails=3 fail_timeout=30s backup; # 备份服务器
keepalive 32; # 保持长连接
}
server {
location / {
proxy_pass http://backend;
proxy_connect_timeout 5s; # 连接超时
proxy_read_timeout 30s; # 读取超时
proxy_send_timeout 30s; # 发送超时
# 失败重试
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 3;
}
}
# 2. 使用滚动部署(Kubernetes 示例)
# deployment.yaml
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 最多超出期望副本数
maxUnavailable: 0 # 部署期间不可用副本数为 0
# 就绪探针,确保新 Pod 就绪后才接收流量
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
failureThreshold: 3
# 存活探针
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
# 3. 添加优雅关闭处理(Node.js 示例)
process.on('SIGTERM', () => {
console.log('收到 SIGTERM 信号,开始优雅关闭...');
// 停止接收新连接
server.close(() => {
console.log('HTTP 服务器已关闭');
// 关闭数据库连接
db.close(() => {
console.log('数据库连接已关闭');
process.exit(0);
});
});
// 强制退出(10 秒后)
setTimeout(() => {
console.error('优雅关闭超时,强制退出');
process.exit(1);
}, 10000);
});
某秒杀活动期间,订单服务返回 503 Service Unavailable,大量用户无法下单。
# 1. nginx 限流配置
http {
# 定义限流区域(按 IP)
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
# 定义连接数限制
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
server {
location /api/orders {
# 请求频率限制
limit_req zone=api_limit burst=20 nodelay;
# 连接数限制
limit_conn conn_limit 10;
# 超过限制返回 503
limit_req_status 503;
limit_conn_status 503;
proxy_pass http://backend;
}
}
}
# 2. 应用层熔断降级(Node.js + Opossum 示例)
const CircuitBreaker = require('opossum');
const options = {
timeout: 3000, // 3 秒超时
errorThresholdPercentage: 50, // 50% 错误率触发熔断
resetTimeout: 30000 // 30 秒后尝试恢复
};
const orderBreaker = new CircuitBreaker(createOrder, options);
orderBreaker.fallback(() => {
// 降级处理:返回排队中状态,建议用户稍后重试
return {
status: 'queued',
message: '订单处理繁忙,请稍后在订单中心查看结果',
retryAfter: 30
};
});
orderBreaker.on('open', () => {
console.log('熔断器打开,服务降级');
// 发送告警
});
async function createOrder(orderData) {
// 正常的订单创建逻辑
return await orderService.create(orderData);
}
# 3. 消息队列削峰
# 将同步下单改为异步处理
async function placeOrder(orderData) {
// 快速响应,将订单放入消息队列
await messageQueue.publish('orders', orderData);
return {
orderId: generateOrderId(),
status: 'processing',
message: '订单已提交,处理中...'
};
}
某报表导出功能,用户导出大数据量报表时返回 504 Gateway Timeout。
报表导出是长时间运行任务,但 nginx 的 proxy_read_timeout 设置过短(60 秒),导致后端尚未完成处理 nginx 就返回 504。
# 方案 1:调整 nginx 超时配置(针对导出路径)
server {
location /api/export {
# 增加超时时间
proxy_read_timeout 300s; # 5 分钟
proxy_send_timeout 300s;
proxy_connect_timeout 10s;
# 关闭请求缓冲,支持流式传输
proxy_buffering off;
proxy_request_buffering off;
proxy_pass http://backend;
}
}
# 方案 2:异步导出(推荐)
# 前端请求
async function requestExport(reportParams) {
const response = await fetch('/api/export/request', {
method: 'POST',
body: JSON.stringify(reportParams)
});
const { exportId, estimatedTime } = await response.json();
// 轮询导出状态
return pollExportStatus(exportId);
}
async function pollExportStatus(exportId) {
const maxAttempts = 60; // 最多轮询 60 次
const interval = 5000; // 每 5 秒轮询一次
for (let i = 0; i < maxAttempts; i++) {
const response = await fetch(`/api/export/status/${exportId}`);
const status = await response.json();
if (status.status === 'completed') {
// 导出完成,下载文件
window.location.href = `/api/export/download/${exportId}`;
return;
}
if (status.status === 'failed') {
throw new Error('导出失败:' + status.error);
}
// 等待后继续轮询
await new Promise(r => setTimeout(r, interval));
}
throw new Error('导出超时');
}
# 后端处理
@app.route('/api/export/request', methods=['POST'])
def request_export():
# 创建导出任务,放入后台队列
export_id = generate_export_id()
task_queue.enqueue('generate_report', export_id, request.json)
return jsonify({
'exportId': export_id,
'estimatedTime': 120 # 预计 2 分钟
})
@app.route('/api/export/status/')
def export_status(export_id):
task = task_queue.get_task(export_id)
return jsonify({
'status': task.status, # pending/processing/completed/failed
'progress': task.progress,
'error': task.error
})
某移动 App 用户反馈使用一段时间后,所有操作都提示"登录已过期",需要重新登录。
access_token 有效期较短(2 小时),但前端未实现自动刷新机制。用户长时间使用 App 时 token 过期,导致所有请求返回 401。
// 前端实现 token 自动刷新(axios 拦截器示例)
import axios from 'axios';
const api = axios.create({
baseURL: '/api',
timeout: 10000
});
// 请求拦截器:添加 token
api.interceptors.request.use(config => {
const token = localStorage.getItem('access_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// 响应拦截器:处理 401 错误,自动刷新 token
let isRefreshing = false;
let failedQueue = [];
const processQueue = (error, token = null) => {
failedQueue.forEach(prom => {
if (error) {
prom.reject(error);
} else {
prom.resolve(token);
}
});
failedQueue = [];
};
api.interceptors.response.use(
response => response,
async error => {
const originalRequest = error.config;
if (error.response?.status === 401 && !originalRequest._retry) {
if (isRefreshing) {
return new Promise((resolve, reject) => {
failedQueue.push({ resolve, reject });
})
.then(token => {
originalRequest.headers.Authorization = `Bearer ${token}`;
return api(originalRequest);
})
.catch(err => Promise.reject(err));
}
originalRequest._retry = true;
isRefreshing = true;
try {
const refreshToken = localStorage.getItem('refresh_token');
const response = await axios.post('/api/auth/refresh', {
refresh_token: refreshToken
});
const { access_token } = response.data;
localStorage.setItem('access_token', access_token);
processQueue(null, access_token);
originalRequest.headers.Authorization = `Bearer ${access_token}`;
return api(originalRequest);
} catch (refreshError) {
processQueue(refreshError, null);
// 刷新失败,跳转到登录页
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
window.location.href = '/login';
return Promise.reject(refreshError);
} finally {
isRefreshing = false;
}
}
return Promise.reject(error);
}
);
某 Web 应用前端部署在 https://app.example.com,后端 API 在 https://api.example.com,前端调用 API 时浏览器控制台报 CORS 错误。
后端 CORS 配置只允许了开发环境的 localhost,未添加生产环境的前端域名。浏览器因同源策略阻止跨域请求。
# Node.js + Express CORS 配置
const express = require('express');
const cors = require('cors');
const app = express();
const corsOptions = {
origin: function (origin, callback) {
// 允许的来源列表
const allowedOrigins = [
'https://app.example.com',
'https://www.example.com',
'http://localhost:3000' // 开发环境
];
// 允许无 origin(如移动端 App、curl)
if (!origin) return callback(null, true);
if (allowedOrigins.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
credentials: true, // 允许携带凭证(cookies)
maxAge: 86400 // 预检请求缓存 24 小时
};
app.use(cors(corsOptions));
# Nginx CORS 配置
server {
listen 443 ssl;
server_name api.example.com;
location / {
# CORS 头配置
add_header 'Access-Control-Allow-Origin' 'https://app.example.com' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Requested-With' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Max-Age' '86400' always;
# 处理预检请求
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' 'https://app.example.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Requested-With';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Max-Age' '86400';
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' '0';
return 204;
}
proxy_pass http://backend;
}
}
某 Java 后端服务,部分用户访问个人中心时返回 500 错误,错误日志显示 NullPointerException。
代码中直接链式调用 user.getAddress().getCity(),但部分老用户数据中 address 为 null,导致空指针异常。
// 修复前(有问题)
public UserProfileDTO getProfile(Long userId) {
User user = userRepository.findById(userId);
UserProfileDTO dto = new UserProfileDTO();
dto.setName(user.getName());
dto.setCity(user.getAddress().getCity()); // 可能 NPE
return dto;
}
// 修复后(防御式编程)
public UserProfileDTO getProfile(Long userId) {
User user = userRepository.findById(userId);
if (user == null) {
throw new UserNotFoundException(userId);
}
UserProfileDTO dto = new UserProfileDTO();
dto.setName(user.getName());
// 空值检查
if (user.getAddress() != null) {
dto.setCity(user.getAddress().getCity());
dto.setDistrict(user.getAddress().getDistrict());
} else {
dto.setCity("");
dto.setDistrict("");
}
return dto;
}
// 或使用 Java 8 Optional
public UserProfileDTO getProfile(Long userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new UserNotFoundException(userId));
UserProfileDTO dto = new UserProfileDTO();
dto.setName(user.getName());
dto.setCity(Optional.ofNullable(user.getAddress())
.map(Address::getCity)
.orElse(""));
return dto;
}
某 API 接口接收 JSON 请求体,部分客户端请求返回 400 错误,提示"Invalid JSON"。
客户端代码使用字符串拼接构造 JSON,当数据中包含特殊字符(如单引号、双引号、换行符)时未正确转义,导致 JSON 格式无效。
// 错误的客户端代码(字符串拼接)
function createUser(name, email) {
// 当 name 包含单引号时会产生无效 JSON
const json = '{"name": "' + name + '", "email": "' + email + '"}';
return json;
}
// 调用:createUser("John's", "john@example.com")
// 结果:{"name": "John's", "email": "john@example.com"} // 无效 JSON
// 正确的客户端代码(使用 JSON.stringify)
function createUser(name, email) {
const data = { name, email };
const json = JSON.stringify(data); // 自动处理转义
return json;
}
// 调用:createUser("John's", "john@example.com")
// 结果:{"name":"John's","email":"john@example.com"} // 有效 JSON
// 后端添加更详细的错误响应
app.post('/api/users', (req, res) => {
try {
const userData = req.body; // Express 自动解析 JSON
// 验证必填字段
if (!userData.name || !userData.email) {
return res.status(400).json({
error: 'Invalid request',
details: 'Missing required fields: name, email',
received: Object.keys(userData)
});
}
// 处理业务逻辑
const user = createUser(userData);
res.json(user);
} catch (error) {
if (error instanceof SyntaxError) {
return res.status(400).json({
error: 'Invalid JSON',
details: error.message,
example: '{"name": "John", "email": "john@example.com"}'
});
}
throw error;
}
});