项目地址:https://github.com/lioncattt/wenda
-
登录和注册
1.1 登录功能:
(1) 校验密码时取出该用户对应的密码和盐进行md5加密后与数据库的密码(密文)进行校对。讲讲md5:md5算法是一种不可逆的算法,使用的是hash算法,只有加密过程没有解密过程,但因为存在彩虹表(黑客将常用的密码通过md5加密后存储到表中,将用户密码进行暴力破解),因此需要加盐,即给每一个用户密码后面添加一个随机的字符串再进行加密。
(2) 校验密码成功后,向该用户发放一个token(一个userId对应一个随机的UUID,还有有效时间),这个token是一个随机的字符串,将其保存到cookie中。当浏览器再次发送请求时,就可以校验浏览器的cookie中的token是否和数据库中的一致。优化: 前期将token存放到mysql中,后期将其放到reidis,大大提高查询效率。为什么要使用token:token是一个标识用户身份的令牌,可以避免每次登录都要到数据库中校验对应的密码,减少频繁查询数据库。
1.2 注册功能:
(1)给用户的密码加盐后通过md5加密将密码已密文的形式存到数据库。
(2) 给注册成功的用户发放token
(3) 实现了通过调用阿里云短信通道接口发送手机验证码的功能。前期将验证码存到session中,后期优化将验证码存到redis中并设置有效期
1.3 拦截器
(1) 登录拦截器: 根据请求的cookie中携带的token查询出当前的用户信息,存放到ThreadLocal中,可供上下文使用。
为什么要使用ThreadLocal?考虑到可能出现线程并发问题,使用ThreadLocal可以保证每一个线程有对应的变量副本(底层使用ThreadLocalMap存储),保证了线程隔离。每一个threadLocal都保存了当前线程的用户信息。
(2) 页面跳转拦截器: 权限控制,实现未登录用户登录后可跳转回之前的页面。 -
敏感词过滤
(1) 读取文件,构建前缀树。使用字典树算法实现敏感词过滤。时间复杂度为O(n),暴力检索时间复杂度为O(n^2)。 -
Redis使用Set实现点赞点踩功能
(1) 将问题的回答和回答的回复等功能抽象成一个Comment, Comment中用entityType和entityId两个字段确定该评论关联的对象。将entityType和entityId构造为对应的like key或dislike key,userId作为value通过Set集合存到redis中。为什么要使用HashSet?去重,保证userId唯一。 -
手写异步框架实现发送站内信功能
(1) 将事件模型封装成EventModel, 定义事件类型枚举类EventType,定义EventHandler事件处理器接口。
(2) 通过Redis的List数据(底层是一个双向链表)结构实现消息队列。
(3) 将事件通过生产者EventProducer类入队。
(4) 消费者EventConsumer初始化时通过HashMap构建EventType和EventHandler的一对多关系
(5) EventConsumer类开启线程取出队列中的事件, 根据事件的EventType找到对应实现EventHandler接口的类执行。
使用异步队列的好处: Java中交互方式分为同步消息处理和异步消息处理两种:同步交互:指发送一个请求,需要等待返回,然后才能够发送下一个请求,有个等待过程;异步交互:指发送一个请求,不需要等待返回,随时可以再发送下一个请求,即不需要等待。
因为一个请求会分配一个线程进行请求处理,该线程负责获取数据、拼装数据或模板然后返回给前端;在同步调用获取数据接口的情况下(等待依赖系统返回数据),整个线程是一直被占用并阻塞的。使用异步无需等待,并保证所有事件都能够处理完。 -
使用Redis的zsort实现关注服务
为什么使用zsort(底层使用sort set)? zsore中有一个权重(scores)可以根据该权重进行排序。
关注列表可以在score中存入时间根据时间排序 -
使用solr实现全局搜索:
(1)配置要构建索引的表的字段。导入中文分词器。
(2) 比如插入一个问题时,使用solr会先创建一个document再通过分词器为对应的字段创建索引添加到该document中。
(3) 搜索时根据索引找到对应的文档。 -
部署
将数据库和springboot导出的jar包项目分别部署在两个服务器上,减轻服务器压力。
把jar包做成一个服务(service)来启动。为什么要导成jar包?前后端分离。(一般静态资源单独放在一台服务器上)