1. 项目概述
这个基于SpringBoot的个人博客系统(项目编号11677)是我在2022年实际开发并持续维护的一个全栈项目。作为一个技术博主,我最初开发它的目的是为了替代第三方博客平台,获得完全自主的内容管理能力。经过两年迭代,系统目前日均处理500+UV,支持Markdown写作、多级分类、标签云、评论互动等完整功能模块。
2. 技术架构解析
2.1 核心框架选型
选择SpringBoot 2.7作为基础框架主要考虑:
- 内嵌Tomcat简化部署(实测单机可承载2000+并发)
- 自动配置特性减少XML配置(相比传统SSM节省60%配置代码)
- 完善的Starter生态(集成MyBatis、Redis等组件只需添加依赖)
数据库采用MySQL 8.0,配合MyBatis-Plus 3.5实现ORM操作。这里特别说明MyBatis-Plus的选择理由:
- Lambda表达式写法更安全(避免SQL注入风险)
- 内置分页插件(PageHelper在复杂查询时有性能问题)
- 代码生成器可快速产出基础CRUD代码
2.2 关键组件设计
2.2.1 文章模块
采用三层架构:
- Controller层处理HTTP请求(@RestController)
- Service层实现业务逻辑(@Transactional管理事务)
- Mapper层操作数据库(继承BaseMapper)
核心代码片段:
java复制// 文章发布接口
@PostMapping("/article")
public Result publishArticle(@Valid ArticleDTO dto) {
Article article = convertTool.dtoToEntity(dto);
return articleService.save(article) ?
Result.success() : Result.fail("发布失败");
}
2.2.2 评论系统
使用Redis实现高频读写:
- 评论数据采用Hash结构存储(key: article_id, field: comment_id)
- 每日凌晨通过定时任务持久化到MySQL
- 布隆过滤器防止恶意刷评(误判率设为0.01%)
3. 核心功能实现
3.1 Markdown编辑器集成
选用Editor.md作为前端编辑器,后端处理流程:
- 前端提交原始Markdown文本
- 服务端使用commonmark-java解析
- 生成HTML缓存到Redis(过期时间7天)
- 敏感词过滤(AC自动机算法实现)
关键配置示例:
yaml复制# 敏感词检测配置
filter:
dict-path: /data/keywords.txt
replace-char: '*'
3.2 文件上传方案
采用本地存储+CDN加速方案:
- 用户上传文件到Nginx静态目录
- 通过Shell脚本同步到阿里云OSS
- 前端通过CDN域名访问资源
- 每天凌晨清理临时文件(find命令实现)
上传接口注意事项:
- 限制文件类型(白名单机制)
- 重命名文件(MD5+时间戳)
- 病毒扫描(调用ClamAV接口)
4. 性能优化实践
4.1 缓存策略
三级缓存结构设计:
- 热点文章:Guava Cache(最大1000条,5分钟过期)
- 分类目录:Redis(1小时过期)
- 全站数据:Ehcache(集群同步)
缓存击穿解决方案:
java复制public Article getArticle(Long id) {
// 双重检查锁
Article article = cache.get(id);
if (article == null) {
synchronized (this) {
article = cache.get(id);
if (article == null) {
article = dbQuery(id);
cache.put(id, article);
}
}
}
return article;
}
4.2 SQL优化案例
典型慢查询优化前后对比:
| 场景 | 优化前(ms) | 优化后(ms) | 措施 |
|---|---|---|---|
| 文章列表 | 1200 | 150 | 添加复合索引 |
| 标签统计 | 800 | 50 | 使用物化视图 |
| 搜索查询 | 2000 | 300 | 引入Elasticsearch |
5. 安全防护方案
5.1 常见攻击防护
- XSS:Jsoup清理HTML标签
- CSRF:Spring Security默认防护
- SQL注入:MyBatis预编译+正则过滤
- 暴力破解:登录失败次数限制(Redis计数)
5.2 数据备份策略
采用全量+增量备份:
- 每日3:00全量备份(mysqldump)
- 每小时binlog增量备份
- 备份文件加密后上传至OSS
- 每月进行恢复演练
备份脚本片段:
bash复制#!/bin/bash
DATE=$(date +%Y%m%d)
mysqldump -uroot -p$PWD blog > /backup/full_$DATE.sql
openssl enc -aes-256-cbc -in /backup/full_$DATE.sql -out /backup/encrypted_$DATE.dat
6. 部署与监控
6.1 容器化部署
Docker Compose编排方案:
yaml复制version: '3'
services:
app:
image: blog:1.0
ports:
- "8080:8080"
depends_on:
- redis
- mysql
redis:
image: redis:6
volumes:
- redis_data:/data
mysql:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: $DB_PWD
volumes:
- mysql_data:/var/lib/mysql
6.2 监控体系
Prometheus监控指标:
- JVM内存使用(grafana展示)
- 接口响应时间(P99<500ms)
- MySQL连接数(预警阈值100)
- 磁盘空间(每日增长量监控)
告警规则示例:
yaml复制- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.1
for: 10m
labels:
severity: critical
7. 踩坑经验总结
-
MyBatis一级缓存问题:
- 在Service方法内多次查询相同数据时,可能返回缓存旧数据
- 解决方案:添加@Transactional(readOnly=true)或手动清空缓存
-
Redis序列化异常:
- 默认JDK序列化导致ClassNotFoundException
- 改用Jackson2JsonRedisSerializer并注册类型信息
-
跨域问题处理:
- 前端开发时需配置代理
- 生产环境使用Nginx反向代理
- 避免使用通配符CORS配置
-
事务失效场景:
- 同类方法内调用(需通过AopContext获取代理对象)
- 异常类型未声明(默认只回滚RuntimeException)
这个项目让我深刻体会到,一个看似简单的博客系统,要保证高性能、高可用需要处理大量技术细节。建议开发时从最小MVP开始迭代,先跑通核心流程再逐步完善周边功能