摘要:异步事件处理本项目涉及到多种异步事件的处理。即是的粉丝,是的关注对象。模式定义优缺点推事件触发后广播给所有粉丝。具体来说,推模式就是事件触发后产生,触发事件的用户下所有粉丝的实现中都存入该的。
项目源代码已托管在 Github,欢迎 Star、Fork。Q & A 问答社区
QA 是一个基于 B/S 架构而设计开发的社区网站。
主要为用户提供以下服务:
问题发布
评论
用户私信
关注
站内全文搜索
技术选型Spring Boot + MyBatis + MySQL + Redis + FreeMarker
功能描述 注册登录为了保证用户信息安全,系统对用户密码采用「salt + md5」方式进行加密。用户注册/登录成功后,系统会生成一个 ticket ,将 ticket 与用户 id 相关联,并将此信息插入到数据库表 login_ticket 中,同时将 ticket 响应给客户端。
用户每次请求页面的时候,都需要先经过 PassportInterceptor 拦截器,拦截器判断此 ticket 是否真实有效,若是,根据 ticket 对应的用户 id ,查出相应用户信息,并添加至页面上下文中。
用户内容发布问题发布
评论发布
私信发布
在以上 UGC (User Generated Content, 用户产生的内容)中,系统都会进行 HTML 标签及敏感词过滤,这在一定程度上防止网站被注入脚本或者充斥着不良信息。
若没有对 HTML 标签进行处理,当用户发布的内容含有如时,网站页面每次加载此内容时都会弹出消息框。
对于敏感词过滤,按照常规的思维,也是最简单的方式,就是:对于每个敏感词,都在文本中查找该敏感词是否出现,出现则进行替换。这种方式,每个敏感词都要在一段文本中进行遍历查找,复杂度非常高。
本项目采用「前缀树」方式实现敏感词过滤,空间换时间,效率较高。前缀树结点结构如下:
class TrieNode { // 标记是否为敏感词结尾 boolean end; // 该结点的所有直接子结点 MapsubNodes = new HashMap<>(); // 添加一个子结点 void addSubNode(Character key, TrieNode node) { subNodes.put(key, node); } // 根据key获取子结点 TrieNode getSubNode(Character key) { return subNodes.get(key); } }
后台从敏感词文件 SensitiveWords.txt 顺序读取每一行建立前缀树。进行过滤时,遍历需要过滤的文本,用星号替换发现的敏感词。假设文本长度为 len,前缀树的最大高度为 h,那么此算法的最坏时间复杂度为 O(len*h)。
算法比较
假如敏感词平均长度为10,数量为100000,文本长度为 len。
常规方式,复杂度O(100000 (len + 10));前缀树算法复杂度O(10 len)。
对于评论功能,系统建立的是一个统一的评论服务中心,通过 EntityType 与 EntityId 识别所评论的实体。用户对于问题/评论的回复,都可以应用此服务。查询某实体下的评论时,同样根据 EntityType 和 EntityId 查询即可。
用户内容赞踩赞踩功能采用「Redis」作为数据存储。Why Redis?
比较一下 Redis 和 MySQL:
Redis: key-value数据库,数据放在内存
MySQL: 关系型数据库,数据放在磁盘
Redis 适合放一些频繁使用、比较热的数据。因为数据放在了内存中,读写性能卓越。
Redis 类型 | 数据结构 | 应用场景 |
---|---|---|
List | 双向列表 | 最新列表、关注列表 |
Set | 无序集合 | 赞踩、抽奖、已读、共同好友 |
SortedSet | 优先队列 | 排行榜 |
Hash | 哈希表 | 不定长属性数 |
KV | 单一数值 | 验证码、PV、缓存 |
除了用户内容赞踩,在本项目中,Redis 还应用于以下场景:
异步事件处理
关注服务
Timeline
本小节讨论用户内容赞踩服务。
用户对某一实体点赞,会将"LIKE:ENTITY_TYPE:ENTITY_ID"作为 key ,用户 id 作为 value ,存入 like 集合中。同时移除 unlike 集合中该 key 对应的用户 id。点踩服务反之。
最后将点赞数响应给页面。
本项目涉及到多种异步事件的处理。如:
用户评论了某个问题
用户点赞了某条评论
用户关注了另一个实体
这些动作并不是单一的,它们会触发一些后续的操作:
用户评论了某个问题,系统除了处理“评论”这个动作外,还需要给该问题对应的用户发送一条消息,通知说“xx评论你的问题”,或者还需要给用户增加积分/经验...
事件触发者并不关心这些后续的任务,系统处理完某个动作后就可以将结果返回给触发者,而后续的任务交给系统进行异步处理即可。
因此,设计一个异步事件处理框架尤为重要。
本项目的异步框架如下图所示:
业务触发一个异步事件,EventProducer 将该事件(EventModel)序列化并存入队列(Redis List)中,EventConsumer 开启线程循环从队列中取出事件,识别该事件的类型,找出该类型对应的一系列 EventHandler,交由这些 Handler 去处理。
EventModel 的设计如下:
class EventModel { // 事件类型 EventType type; // 事件触发者 int actorId; // 事件对应的实体 int entityType; int entityId; // 事件对应的实体的Owner int entityOwnerId; // 一些扩展字段 MapSNS 关注服务exts; }
与评论功能类似,对于关注功能,系统同样建立了一个统一的关注服务中心,用户可以关注不同的实体(问题/用户),只需要通过 EntityType 和 EntityId 识别即可。
在数据存储方面,采用 Redis 的 zset 完成,原因有以下几个:
zset 有序,系统可以根据用户关注实体的时间倒序排列,获取最新的关注列表;
zset 去重,用户不能重复关注同一个实体;
zset 可以获取两用户之间的共同关注。
一个用户,系统存储两个集合:
①保存用户关注的实体;②保存关注用户的人。
即 A 是 B 的粉丝,B 是 A 的关注对象。 [参考资料 ]
用户关注了一个问题,需要发生两个动作:
将问题存入①中
在②中存入用户 id
这两个动作必须同时发生,因此,这里用到了 Redis 事务保证原子性和数据的一致性。
另外,对于关注功能,如前面所说,会触发异步事件,将消息通知被关注的实体 / 实体 Owner。
用户内容排名本系统未采用排名算法。若要了解相关算法,可以参考如下资料:
基于用户投票的排名算法(一):Delicious和Hacker News
基于用户投票的排名算法(二):Reddit
基于用户投票的排名算法(三):Stack Overflow
基于用户投票的排名算法(四):牛顿冷却定律
基于用户投票的排名算法(五):威尔逊区间
基于用户投票的排名算法(六):贝叶斯平均
Timeline Feed 流服务当用户更新动态时,该用户所有粉丝都可以在一定时间内收到新的动态(也称为新鲜事、feed),可以由 “推拉模式” 实现。
模式 | 定义 | 优缺点 |
---|---|---|
推 | 事件触发后广播给所有粉丝。 | 对于粉丝数过多的事件,后台压力较大,浪费存储空间; 流程清晰,开发难度低,关注新用户需要同步更新 feed 流。 |
拉 | 登录打开页面时,根据关注的实体动态生成 Timeline 内容。 | 读取压力大,存储占用小,缓存最新读取的 feed,根据时间分区拉取。 |
推拉 | 活跃/在线用户推,其他用户拉。 | 降低存储空间,又满足大部分用户的读取需求。 |
具体来说,推模式就是:事件触发后产生 feed,触发事件的用户下所有粉丝的 Timeline(redis list 实现)中都存入该 feed 的 id。而拉模式,就是当前用户去拉取自己关注的人的 feed。
更多推拉模式相关,可以参考 微博 feed 系统推拉模式。
Python 爬虫由于系统初始数据较少,为了丰富网站内容,本项目采用 pyspider 实现对 V2EX 网站的数据爬取,存储到后台数据库,并展示在前端页面上。
安装 pyspider:
pip install pyspider
启动 pyspider:
pyspider站内全文搜索服务
本项目在全文搜索服务上采用 Solr 框架,中文分词采用 Solr 自带的中文分词插件 solr_cnAnalyzer 。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/71222.html
摘要:作为微服务的基础设施之一,背靠强大的生态社区,支撑技术体系。微服务实践为系列讲座,专题直播节,时长高达小时,包括目前最流行技术,深入源码分析,授人以渔的方式,帮助初学者深入浅出地掌握,为高阶从业人员抛砖引玉。 简介 目前业界最流行的微服务架构正在或者已被各种规模的互联网公司广泛接受和认可,业已成为互联网开发人员必备技术。无论是互联网、云计算还是大数据,Java平台已成为全栈的生态体系,...
摘要:会话管理一直是企业级应用的重要部分。传统会话管理技术的问题的目的是解决传统的会话管理技术的各种问题。对如和之类的闭源产品,找到适合它们的会话管理技术的替代实现则通常是不可能的。典型的应用会将当前用户的身份及其安全级别或角色存储在会话里面。 欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文来自云+社区翻译社,由Tnecesoc编译。 会话管理一直是 Java 企业级应用的...
摘要:来源是最流行的用于开发微服务的框架。以下依次列出了最佳实践,排名不分先后。这非常有助于避免可怕的地狱。推荐使用构造函数注入这一条实践来自的项目负责人。保持业务逻辑免受代码侵入的一种方法是使用构造函数注入。 showImg(https://mmbiz.qpic.cn/mmbiz_jpg/R3InYSAIZkHQ40ly9Oztiart2lESCyjCH0JwFRp3oErlYobhibM...
阅读 2676·2021-09-24 09:47
阅读 4295·2021-08-27 13:10
阅读 2956·2019-08-30 15:44
阅读 1244·2019-08-29 12:56
阅读 2576·2019-08-28 18:07
阅读 2591·2019-08-26 14:05
阅读 2506·2019-08-26 13:41
阅读 1245·2019-08-26 13:33