提供植物鉴别功能、含有 Android
、网页端、手机移动网页端多端兼容 api
后端服务项目
软件架构说明
项目架构:
接口文档:http://localhost:8083/swagger-ui.html
数据映射url:http://localhost:9001/crop_multi_data/
后端数据存储采用两台服务器支持,一台作为mysql和reids缓存和基本类型数据存储,另一台作为大数据文章类型存储。
数据库支持:MySQL、Redis、MongoDB
- MySQL 处理关系型数据
MySQL
对于处理关系型数据是最合适的,它在POJO
与PO
之间数据的映射是一对一的,所以在开发的时候可以使用逆向工具快速生成对应的POJO
类,缩短开发时间和开发成本。
- MongoDB 处理非关系型数据
对于非关系型数据存储,采用MongoDB
是比较合适的,比如本项目的评论和文章都采用这种方式存储,它们不仅支持k-v
形式、文档形式、图片形式,还容易扩展,数据没有耦合性。
- Redis 处理缓存数据
对于可以使用弱一致性的,比如CSDN
它的文章阅读量是每天中午会更新,不要求即时更新,就可以使用Redis
来缓存数据,而且我在Redis
数据库前面做了一层类似于布隆过滤器,先是过滤了敏感和没有营养的词汇,然后才可能会去查询数据库,如果关键字没有被Redis
采录,它会写入到Redis
中,以维护热度搜索,然后依次按照文章类名、标题、简述、内容去匹配,前面匹配到了,就停止去匹配后面的字段,就直接将匹配的文章查询出来。
- 分布式事务
由于使用了MySQL
与MongoDB
的业务上耦合,但单机MongoDB
不支持事务,使得业务耦合可能造成事务结果的不一致性
解决方案
- 使用分布式事务解决;
- 使用
MQ
消息队列,先把消息发MQ
,确定消息落盘后,MySQL执行事务,提交/回滚,都给MQ
发通知。接着MQ
收到通知,再把消息退给MongoDB
,MongoDB
事务必须保证成功,如果失败,就人工处理,手动补偿。
项目的云部署采用两台服务器,将前端项目接口暴露,后台关键接口放到另一台服务器,再通过反向代理接口去访问后端服务,能起到隐藏真实服务地址,起到保护的作用,而之所以把MongoDB
放到前端暴露的接口,是考虑到服务器的抗压能力,当然还有其他成本因素包含在里面。
- 请求过程
用户发起请求 → 云服务器1 → 前端端口 → nginx
反向代理 → 代理调用api
接口 → 云服务器2 → 真实服务端口 → Redis
(缓冲层) → MySQL/MongoDB
(持久层)
之所以在前端与后端端口交互之间加了一层代理层,是起到代理后端服务的作用,能够直接屏蔽掉后端的地址,保证后端服务的安全。
- 高可用服务
目前项目后端接口服务只使用了一个端口,在该环境下,还可以启动多端口的接口服务,如果要搭建高可用服务,可以直接到nginx中配置后端代理点,让这个代理点去代理多个服务接口,如果某一个springboot
挂了(高请求导致),可利用Nginx
来搭建负载均衡
使用nginx
搭建反向代理和负载均衡:
前端项目所在机器:
http {
// 使用的负载均衡策略
upstream cropServer {
server [后端ip]:[port] weight=10;
server [后端ip]:[port] weight=10;
}
// 服务代理
server {
listen 9001;
server_name localhost;
# Reverse proxy backend server
// 负载均衡方式
location / {
proxy_pass http://cropServer;
}
// 直连方式
location / {
proxy_pass http://[ip]:[port];
}
location /crop_multi_data/ {
proxy_pass http://[后端ip]:9001;
index index.html index.htm index.jsp;
}
}
}
后端服务所在机器 代理静态文件
http {
server {
listen 9001;
server_name 127.0.0.1;
location /crop_multi_data/ {
root /**/***/;
autoindex on;
}
}
}
上面代理静态文件是通过前台机器代理到后台的代理,再由后台代理映射到本地文件
- 代理层
- 阅读定时同步
阅读量不会即时更新到 mongodb
,但会及时更新到 redis
缓存起来
开启定时任务,使用合适的策略将数据同步到 mongodb
中。
实现
用户阅读一篇文章时,在所调用的 api
接口获取该请求所在的ip
地址和 文章id
,这样下一步就可以做到将该ip
和文章id
保存到Redis
中,能够避免短时间内重复阅读同一篇文章而额外去执行一次 数据库IO
操作,可以在Redis
设置该key
存活时间为 1 小时。
这样,我们就实现了即时将数据暂存到了Redis
中,接着我们只需开启一个子线程获取Redis
中每篇文章的id
值和阅读数,将其同步到MongoDB
就可以了,实现可以参考ScheduledExecutorService
的scheduleAtFixedRate()
方法。
- 热度搜索
热度搜索利用的是 Redis
的两大数据结构zSet
和Hash
来实现存储近期和搜索量的,从而实现热度搜索,当然搜索前必须得先对敏感关键字过滤,切忌让敏感关键字作为了热搜词汇,导致出现敏感文章被顶推上热搜。
实现
热度搜索需要在前端做一个搜索,接着调用api
将用户的搜索词汇放到Redis
中,可以使用zSet
,把时间戳当成zSet
的权值,所以是有序的,可以获取近期的搜索,实现近期搜索(这里还可以将该关键字连同userId
放到Redis
做用户搜索记录),接着再使用Redis
的Hash
数据结构,将关键字作为key
,value
作为关键字搜索量,就能够做到近期热度搜索了。
- 2022/4/7
- 配合 nginx 的url 映射技术,新增浏览器 查阅后台日志文件
- 2022/4/8
- 更新评论内容、管理员注册和登录;
- 修复发表评论时,父评论id和被回复用户id为空仍可以保存的bug;
- 2022/4/8
- 更新标签功能;
- 修复标签为私有,其他人不可见;
- 2022/4/9
- 增加删除文章功能;
- 2022/4/12
- 修复了管理员对文章分类可重复新增的bug
- 2022/4/13
- 修复了用户恶意在文章详情接口输入错误文章id导致空指针的情况,导致对redis数据库的访问失败
- 添加敏感信息过滤功能(使用DFA算法)
- 添加推荐和近期文章接口
- 2022/4/14
- 一部分接口分页失效问题
- 更新热度文章获取接口,更新获取策略
- 支持一种新的标准时间格式
- 修复获取评论接口中toUser字段为空报错的情况
- 2022/4/15
- 更新评论接口,新增时间排序功能
- 2022/4/16
- 更新获取所有评论的接口,引用内容可见
- 修复获取评论接口字段查询错误
- 2022/4/17
- 修复标记标签和重新标记标签出现的问题