1.核心功能:
- 发帖、评论、私信、转发;
- 点赞、关注、通知、搜索;
- 权限、统计、调度、监控;
2.核心技术:
- Spring Boot、SSM
- Redis、Kafka、ElasticSearch
- Spring Security、Quatz、Caffeine
3.项目亮点:
- 项目构建在Spring Boot+SSM框架之上,并统一的进行了状态管理、事务管理、异常处理;
- 利用Redis实现了点赞和关注功能;
- 利用Kafka实现了异步的站内通知;
- 利用ElasticSearch实现了全文搜索功能,可准确匹配搜索结果,并高亮显示关键词;
- 利用Caffeine+Redis实现了两级缓存,并优化了热门帖子的访问。
- 利用Spring Security实现了权限控制,实现了多重角色、URL级别的权限管理;
- 利用HyperLogLog、Bitmap分别实现了UV、DAU的统计功能,100万用户数据只需*M内存空间;
- 利用Quartz实现了任务调度功能,并实现了定时计算帖子分数、定时清理垃圾文件等功能;
- 利用Actuator对应用的Bean、缓存、日志、路径等多个维度进行了监控,并通过自定义的端点对数据库连接进行了监控。
实现了项目部署上线,日访问量到达200+(很欣慰了)
![](https://img-blog.csdnimg.cn/35880f76dbd1419ca6c3fbb21b14b444.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rSL6JGx6aW85bmyeXk=,size_20,color_FFFFFF,t_70,g_se,x_16)
开始总结
![](https://img-blog.csdnimg.cn/20210520202315292.png)
图中的下方三行是基石。
原图中带下划线的和带@的内容是重点内容。
算法:前缀树过滤敏感词。
数据结构:Redis的数据结构。Redis有许多种数据结构,每种数据结构适合解决什么样的问题,适合缓存什么样的数据。
Kafka:重点是生产者与消费者模式,只有你理解了,才能明白什么时候用哪些功能来解决什么问题。
ElasticSearch:重点是它的数据结构,以索引(表)方式来存。建议找文章自己学习ES的索引。
Caffeine:本地缓存,有一定局限性,把它和Redis一起回顾,因为它们都是缓存。
从运维角度来讲整个项目(就是分布式部署)
![](https://img-blog.csdnimg.cn/12b3f620331b43cdac561a686d7f06d3.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5rSL6JGx6aW85bmyeXk=,size_20,color_FFFFFF,t_70,g_se,x_16)
静态资源(图片)和动态资源(controller)是部署在两个服务器上的。比如:牛客网的是在域名
nowcoder.com
上,牛客网的图片是在
static.nowcoder.com
上,这两个是不同的域名,会对应到两个不同的服务器。
客户端找nginx请求动态资源,找CDN请求静态资源。
nginx:能对服务器做反向代理,如果你有多个服务器,它能对服务器做负载均衡。nginx会主从备份,即有两台服务器,一台处理客户端请求,一台只是备份。
CDN:把你的资源部署到各地的多个服务器上,用户访问网站时,会从最近的服务器加载资源。
有多台服务器,nginx会把客户端请求发送到某一台服务器,每台服务器中都有项目community和本地缓存。
数据库通常要做读写分离的,一台数据库复制读,一台数据库负责写。负责写的数据库在写入数据以后,会把数据同步到负责读取的数据库里。
为什么Redis、Kafka、ElasticSearch都是部署多个,但是数据库却只进行主从备份?因为数据库部署和它们一样做集群式部署的话的话,那么就要处理分布式事务,这个比较麻烦,所以我们一般尽量避免这种情况。而且,一般的业务也达不到要部署多个数据库,因为在访问数据库前,会先访问本地缓存,再访问Redis,最后都没有才访问数据库。
我们用的文件服务器是七牛云。
咋们就从核心功能处开始捋,一个一个功能点开始总结实现过程:
核心功能:
发帖:
点击发布帖子按钮,输入帖子标题内容等信息后发布,以JS异步请求的方式进行提交,首先判断该线程中有无用户信息(在登录成功是会将用户信息与本线程的id进行键值对存储,保证了多线程情况下安全)即判断有无登录,已登录则初始化帖子信息将其添加至DB中,并且触发发帖事件,采用kafka信息队列来完成,将帖子信息添加至ES中,通知再将其存入Redis中计算帖子的分值,用来在最热列表中展示出来:
添加帖子
// 发布帖子
@RequestMapping(path = "/add",method = RequestMethod.POST)
@ResponseBody
public String addDiscussPost(String title,String content){
User users = hostHolder.getUsers();
if(users == null){
return CommunityUtil.getJSONString(403,"你还没有登录!");
}
DiscussPost post=new DiscussPost();
post.setUserId(users.getId());
post.setTitle(title);
post.setContent(content);
post.setCreateTime(new Date());
discussPostService.addDiscussPost(post);
// 触发发帖事件 添加至es中
Event event = new Event()
.setTopic(TOPIC_PUBLISH)
.setUserId(users.getId())
.setEntityType(ENTITY_TYPE_POST)
.setEntityId(post.getId());
eventProducer.fireEvent(event);
// 计算帖子分数
String redisKey= RediskeyUtil.getPostScoreKey();
redisTemplate.opsForSet().add(redisKey,post.getId());
return CommunityUtil.getJSONString(0,"发布成功!");
}
kafak消费者身份,ES同样添加数据
// 消费 发帖事件 es添加数据
@KafkaListener(topics = {TOPIC_PUBLISH})
public void handlePublishMessage(ConsumerRecord record){
if(record == null && record.value() == null){
logger.error("消息的内容为空!");
return;
}
Event event = JSONObject.parseObject(record.value().toString(), Event.class);
if(event == null){
logger.error("消息格式错误!");
return;
}
DiscussPost discussPost =
discussPostService.selectDiscussPostById(event.getEntityId());
elasticsearchService.saveDiscussPost(discussPost);
}
评论:
评论这一块