第2章 - 后端开发中的网络应用
嗨,后端开发的朋友们!
作为后端开发者,你需要设计和实现高性能、高可用的网络服务。从 RESTful API 到 WebSocket 服务器,从数据库连接到微服务通信,网络知识贯穿整个后端开发流程。
这一章,我会带你深入后端网络编程的实战应用,让你的服务更快、更稳定!
🌐 RESTful API 设计与实现
Express.js API 服务器
基础服务器搭建
// app.js
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const app = express();
// 安全中间件
app.use(helmet());
// CORS 配置
app.use(cors({
origin: ['https://example.com', 'https://app.example.com'],
credentials: true,
maxAge: 86400 // 24小时
}));
// 解析 JSON
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));
// 请求日志
app.use((req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
next();
});
// 限流配置
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100, // 最多100个请求
message: '请求过于频繁,请稍后再试'
});
app.use('/api/', limiter);
// 健康检查
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: Date.now() });
});
// 启动服务器
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器运行在端口 ${PORT}`);
});
module.exports = app;
RESTful 路由设计
// routes/users.js
const express = require('express');
const router = express.Router();
const { body, param, validationResult } = require('express-validator');
// 模拟数据库
const users = [
{ id: 1, name: '张三', email: 'zhangsan@example.com' },
{ id: 2, name: '李四', email: 'lisi@example.com' }
];
// GET /users - 获取用户列表
router.get('/', (req, res) => {
const { page = 1, limit = 10, search } = req.query;
let result = users;
// 搜索过滤
if (search) {
result = result.filter(u =>
u.name.includes(search) || u.email.includes(search)
);
}
// 分页
const start = (page - 1) * limit;
const end = start + parseInt(limit);
const paginatedUsers = result.slice(start, end);
res.json({
success: true,
data: paginatedUsers,
pagination: {
page: parseInt(page),
limit: parseInt(limit),
total: result.length
}
});
});
// GET /users/:id - 获取单个用户
router.get('/:id',
param('id').isInt().withMessage('ID必须是整数'),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ success: false, errors: errors.array() });
}
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) {
return res.status(404).json({ success: false, message: '用户不存在' });
}
res.json({ success: true, data: user });
}
);
// POST /users - 创建用户
router.post('/',
body('name').notEmpty().withMessage('姓名不能为空'),
body('email').isEmail().withMessage('邮箱格式不正确'),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ success: false, errors: errors.array() });
}
const newUser = {
id: users.length + 1,
name: req.body.name,
email: req.body.email
};
users.push(newUser);
res.status(201).json({ success: true, data: newUser });
}
);
// PUT /users/:id - 更新用户
router.put('/:id',
param('id').isInt(),
body('name').optional().notEmpty(),
body('email').optional().isEmail(),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ success: false, errors: errors.array() });
}
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) {
return res.status(404).json({ success: false, message: '用户不存在' });
}
if (req.body.name) user.name = req.body.name;
if (req.body.email) user.email = req.body.email;
res.json({ success: true, data: user });
}
);
// DELETE /users/:id - 删除用户
router.delete('/:id',
param('id').isInt(),
(req, res) => {
const index = users.findIndex(u => u.id === parseInt(req.params.id));
if (index === -1) {
return res.status(404).json({ success: false, message: '用户不存在' });
}
users.splice(index, 1);
res.status(204).send();
}
);
module.exports = router;
统一错误处理
// middleware/errorHandler.js
class ApiError extends Error {
constructor(statusCode, message) {
super(message);
this.statusCode = statusCode;
}
}
const errorHandler = (err, req, res, next) => {
console.error('错误:', err);
// 已知错误
if (err instanceof ApiError) {
return res.status(err.statusCode).json({
success: false,
message: err.message
});
}
// 数据库错误
if (err.name === 'MongoError') {
return res.status(500).json({
success: false,
message: '数据库操作失败'
});
}
// JWT 错误
if (err.name === 'JsonWebTokenError') {
return res.status(401).json({
success: false,
message: 'Token 无效'
});
}
// 未知错误
res.status(500).json({
success: false,
message: process.env.NODE_ENV === 'production'
? '服务器内部错误'
: err.message
});
};
module.exports = { ApiError, errorHandler };
🔌 数据库连接管理
MySQL 连接池
// db/mysql.js
const mysql = require('mysql2/promise');
// 创建连接池
const pool = mysql.createPool({
host: process.env.DB_HOST || 'localhost',
port: process.env.DB_PORT || 3306,
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || '',
database: process.env.DB_NAME || 'myapp',
waitForConnections: true,
connectionLimit: 10, // 最大连接数
queueLimit: 0, // 队列限制(0表示无限制)
enableKeepAlive: true,
keepAliveInitialDelay: 0
});
// 测试连接
pool.getConnection()
.then(connection => {
console.log('MySQL 数据库连接成功');
connection.release();
})
.catch(err => {
console.error('MySQL 连接失败:', err);
});
// 查询封装
const query = async (sql, params) => {
try {
const [rows] = await pool.execute(sql, params);
return rows;
} catch (error) {
console.error('查询失败:', error);
throw error;
}
};
// 事务封装
const transaction = async (callback) => {
const connection = await pool.getConnection();
try {
await connection.beginTransaction();
const result = await callback(connection);
await connection.commit();
return result;
} catch (error) {
await connection.rollback();
throw error;
} finally {
connection.release();
}
};
module.exports = { pool, query, transaction };
MongoDB 连接
// db/mongodb.js
const mongoose = require('mongoose');
const connectDB = async () => {
try {
await mongoose.connect(process.env.MONGODB_URI, {
maxPoolSize: 10, // 连接池大小
minPoolSize: 5,
socketTimeoutMS: 45000,
serverSelectionTimeoutMS: 5000,
family: 4 // 使用 IPv4
});
console.log('MongoDB 连接成功');
} catch (error) {
console.error('MongoDB 连接失败:', error);
process.exit(1);
}
};
// 监听连接事件
mongoose.connection.on('error', err => {
console.error('MongoDB 错误:', err);
});
mongoose.connection.on('disconnected', () => {
console.log('MongoDB 断开连接');
});
// 优雅关闭
process.on('SIGINT', async () => {
await mongoose.connection.close();
console.log('MongoDB 连接已关闭');
process.exit(0);
});
module.exports = connectDB;
Redis 缓存
// db/redis.js
const redis = require('redis');
// 创建 Redis 客户端
const client = redis.createClient({
host: process.env.REDIS_HOST || 'localhost',
port: process.env.REDIS_PORT || 6379,
password: process.env.REDIS_PASSWORD,
db: 0,
retry_strategy: (options) => {
if (options.error && options.error.code === 'ECONNREFUSED') {
return new Error('Redis 服务器拒绝连接');
}
if (options.total_retry_time > 1000 * 60 * 60) {
return new Error('Redis 重连超时');
}
if (options.attempt > 10) {
return undefined; // 停止重试
}
return Math.min(options.attempt * 100, 3000); // 重试间隔
}
});
client.on('connect', () => {
console.log('Redis 连接成功');
});
client.on('error', (err) => {
console.error('Redis 错误:', err);
});
// 缓存封装
const cache = {
// 获取缓存
async get(key) {
return new Promise((resolve, reject) => {
client.get(key, (err, data) => {
if (err) reject(err);
resolve(data ? JSON.parse(data) : null);
});
});
},
// 设置缓存
async set(key, value, ttl = 3600) {
return new Promise((resolve, reject) => {
const data = JSON.stringify(value);
client.setex(key, ttl, data, (err, reply) => {
if (err) reject(err);
resolve(reply);
});
});
},
// 删除缓存
async del(key) {
return new Promise((resolve, reject) => {
client.del(key, (err, reply) => {
if (err) reject(err);
resolve(reply);
});
});
},
// 批量删除(使用模式匹配)
async delPattern(pattern) {
return new Promise((resolve, reject) => {
client.keys(pattern, (err, keys) => {
if (err) reject(err);
if (keys.length > 0) {
client.del(keys, (err, reply) => {
if (err) reject(err);
resolve(reply);
});
} else {
resolve(0);
}
});
});
}
};
// 缓存中间件
const cacheMiddleware = (ttl = 3600) => {
return async (req, res, next) => {
const key = `cache:${req.originalUrl}`;
try {
const cached = await cache.get(key);
if (cached) {
console.log('使用缓存:', key);
return res.json(cached);
}
// 重写 res.json 以自动缓存
const originalJson = res.json.bind(res);
res.json = (data) => {
cache.set(key, data, ttl).catch(err => console.error('缓存失败:', err));
return originalJson(data);
};
next();
} catch (error) {
console.error('缓存中间件错误:', error);
next();
}
};
};
module.exports = { client, cache, cacheMiddleware };
🚀 WebSocket 服务器
Socket.IO 实现
// server.js
const express = require('express');
const http = require('http');
const socketIO = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = socketIO(server, {
cors: {
origin: "*",
methods: ["GET", "POST"]
},
pingTimeout: 30000,
pingInterval: 25000
});
// 在线用户
const onlineUsers = new Map();
// 房间管理
const rooms = new Map();
// 连接处理
io.on('connection', (socket) => {
console.log('用户连接:', socket.id);
// 用户登录
socket.on('login', (data) => {
const { userId, username } = data;
onlineUsers.set(userId, {
socketId: socket.id,
username,
loginTime: Date.now()
});
socket.userId = userId;
socket.username = username;
// 广播用户上线
io.emit('user-online', {
userId,
username,
onlineCount: onlineUsers.size
});
console.log(`${username} 上线,当前在线 ${onlineUsers.size} 人`);
});
// 加入房间
socket.on('join-room', (roomId) => {
socket.join(roomId);
if (!rooms.has(roomId)) {
rooms.set(roomId, new Set());
}
rooms.get(roomId).add(socket.userId);
// 通知房间内其他人
socket.to(roomId).emit('user-joined', {
userId: socket.userId,
username: socket.username
});
console.log(`${socket.username} 加入房间 ${roomId}`);
});
// 离开房间
socket.on('leave-room', (roomId) => {
socket.leave(roomId);
if (rooms.has(roomId)) {
rooms.get(roomId).delete(socket.userId);
if (rooms.get(roomId).size === 0) {
rooms.delete(roomId);
}
}
// 通知房间内其他人
socket.to(roomId).emit('user-left', {
userId: socket.userId,
username: socket.username
});
});
// 发送消息
socket.on('send-message', (data) => {
const { roomId, message } = data;
const messageData = {
id: Date.now(),
userId: socket.userId,
username: socket.username,
message,
timestamp: Date.now()
};
// 发送到房间
io.to(roomId).emit('new-message', messageData);
console.log(`消息 [${roomId}] ${socket.username}: ${message}`);
});
// 私聊
socket.on('private-message', (data) => {
const { toUserId, message } = data;
const toUser = onlineUsers.get(toUserId);
if (toUser) {
io.to(toUser.socketId).emit('private-message', {
fromUserId: socket.userId,
fromUsername: socket.username,
message,
timestamp: Date.now()
});
}
});
// 断开连接
socket.on('disconnect', () => {
if (socket.userId) {
onlineUsers.delete(socket.userId);
// 从所有房间移除
rooms.forEach((users, roomId) => {
if (users.has(socket.userId)) {
users.delete(socket.userId);
socket.to(roomId).emit('user-left', {
userId: socket.userId,
username: socket.username
});
}
});
// 广播用户下线
io.emit('user-offline', {
userId: socket.userId,
username: socket.username,
onlineCount: onlineUsers.size
});
console.log(`${socket.username} 下线,当前在线 ${onlineUsers.size} 人`);
}
});
});
// 启动服务器
const PORT = 3000;
server.listen(PORT, () => {
console.log(`WebSocket 服务器运行在端口 ${PORT}`);
});
🔐 认证与授权
JWT Token 认证
// middleware/auth.js
const jwt = require('jsonwebtoken');
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
const JWT_EXPIRES_IN = '7d';
// 生成 Token
const generateToken = (userId, username) => {
return jwt.sign(
{ userId, username },
JWT_SECRET,
{ expiresIn: JWT_EXPIRES_IN }
);
};
// 验证 Token
const verifyToken = (token) => {
try {
return jwt.verify(token, JWT_SECRET);
} catch (error) {
return null;
}
};
// 认证中间件
const authMiddleware = (req, res, next) => {
// 从 Header 获取 Token
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({
success: false,
message: '未提供认证令牌'
});
}
const token = authHeader.substring(7);
const decoded = verifyToken(token);
if (!decoded) {
return res.status(401).json({
success: false,
message: '认证令牌无效或已过期'
});
}
// 将用户信息挂载到 req
req.user = decoded;
next();
};
// 可选认证(有Token就验证,没有也放行)
const optionalAuth = (req, res, next) => {
const authHeader = req.headers.authorization;
if (authHeader && authHeader.startsWith('Bearer ')) {
const token = authHeader.substring(7);
const decoded = verifyToken(token);
if (decoded) {
req.user = decoded;
}
}
next();
};
module.exports = {
generateToken,
verifyToken,
authMiddleware,
optionalAuth
};
登录接口
// routes/auth.js
const express = require('express');
const router = express.Router();
const bcrypt = require('bcrypt');
const { generateToken } = require('../middleware/auth');
// POST /auth/register - 注册
router.post('/register', async (req, res) => {
try {
const { username, email, password } = req.body;
// 验证输入
if (!username || !email || !password) {
return res.status(400).json({
success: false,
message: '请填写完整信息'
});
}
// 检查用户是否存在
// const existingUser = await User.findOne({ email });
// if (existingUser) { ... }
// 加密密码
const hashedPassword = await bcrypt.hash(password, 10);
// 创建用户
// const user = await User.create({ username, email, password: hashedPassword });
// 生成 Token
const token = generateToken('user123', username);
res.status(201).json({
success: true,
data: {
userId: 'user123',
username,
email,
token
}
});
} catch (error) {
res.status(500).json({
success: false,
message: '注册失败'
});
}
});
// POST /auth/login - 登录
router.post('/login', async (req, res) => {
try {
const { email, password } = req.body;
// 查找用户
// const user = await User.findOne({ email });
// if (!user) { ... }
// 验证密码
// const isValidPassword = await bcrypt.compare(password, user.password);
// if (!isValidPassword) { ... }
// 模拟验证成功
const token = generateToken('user123', '张三');
res.json({
success: true,
data: {
userId: 'user123',
username: '张三',
email,
token
}
});
} catch (error) {
res.status(500).json({
success: false,
message: '登录失败'
});
}
});
module.exports = router;
⚡ 性能优化
接口限流
// middleware/rateLimit.js
const redis = require('redis');
const client = redis.createClient();
// 滑动窗口限流
const rateLimitMiddleware = (options = {}) => {
const {
windowMs = 60000, // 时间窗口(1分钟)
max = 100, // 最大请求数
keyGenerator = (req) => req.ip, // 生成限流key的函数
handler = (req, res) => { // 超限处理
res.status(429).json({
success: false,
message: '请求过于频繁,请稍后再试'
});
}
} = options;
return async (req, res, next) => {
const key = `ratelimit:${keyGenerator(req)}`;
const now = Date.now();
const windowStart = now - windowMs;
try {
// 使用 Redis sorted set 实现滑动窗口
await new Promise((resolve, reject) => {
// 删除窗口外的记录
client.zremrangebyscore(key, 0, windowStart, (err) => {
if (err) return reject(err);
// 添加当前请求
client.zadd(key, now, `${now}-${Math.random()}`, (err) => {
if (err) return reject(err);
// 获取当前窗口内的请求数
client.zcard(key, (err, count) => {
if (err) return reject(err);
// 设置过期时间
client.expire(key, Math.ceil(windowMs / 1000));
if (count > max) {
return handler(req, res);
}
resolve();
});
});
});
});
next();
} catch (error) {
console.error('限流中间件错误:', error);
next();
}
};
};
module.exports = rateLimitMiddleware;
请求压缩
const compression = require('compression');
// 压缩中间件
app.use(compression({
filter: (req, res) => {
// 只压缩大于 1KB 的响应
if (req.headers['x-no-compression']) {
return false;
}
return compression.filter(req, res);
},
level: 6, // 压缩级别 (0-9)
threshold: 1024 // 大于 1KB 才压缩
}));
HTTP/2 支持
const http2 = require('http2');
const fs = require('fs');
const server = http2.createSecureServer({
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.crt')
}, app);
server.listen(443, () => {
console.log('HTTP/2 服务器运行在端口 443');
});
📊 监控与日志
请求日志
const morgan = require('morgan');
const fs = require('fs');
const path = require('path');
// 创建日志目录
const logDirectory = path.join(__dirname, 'logs');
if (!fs.existsSync(logDirectory)) {
fs.mkdirSync(logDirectory);
}
// 创建写入流
const accessLogStream = fs.createWriteStream(
path.join(logDirectory, 'access.log'),
{ flags: 'a' }
);
// 自定义日志格式
morgan.token('body', (req) => JSON.stringify(req.body));
const logFormat = ':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent" - :response-time ms - :body';
// 使用日志中间件
app.use(morgan(logFormat, { stream: accessLogStream }));
app.use(morgan('dev')); // 控制台输出
性能监控
// middleware/monitor.js
const responseTime = require('response-time');
// 监控响应时间
app.use(responseTime((req, res, time) => {
const stat = `${req.method} ${req.url}: ${time.toFixed(2)}ms`;
console.log(stat);
// 记录慢查询
if (time > 1000) {
console.warn(`[慢查询] ${stat}`);
}
}));
// 健康检查端点
app.get('/metrics', (req, res) => {
res.json({
uptime: process.uptime(),
memory: process.memoryUsage(),
timestamp: Date.now()
});
});
💡 最佳实践
1. 连接池配置
// 数据库连接池配置建议
const poolConfig = {
min: 2, // 最小连接数
max: 10, // 最大连接数(根据服务器配置)
idle: 30000, // 空闲超时(30秒)
acquire: 60000, // 获取连接超时(60秒)
evict: 1000 // 检查空闲连接间隔
};
2. 优雅关闭
// 优雅关闭服务器
const gracefulShutdown = () => {
console.log('开始优雅关闭...');
server.close(() => {
console.log('HTTP 服务器已关闭');
// 关闭数据库连接
mongoose.connection.close(false, () => {
console.log('数据库连接已关闭');
process.exit(0);
});
});
// 超时强制关闭
setTimeout(() => {
console.error('超时,强制关闭');
process.exit(1);
}, 30000);
};
process.on('SIGTERM', gracefulShutdown);
process.on('SIGINT', gracefulShutdown);
3. 环境配置
// config/index.js
require('dotenv').config();
module.exports = {
env: process.env.NODE_ENV || 'development',
port: parseInt(process.env.PORT, 10) || 3000,
db: {
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT, 10) || 3306,
name: process.env.DB_NAME || 'myapp',
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || ''
},
redis: {
host: process.env.REDIS_HOST || 'localhost',
port: parseInt(process.env.REDIS_PORT, 10) || 6379,
password: process.env.REDIS_PASSWORD
},
jwt: {
secret: process.env.JWT_SECRET || 'your-secret-key',
expiresIn: process.env.JWT_EXPIRES_IN || '7d'
}
};
📝 小结
这一章我们学习了:
✅ RESTful API 设计:路由设计、参数验证、错误处理
✅ 数据库连接:MySQL 连接池、MongoDB 连接、Redis 缓存
✅ WebSocket 服务器:实时通信、房间管理、在线状态
✅ 认证授权:JWT Token、登录注册、权限验证
✅ 性能优化:接口限流、请求压缩、HTTP/2
✅ 监控日志:请求日志、性能监控、健康检查
🎯 下一步
接下来学习运维工程师如何配置和管理网络服务!
