Audiobookshelf多服务器负载均衡:提升系统可用性
Audiobookshelf多服务器负载均衡:提升系统可用性
【免费下载链接】audiobookshelf Self-hosted audiobook and podcast server 项目地址: https://gitcode.com/gh_mirrors/au/audiobookshelf
随着数字内容的爆炸式增长,自托管音频书和播客服务器(Self-hosted audiobook and podcast server)面临着用户访问量激增带来的性能挑战。单服务器架构在高并发场景下容易出现瓶颈,而多服务器负载均衡方案能够有效提升系统可用性、扩展性和容错能力。本文将详细介绍如何为Audiobookshelf实现多服务器负载均衡部署,从架构设计到具体实施步骤,帮助管理员构建高可用的音频内容服务平台。
负载均衡架构设计
Audiobookshelf作为自托管音频服务,其负载均衡架构需考虑媒体文件存储、数据库同步和会话管理三大核心问题。推荐采用"共享存储+主从复制+会话共享"的三层架构:
该架构通过负载均衡器分发请求,共享存储确保媒体文件一致性,数据库主从复制实现数据同步,Redis集群管理用户会话,从而实现系统的水平扩展。
基础设施准备
服务器节点配置
Audiobookshelf服务器节点推荐配置如下:
- CPU:4核8线程及以上
- 内存:8GB RAM(音频转码需求较高时建议16GB)
- 存储:系统盘100GB SSD,数据盘通过NFS挂载共享存储
- 网络:千兆网卡,建议配置双网卡绑定提高可用性
共享存储配置
媒体文件存储推荐使用NFS或SMB协议实现共享访问,配置示例(NFS服务端):
# 安装NFS服务
sudo apt update && sudo apt install nfs-kernel-server -y
# 创建共享目录
sudo mkdir -p /data/audiobookshelf/media
sudo chown -R nobody:nogroup /data/audiobookshelf/media
sudo chmod -R 777 /data/audiobookshelf/media
# 配置共享权限(/etc/exports)
echo "/data/audiobookshelf/media 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)" | sudo tee -a /etc/exports
# 应用配置
sudo exportfs -a
sudo systemctl restart nfs-kernel-server
客户端挂载命令:
sudo mount -t nfs 192.168.1.5:/data/audiobookshelf/media /opt/audiobookshelf/media
数据库集群配置
Audiobookshelf默认使用SQLite数据库,在多服务器环境下需迁移至PostgreSQL并配置主从复制。数据库迁移脚本可参考server/Database.js中的初始化逻辑,主要步骤包括:
- 导出SQLite数据:
sqlite3 audiobookshelf.db .dump > backup.sql - 创建PostgreSQL数据库:
createdb audiobookshelf - 导入数据:
psql -d audiobookshelf -f backup.sql - 配置主从复制,修改postgresql.conf:
wal_level = replica max_wal_senders = 3 wal_keep_size = 1GB
应用层配置
服务器节点部署
每个Audiobookshelf节点通过Docker部署,确保环境一致性:
# docker-compose.yml
version: '3.8'
services:
audiobookshelf:
image: ghcr.io/advplyr/audiobookshelf:latest
container_name: audiobookshelf
restart: always
ports:
- "80:80"
environment:
- TZ=Asia/Shanghai
- DB_HOST=192.168.1.6
- DB_PORT=5432
- DB_USER=audiobookshelf
- DB_PASS=secure_password
- DB_NAME=audiobookshelf
- REDIS_HOST=192.168.1.7
- REDIS_PORT=6379
volumes:
- /opt/audiobookshelf/config:/config
- /opt/audiobookshelf/media:/media # NFS挂载点
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
会话共享配置
修改服务器配置文件server/Server.js,将会话存储从内存改为Redis:
// 修改express-session配置
app.use(
expressSession({
secret: this.auth.tokenManager.TokenSecret,
resave: false,
saveUninitialized: false,
cookie: {
secure: true, // 生产环境启用HTTPS时设置为true
maxAge: 24 * 60 * 60 * 1000 // 24小时有效期
},
store: new RedisStore({
host: process.env.REDIS_HOST,
port: process.env.REDIS_PORT,
prefix: 'abs:sess:'
})
})
)
媒体文件处理优化
为避免多节点同时转码冲突,修改server/managers/CoverManager.js实现分布式锁机制:
async getCoverPath(itemId, coverType, size) {
const lockKey = `cover:${itemId}:${coverType}:${size}`;
// 获取Redis锁
const lockAcquired = await redisClient.set(lockKey, '1', 'NX', 'PX', 30000);
if (!lockAcquired) {
// 等待锁释放或超时
await new Promise(resolve => setTimeout(resolve, 1000));
return this.getCoverPath(itemId, coverType, size);
}
try {
// 原有封面处理逻辑
// ...
} finally {
// 释放锁
await redisClient.del(lockKey);
}
}
负载均衡器配置
Nginx配置示例
# /etc/nginx/sites-available/audiobookshelf.conf
upstream audiobookshelf_nodes {
server 192.168.1.10:80 weight=1 max_fails=3 fail_timeout=30s;
server 192.168.1.11:80 weight=1 max_fails=3 fail_timeout=30s;
server 192.168.1.12:80 weight=1 max_fails=3 fail_timeout=30s;
# 健康检查
keepalive 32;
}
server {
listen 80;
server_name audio.example.com;
# 重定向到HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name audio.example.com;
ssl_certificate /etc/letsencrypt/live/audio.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/audio.example.com/privkey.pem;
# SSL配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
# 负载均衡配置
location / {
proxy_pass http://audiobookshelf_nodes;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Cluster-Client-IP $remote_addr; # 保留客户端真实IP
proxy_connect_timeout 30s;
proxy_read_timeout 90s;
proxy_send_timeout 30s;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
# 静态资源缓存
location ~* .(jpg|jpeg|png|gif|ico|css|js)$ {
proxy_pass http://audiobookshelf_nodes;
proxy_cache_valid 200 30d;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
add_header Cache-Control "public, max-age=2592000";
}
# WebSocket支持(用于实时通知)
location /socket.io {
proxy_pass http://audiobookshelf_nodes;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
健康检查配置
Nginx主动健康检查配置:
http {
# ...其他配置...
match status_check {
status 200;
header Content-Type = text/plain;
body ~ "OK";
}
upstream audiobookshelf_nodes {
server 192.168.1.10:80;
server 192.168.1.11:80;
server 192.168.1.12:80;
health_check match=status_check interval=5s fails=2 passes=1;
}
}
同时在Audiobookshelf中添加健康检查接口server/routers/PublicRouter.js:
router.get('/health', async (req, res) => {
try {
// 检查数据库连接
await Database.sequelize.authenticate();
// 检查Redis连接
await redisClient.ping();
// 检查共享存储
const testFile = path.join(global.MetadataPath, 'healthcheck.txt');
fs.writeFileSync(testFile, Date.now().toString());
fs.unlinkSync(testFile);
res.status(200).send('OK');
} catch (error) {
Logger.error('Health check failed:', error);
res.status(503).send('ERROR');
}
});
监控与维护
系统监控
部署Prometheus和Grafana监控整个系统,Audiobookshelf可通过server/controllers/StatsController.js暴露监控指标:
// 添加Prometheus指标暴露端点
router.get('/metrics', async (req, res) => {
const stats = await this.getSystemStats();
const metrics = [
`audiobookshelf_active_users ${stats.activeUsers}`,
`audiobookshelf_total_books ${stats.totalBooks}`,
`audiobookshelf_total_podcasts ${stats.totalPodcasts}`,
`audiobookshelf_streaming_sessions ${stats.streamingSessions}`,
`audiobookshelf_disk_usage{path="/media"} ${stats.diskUsage.media.used}`,
`audiobookshelf_disk_usage{path="/config"} ${stats.diskUsage.config.used}`,
`audiobookshelf_memory_usage ${stats.memoryUsage}`
].join('
');
res.set('Content-Type', 'text/plain');
res.send(metrics);
});
自动扩展
结合Kubernetes实现自动扩缩容,配置示例:
# audiobookshelf-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: audiobookshelf
spec:
replicas: 3
selector:
matchLabels:
app: audiobookshelf
template:
metadata:
labels:
app: audiobookshelf
spec:
containers:
- name: audiobookshelf
image: ghcr.io/advplyr/audiobookshelf:latest
resources:
requests:
cpu: 1000m
memory: 2048Mi
limits:
cpu: 2000m
memory: 4096Mi
ports:
- containerPort: 80
env:
- name: DB_HOST
valueFrom:
secretKeyRef:
name: db-credentials
key: host
# ...其他环境变量...
volumeMounts:
- name: media-storage
mountPath: /media
volumes:
- name: media-storage
persistentVolumeClaim:
claimName: nfs-media-pvc
---
# HPA配置
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: audiobookshelf
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: audiobookshelf
minReplicas: 2
maxReplicas: 6
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Percent
value: 50
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300
备份策略
配置定期备份数据库和配置文件,可通过server/managers/BackupManager.js实现自动化备份:
// 修改备份管理器,支持远程备份存储
async createBackup() {
const backupDir = path.join(global.ConfigPath, 'backups');
fs.ensureDirSync(backupDir);
const timestamp = new Date().toISOString().replace(/:/g, '-');
const backupFile = path.join(backupDir, `backup-${timestamp}.zip`);
// 备份数据库
await this.backupDatabase(backupFile);
// 备份配置文件
await this.backupConfig(backupFile);
// 上传到远程存储(S3或其他服务)
if (global.ServerSettings.remoteBackup.enable) {
await this.uploadToRemoteStorage(backupFile);
}
// 清理旧备份
await this.cleanupOldBackups(backupDir);
return backupFile;
}
性能优化最佳实践
数据库优化
- 为常用查询添加索引,修改server/migrations中的迁移脚本:
// 添加书籍查询索引
await queryInterface.addIndex('books', ['title'], {
name: 'books_title_idx',
using: 'BTREE'
});
// 添加用户进度索引
await queryInterface.addIndex('media_progress', ['userId', 'updatedAt'], {
name: 'media_progress_user_updated_idx'
});
- 配置数据库连接池,修改server/Database.js:
// 修改Sequelize配置
this.sequelize = new Sequelize(database, user, password, {
host: host,
port: port,
dialect: 'postgres',
pool: {
max: 20, // 最大连接数
min: 5, // 最小连接数
acquire: 30000,
idle: 10000
},
query: {
raw: true
},
logging: (msg) => Logger.debug(`[DB] ${msg}`)
});
媒体流优化
- 启用HLS流媒体,配置server/routers/HlsRouter.js支持自适应码率:
// 配置HLS转码参数
router.get('/:itemId/stream.m3u8', async (req, res) => {
const { itemId } = req.params;
const { quality = 'medium' } = req.query;
// 根据质量参数设置不同的转码配置
const qualityProfiles = {
low: { bitrate: '64k', format: 'mp3' },
medium: { bitrate: '128k', format: 'mp3' },
high: { bitrate: '256k', format: 'aac' },
lossless: { bitrate: '1024k', format: 'flac' }
};
const profile = qualityProfiles[quality] || qualityProfiles.medium;
try {
const stream = await this.mediaService.createHlsStream(itemId, profile);
res.setHeader('Content-Type', 'application/x-mpegURL');
stream.pipe(res);
} catch (error) {
Logger.error('HLS stream error:', error);
res.status(500).send('Stream creation failed');
}
});
- 使用CDN加速静态资源和媒体流分发,修改Nginx配置添加CDN支持:
# 添加CDN配置
location ~* ^/(covers|images|static)/ {
proxy_pass http://audiobookshelf_nodes;
proxy_cache_valid 200 30d;
proxy_cache_use_stale error timeout invalid_header updating;
add_header Cache-Control "public, max-age=2592000";
add_header X-Proxy-Cache $upstream_cache_status;
# CDN友好的缓存控制
add_header Vary "Accept-Encoding";
expires 30d;
}
安全加固
- 启用HTTPS,配置自动续期Let's Encrypt证书
- 实施API限流,修改server/utils/rateLimiterFactory.js:
// 创建API限流中间件
createApiLimiter() {
return rateLimit({
store: new RedisStore({
client: redisClient,
prefix: 'rate_limit:'
}),
windowMs: 15 * 60 * 1000, // 15分钟
max: 100, // 每个IP限制100请求/窗口
standardHeaders: true,
legacyHeaders: false,
message: {
error: '请求过于频繁,请稍后再试'
},
skip: (req) => {
// 白名单IP
return global.ServerSettings.rateLimit.whitelist.includes(req.ip);
}
});
}
- 定期更新依赖包,修复安全漏洞,可通过package.json和client/package.json检查依赖版本。
总结
通过实施多服务器负载均衡方案,Audiobookshelf能够显著提升系统可用性和并发处理能力。本文详细介绍了从架构设计、基础设施准备、应用配置、负载均衡器部署到监控维护的完整流程。关键要点包括:
- 采用共享存储确保媒体文件一致性
- 使用数据库主从复制实现数据高可用
- 通过Redis集群管理用户会话
- 配置Nginx负载均衡和健康检查
- 实施全面的监控和自动扩展策略
随着用户规模增长,可进一步优化架构,如引入CDN加速内容分发、实施地理分布式部署等。通过持续优化和监控,Audiobookshelf能够为用户提供稳定、高效的音频内容服务体验。
官方文档:docs/ 社区教程:readme.md API参考:docs/openapi.json 部署配置:docker-compose.yml
【免费下载链接】audiobookshelf Self-hosted audiobook and podcast server 项目地址: https://gitcode.com/gh_mirrors/au/audiobookshelf







