Skip to content

tudoutiaoya/blog-backend

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

基于SpingBoot + Vue的博客项目

应用技术

SpringBoot + Redis + Mysql + Swagger + 七牛云

学到东西

  1. Swagger接口文档

  2. 统一异常处理,捕获Controller层的异常

  3. 登录拦截器,对指定接口进行拦截 从redis中获取token以及用户信息,判断是否登录

  4. 登录拦截器从redis获取到用户,放入到ThreadLocal中,方便在请求内部获取用户信息,减少了用户信息远程查询次数

  5. 登录拦截器中使用完ThreadLocal做了value的删除,防止了内存泄漏

  6. JWT + redis token令牌登录方式,登录用户做了缓存, 灵活控制用户的过期(提掉线)【token令牌登录方式,访问认证速度快,session共享,安全性】【redis做了令牌和用户信息的管理 1.进一步增加了安全性 2.登录用户做了缓存,灵活控制用户的过期时间】

  7. 对接七牛云对象存储,做了CND加速, 图片上传到七牛云,降低服务器访问压力

  8. 查看文章详情,阅读数量增加 扔到线程池中执行,不影响主业务逻辑(对当前主业务流程无影响的操作,放入线程池中执行) 【查看完文章之后,本应该直接返回数据了,这时候做了一个更新操作,更新时加写锁,阻塞其他的读操作,性能就会比较低,更新 增加了此次接口的 耗时 如果一旦更新出问题,不能影响 查看文章的操作】

  9. 线程安全 文章详情 (1)先查询出来文章 (2)更新阅读数扔到线程池中 (3)更新阅读数量 update 文章表 set view_count = oldValue+1 where id = ? and view_count = oldValue

  10. 统一缓存处理,自定义缓存注解 + AOP,想要缓存的接口直接加上注解

  11. 统一日志处理,自定义日志注解 + AOP 记录日志(记录了 module、operation、请求方法名参数、请求ip地址、接口执行时间)

  12. RequestContextHodler Spring提供的工具获取request对象, 比如说日志切面需要获取request对象

    获取request 请求工具类 RequestContextHolder 是Spirng提供的一个用来暴漏Request对象的工具,利用RequestContextHolder,可以在一个请求线程中获取到Request,避免了Request从投传到尾的情况

  13. DO对象转换为VO对象 copy copyList

  14. Dockerfile构建镜像,Nginx + Docker 部署

  15. 使用MQ解决缓存不一致问题,修改文章后,缓存中的数据和数据库中不一致,采用先更后删,修改文件-->更新数据库-->发送消息到MQ中-->删除缓存

亮点

image-20230102120059301

使用mq解决缓存不一致问题

修改文章后,能够保证最终一致性

删除文章列表缓存、更新查看某个文章的缓存

部署:加上/api区分 前端的访问与后端的访问

踩坑

  1. Long类型id 太长传到前端 无法识别
  2. FROM_UNIXTIME 查询月份用 %c 而不是 %m(月份小于10为 01, 02, 03...)

功能

主界面

image-20230105105001581

首页

  1. 首页-文章列表

    瀑布流展示,分页查询(第几页+每页大小)

    注解 加入Redis缓存

    按时间倒排

  2. 首页-最热标签

    标签对应的文章数量多

    文章标签表 按照标签分组-->按照文章排序

  3. 首页-最新文章

    按照时间排序

    注解 加入Redis缓存

  4. 首页-文章归档

    查询xxx年xxx月文章数量

    时间戳 --> 函数 --> year、month

    按照year、month 分组 查询分组后的数量

  5. 首页-最热文章

    观看数量最多

    注解 加入Redis缓存

文章详情

  1. 文章详情

    查询文章 -> 文章内容 and 一级评论、二级评论

    更新阅读数量 ---> 扔到线程池中

分类、标签、归档

  1. 文章分类

    查看所有文章分类

  2. 文章标签

    查看所有标签

  3. 查看特定分类

    分类详细信息

    相应分类文章列表 --> 复用文章列表接口,加入分类查询参数

  4. 查看特定标签

    标签详细信息

    相应标签文章列表 --> 复用文章列表接口,加入标签查询参数

  5. 查看特定归档

    相应归档文章列表 --> 复用文章列表接口,加入归档(year、month)查询参数

登录、注册

  1. 登录

    验证参数合法性

    数据库中验证

    生成token

    存入redis 缓存token和用户信息

  1. 注册

    验证参数合法性

    数据库中验证

    生成token

    存入redis 缓存token和用户信息

  2. 注销

    redis 中删除token

写文章、评论

  1. 写文章

    上传图片接口 --> 上传到七牛云

    查询文章分类 --> 发表时选分类

    查询文章标签 --> 发表时选标签

    加入到登录拦截器

  2. 评论

    封装评论信息

    更新文章表评论数量

battle

为什么jwt用redis

  1. 登录用户信息做缓存,一些必须登录的接口放入到登录拦截器当中,判断用户是否登录,(顺便放入ThreadLocal中,在请求线程中,随时获取登录的用户,做了线程隔离,减少远程数据库查询次数)

  2. 灵活控制用户过期时间,注销功能,jwt是保存在客户端,服务端无法控制

  3. 进一步增加了安全性,jwt中不能存放敏感信息,jwt数据载荷部分可以存放用户的id,,做用户验证的时候,(reids中key为token,value为用户信息), 用token从缓存中获取用户信息进行验证

全局异常处理是怎么做的

用一个类标注 @ControllerAdvice 注解,对加了@Controller注解的方法进行拦截处理(AOP), 然后在方法上边标注@ExceptionHandler 表示要处理什么样的异常

ThreadLocal 为什么会有内存泄漏

线程池的参数讲一下

为什么更新阅读数量放到线程池中

阅读数量与主业务逻辑分隔开,将不影响主业务逻辑放入到线程池中执行

查看完文章之后,本应该直接返回数据了,这时候做了一个更新操作,更新时加写锁,阻塞其他的读操作,性能就会比较低,更新 增加了此次接口的 耗时 如果一旦更新出问题,不能影响 查看文章的操作

统一日志处理是怎么做的

首先定义了一个 日志注解,然后定义一个切面类,切点就是我们的缓存注解,在通知中记录请求方法名、请求参数、请求接口耗时、请求ip等,任何加了缓存注解的方法都会加入我们的通知逻辑。

统一缓存是怎么做的

自定义一个缓存注解,然后利用SpringAOP面向切面编程,加了缓存注解的接口,会先去缓存中查找,如果缓存中不存在或者缓存过期,那么再执行接口的业务逻辑去数据库中查询。

为什么会出现缓存不一致问题

当修改文章之后,数据库中的数据更新了,但是缓存中缓存了我们的文章列表,缓存中的数据还是旧的数据,这样会出现用户更新文章之后显示的文章列表中还是旧的数据,出现了缓存不一致问题。

如何解决缓存不一致的问题

采用了先更后删的解决方案,利用MQ解决,MQ解耦,更新文章,然后发送一条消息到MQ中,由服务消费者进行处理,删除缓存。

不适用MQ可以吗,更新完直接删除缓存

可以,但是不好。一旦删除缓存失败还是会导致我们的缓存不一致问题,利用MQ呢,可以失败重试。。。。// 之后添加

为什么更新阅读数操作会出现缓存不一致问题

部署

原生部署

前端Nginx + 后端jar包运行

Docker部署

前端Nginx + 后端构建Docker镜像运行

前端Nginx + 后端Docker-compose容器编排

后端的服务非常多:Mysql、Redis、RabbitMQ、SpringBoot镜像

未解决:vue项目部署在nginx非根目录,图片资源访问出错

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published