
1. 项目概述当美食遇上Spring Boot去年帮朋友改造他的美食博客时我用了两周时间基于Spring Boot重写了整个系统。原本的PHP架构在用户量突破5万后开始频繁崩溃而迁移到Spring Boot后不仅QPS提升了3倍还实现了实时图片处理等新功能。这个美食分享系统本质上是一个内容社区平台核心解决三个问题如何让用户高效发布美食内容、如何构建内容互动生态、如何实现个性化推荐。典型的用户场景包括美食博主上传菜谱时自动生成步骤图、用户通过标签系统发现同城美食、后台基于用户行为数据优化推荐算法等。系统需要处理高并发的图片上传实测峰值每秒30张、复杂的社交关系链关注/收藏/点赞、以及地域化的内容分发。选择Spring Boot作为技术栈看中的正是其快速集成能力——通过starter依赖可以轻松整合Redis缓存、Elasticsearch搜索、WebSocket通知等组件避免重复造轮子。2. 系统架构设计2.1 技术栈选型对比在初期技术评审时我们对比了三种方案传统SSM架构配置复杂需要手动整合MyBatis、Spring MVC等组件PythonDjango开发速度快但性能瓶颈明显GC效率不如JVMSpring BootMyBatis-Plus自动配置代码生成兼顾开发效率和运行性能最终方案的核心组件包括graph TD A[Spring Boot 2.7] -- B[WebMVC] A -- C[Security OAuth2] A -- D[MyBatis-Plus] B -- E[Swagger/Knife4j] C -- F[JWT] D -- G[MySQL8] D -- H[Redis]2.2 分层架构设计采用经典的三层架构但针对美食业务做了特殊优化表现层增加图片预处理Filter在上传时自动压缩/添加水印业务层拆分为内容服务、社交服务、推荐服务三个子模块数据层MySQL主从分离Elasticsearch构建混合索引关键设计决策使用阿里云OSS存储图片通过CDN加速访问敏感操作如删除菜谱采用Spring事务注解保证原子性热门数据点赞数、浏览数用Redis原子计数器实现3. 核心功能实现3.1 菜谱发布流程用户发布菜谱的完整流程涉及多个技术要点PostMapping(/recipe) public Result publishRecipe(Valid RecipeDTO dto) { // 1. 图片处理 ListString imgUrls imageService.upload(dto.getImages()); // 2. 内容审核 if(contentCheckService.checkText(dto.getContent()) CheckStatus.REJECT) { throw new BusinessException(内容违规); } // 3. 保存数据库 Recipe recipe RecipeConverter.INSTANCE.toEntity(dto); recipe.setImages(imgUrls); recipeMapper.insert(recipe); // 4. 建立搜索索引 elasticService.indexRecipe(recipe); // 5. 触发消息通知 eventPublisher.publishEvent(new RecipeEvent(recipe)); return Result.success(recipe.getId()); }性能优化点图片上传采用线程池异步处理内容审核走RocketMQ异步队列Elasticsearch索引使用bulk API批量操作3.2 社交互动系统点赞/收藏/评论功能看似简单但高并发下容易出问题。我们的解决方案Transactional public void likeRecipe(Long userId, Long recipeId) { // 使用Redis原子操作防止重复点赞 String key like: recipeId; if(redisTemplate.opsForSet().isMember(key, userId)) { throw new BusinessException(已点赞); } // 双写一致性先写Redis再写MySQL redisTemplate.opsForSet().add(key, userId); likeMapper.insert(new Like(userId, recipeId)); // 更新计数最终一致性 redisTemplate.opsForValue().increment(count:like: recipeId); asyncTask.updateLikeCount(recipeId); }踩坑记录初期没有用Redis事务导致计数不准确后来改用Lua脚本保证原子性local key KEYS[1] local user ARGV[1] if redis.call(sismember, key, user) 1 then return 0 else redis.call(sadd, key, user) redis.call(incr, key..:count) return 1 end4. 推荐系统实现4.1 基于内容的推荐使用TF-IDF算法计算菜谱相似度public ListRecipe recommendByContent(Long recipeId) { Recipe current getById(recipeId); ListRecipe candidates listByTag(current.getTags()); return candidates.stream() .sorted(Comparator.comparingDouble(r - cosineSimilarity(current.getTfidf(), r.getTfidf()))) .limit(10) .collect(Collectors.toList()); }4.2 协同过滤改进原始UserCF算法在用户量少时效果差我们做了以下优化混合物品相似度Jaccard(标签) Cosine(文本)加入时间衰减因子最近3个月的行为权重更高冷启动处理新用户展示热门同城菜谱5. 运维监控方案5.1 健康检查配置Spring Actuator的定制化配置management: endpoints: web: exposure: include: health,metrics,prometheus endpoint: health: show-details: always probes: enabled: true5.2 日志收集架构采用ELK栈处理每日10GB的日志数据Filebeat收集各节点日志Logstash添加业务标签Elasticsearch建立多维度索引Kibana展示关键看板关键监控指标接口响应时间P99 500msMySQL连接池使用率 80%Redis缓存命中率 95%6. 安全防护措施6.1 防XSS攻击前端使用DOMPurify净化输入后端补充检查public String sanitize(String html) { return Jsoup.clean(html, Safelist.basic() .addTags(img) .addAttributes(img, src, alt) ); }6.2 权限控制方案基于Spring Security的RBAC模型PreAuthorize(hasRole(ADMIN) or permission.checkOwner(#recipeId)) DeleteMapping(/recipe/{id}) public Result deleteRecipe(PathVariable Long id) { recipeService.delete(id); return Result.success(); }权限校验逻辑封装为BeanComponent(permission) public class PermissionService { public boolean checkOwner(Long recipeId) { Long currentUserId SecurityUtils.getUserId(); return recipeMapper.selectById(recipeId).getUserId().equals(currentUserId); } }7. 性能优化实战7.1 缓存设计策略采用多级缓存架构本地Caffeine缓存热点数据TTL5分钟Redis集群缓存公共数据TTL1小时MySQL持久化存储缓存击穿解决方案public Recipe getRecipe(Long id) { String key recipe: id; Recipe recipe redisTemplate.opsForValue().get(key); if (recipe null) { synchronized (this) { recipe redisTemplate.opsForValue().get(key); if (recipe null) { recipe recipeMapper.selectById(id); if (recipe ! null) { redisTemplate.opsForValue().set(key, recipe, 1, HOURS); } else { // 缓存空值防止穿透 redisTemplate.opsForValue().set(key, new NullValue(), 5, MINUTES); } } } } return recipe instanceof NullValue ? null : recipe; }7.2 数据库优化MySQL调优关键参数innodb_buffer_pool_size4G innodb_log_file_size1G max_connections500慢查询优化案例-- 优化前没有使用索引 SELECT * FROM recipe WHERE title LIKE %火锅% ORDER BY create_time DESC; -- 优化后全文索引覆盖索引 ALTER TABLE recipe ADD FULLTEXT INDEX ft_title (title); SELECT id,title FROM recipe WHERE MATCH(title) AGAINST(火锅 IN BOOLEAN MODE) ORDER BY create_time DESC;8. 容器化部署8.1 Dockerfile配置多阶段构建减小镜像体积FROM maven:3.8-jdk-11 AS build COPY . . RUN mvn package -DskipTests FROM openjdk:11-jre-slim COPY --frombuild /target/*.jar app.jar ENTRYPOINT [java,-jar,/app.jar]8.2 Kubernetes部署关键资源配置示例apiVersion: apps/v1 kind: Deployment metadata: name: foodie-web spec: replicas: 3 template: spec: containers: - name: app image: registry.example.com/foodie:1.0 resources: limits: cpu: 2 memory: 2Gi livenessProbe: httpGet: path: /actuator/health port: 80809. 项目演进方向当前系统已在生产环境稳定运行9个月后续计划引入Flink实现实时数据分析用GraphQL重构API接口开发微信小程序端在开发过程中最深的体会是Spring Boot的快速迭代能力让团队能专注于业务创新而不用反复处理框架整合问题。比如当需要接入第三方登录时只需添加spring-boot-starter-oauth2-client依赖配置几个参数就能立即使用。