Redis 实战 —— 11. 实现简单的社交网站
简介
前面介绍了广告定向的实现,它是一个查询密集型 (query-intensive) 程序,所以每个发给它的请求都会引起大量计算。本文将实现一个简单的社交网站,则会尽可能地减少用户在查看页面时系统所需要做的工作。 P184
用户和状态 P185
用户对象存储了用户的基本身份标识信息、用户的关注者人数、用户已发布的状态消息数量等信息,它是构建其他可用且有趣的数据的起点。 P185
状态消息记录了不同的用户都说了什么,以及不同用户之间进行了什么交流,这些由用户创建的状态消息是社交网站真正的内容。 P185
用户信息 P185
用户信息使用 HASH
来存储,用户信息包括:用户名、用户拥有的关注者人数、用户正在关注的人的数量、用户已发布的状态消息的数量、用户的注册日期以及其他一些元信息 (meta-information) 。示例: "user:139960061": {"login": "dr_josiah", "id": "139960061", "name": "Josiah Carlson", "followers": "176", "following": "79", "posts": "386", "signup": "1272948506"}
P185
创建用户 P185
创建用户时需要根据用户指定的用户名以及当时的时间戳,创建一个正在关注数量、关注者数量、已发布状态消息数量都被设置为 0 的对象。 P185
创建时除了要对用户信息进行初始化,还需要对用户名进行加锁,用以防止多个请求 (request) 在同一时间内使用相同的用户名来创建新用户(可以使用 08. 实现自动补全、分布式锁和计数信号量 中实现的分布式锁进行加锁操作)。
状态消息 P186
状态消息使用 HASH
来存储,状态消息包括:消息本身、消息发布的时间、消息发布者的 id 、用户名(冗余用户名是为了展示时避免用户信息的 HASH
,因为用户名是不会改变的)以及其他一些关于状态消息的附加信息。示例: "status:223499221154799616": {"message": "My pleasure. I was amazed that...", "posted": "1342908431", "id": "223499221154799616", "uid": "139960061", "login": "dr_josiah"}
P186
创建消息时需要先获取用户名,再将相关的信息组合起来存储到 HASH
中。 P187
主页时间线 P187
主页时间线是一个列表,它由用户以及用户正在关注的人所发布的状态消息组成。个人时间线与主页时间线类似,它只包含用户自己所发布的状态消息。因为主页时间线是用户访问网站时的主要入口,所以这些数据必须尽可能地易于获取。 P187
主页时间线使用有序集合存储,成员为状态消息的 id ,成员的分值为状态消息发布的时间戳。示例: "home:139960061": {..., "227138379358277633": "1342988984", ...}
P188
获取主页时间线分为两步: P188
- 使用
ZREVRANGE
分页获取最新状态消息的 id 列表 - 使用流水线和
HGETALL
获取到状态消息 id 列表中所有状态消息
关注者列表和正在关注列表 P189
用户正在关注列表以及关注者列表同样用有序集合存储,成员为用户的 id ,成员的分值为用户开始关注某人或被某人关注的时间戳。示例:"followers:139960061": {..., "558960079": "1342915440", "14502701": "1342917840", ...}
"following:139960061": {..., "18697326": "1339286400", "558960079": "1342742400"}
P188
当用户开始关注或者停止关注另一个用户时,就需要对这两个用户的正在关注有序集合和关注者有序集合进行更新 (ZADD
, ZREM
),并修改他们用户信息中的关注数量和被关注数量 (HINCRBY
),同时还需要操作者的主页时间线,添加或删除另一个用户的状态消息 (ZUNIONSTORE
)。 P189
删除操作需要两个命令,因为有序集合没有求差集的命令,有两种方式可以实现:
- 使用
ZREVRANGE
获取另一用户个人主页对应的有序集合的成员列表,然后再使用ZREM
从操作用户主页流水线对应的有序集合中删除这些成员 - 先使用
ZUNIONSTORE
合并集合,操作用户主页流水线对应的有序集合的权重为 1 ,另一用户个人流水线对应的有序集合的权重为 0 ,聚合方式使用MIN
,然后再使用ZREMRANGEBYSCORE key 0 0
移除所有分值为 0 的成员(可以使用流水线合并)
所思
练习题让在给定的基础上支持批量关注和取消关注的操作。其实业务中大部分情况下单个和批量操作都是类似的,并且后续很可能会支持批量操作,为了减少后期后端开发量、避免改动接口签名或类似功能接口爆炸性增长,我常常都会提前实现批量操作的接口。我们平时开发时不能仅对当前需求进行处理,还要去理解整体功能并站在用户的角度进行思考,尽早关注可能的变动,这样才能开发出具有扩展性的接口。
状态消息的发布与删除 P191
用户可以执行的一个最基本的操作就是发布状态消息,前面已将实现了创建状态消息的逻辑,接下来将实现把新的状态消息添加到每个关注者的主页时间线中。 P191
为了让发布操作尽可能快地返回,我们在创建后会仅针对前 1000 个关注者的主页时间线添加新的状态消息,如果当前发布者的关注者超过了 1000 个,则使用 09. 实现任务队列、消息拉取和文件分发 中实现的任务队列异步处理后续的关注者。 P192
删除消息时同理操作即可,先删除状态消息本身,然后处理发布者自己的统计信息,再从个人时间线中删除该消息 id ,最后再从前 1000 个关注者的主页时间线中删除,若关注者人数超过 1000 ,再使用任务队列异步处理后续的关注者。 P193
流 API P194
构建一些函数来广播 (broadcast) 简单的事件 (event) ,然后由负责进行数据分析的事件监听器 (event listener) 来接收并处理这些事件,流 API 请求能在一段比较长的时间内持续地返回数据。 P194
流 API 的作用是随着时间的推移,产生一个由时间组成的序列,以此来让整个网络上的客户端和其他服务及时地了解到网站目前正在发生的事情。 P195
构建流 API 的过程中需要进行各种各样的决策,这些决策主要和以下 3 个问题有关: P195
- 流 API 需要对外公开哪些事件?
- 需要公开目前实现的四种用户操作:发布消息、删除消息、关注用户和取消关注用户。本节将以创建发布消息事件和删除消息事件为例进行实现
- 是否需要进行访问限制?如果需要的话,采取何种方式实现?
- 目前仅在涉及用户隐私或者系统资源的时候,考虑访问限制
- 流 API 应该提供哪些过滤选项?
- 既可以通过关注过滤器(基于用户进行过滤)、检测过滤器(基于关键字进行过滤)和位置过滤器来获取过滤后的消息,又可以通过类似推特的 firehose (可以获取所有公开消息) 和 sample (只能获取少量公开消息) 这样的流来获取一些随机的消息。
标识客户端 P198
为了区分不同的客户端,需要对其进行标识,大部分情况下来说只用考虑已登陆用户即可,那我么使用用户 id 进行标识,为了同时实现验证用户 id 的功能,可以使用 JWT 。
过滤消息 P200
我们将使用 Redis 的 PUBLISH
命令和 SUBSCRIBE
命令(05. Redis 其他命令简介 介绍了这两个命令的用法和缺陷)来实现订阅发布功能:当用户发布一条消息时,程序会通过 PUBLISH
发送给某个频道,而各个过滤器则通过 SUBSCRIBE
来订阅并接收那个频道的消息,并在发现与过滤器相匹配的消息时,将消息回传 (yield back) (如果是发布消息,则回传实体;如果是删除消息,则回传 id 和当前状态)给 Web 服务器,然后由服务器将这些消息发送给客户端。 P200
本文首发于公众号:满赋诸机(点击查看原文) 开源在 GitHub :reading-notes/redis-in-action
Redis 实战 —— 11. 实现简单的社交网站的更多相关文章
- redis实战笔记(8)-第8章 构建简单的社交网站
本章主要内容 用户和状态 主页时间线 关注者列表和正在关注列表 状态消息的发布与删除 流API
- Redis实战:如何构建类微博的亿级社交平台
微博及 Twitter 这两大社交平台都重度依赖 Redis 来承载海量用户访问.本文介绍如何使用 Redis 来设计一个社交系统,以及如何扩展 Redis 让其能够承载上亿用户的访问规模. 虽然单台 ...
- (转)国内外三个不同领域巨头分享的Redis实战经验及使用场景
随着应用对高性能需求的增加,NoSQL逐渐在各大名企的系统架构中生根发芽.这里我们将为大家分享社交巨头新浪微博.传媒巨头Viacom及图片分享领域佼佼者Pinterest带来的Redis实践,首先我们 ...
- Redis实战经验及使用场景
随着应用对高性能需求的增加,NoSQL逐渐在各大名企的系统架构中生根发芽.这里我们将为大家分享社交巨头新浪微博.传媒巨头Viacom及图片分享领域佼佼者Pinterest带来的Redis实践,首先我们 ...
- Redis实战
大约一年多前,公司同事开始使用Redis,不清楚是配置,还是版本的问题,当时的Redis经常在使用一段时间后,连接爆满且不释放.印象中,Redis 2.4.8以下的版本由于设计上的主从库同步问题,就会 ...
- Redis实战之Redis + Jedis
用Memcached,对于缓存对象大小有要求,单个对象不得大于1MB,且不支持复杂的数据类型,譬如SET 等.基于这些限制,有必要考虑Redis! 相关链接: Redis实战 Redis实战之Redi ...
- Redis实战之征服 Redis + Jedis + Spring (一)
Redis + Jedis + Spring (一)—— 配置&常规操作(GET SET DEL)接着需要快速的调研下基于Spring框架下的Redis操作. 相关链接: Redis实战 Re ...
- Redis实战之Redis + Jedis[转]
http://blog.csdn.net/it_man/article/details/9730605 2013-08-03 11:01 1786人阅读 评论(0) 收藏 举报 目录(?)[-] ...
- redis实战笔记(10)-第10章 扩展Redis
本章主要内容 扩展读性能 扩展写性能以及内存容量 扩展复杂的查询 随着Redis的使用越来越多, 只使用一台Redis服务器没办法存储所有数据或者没办法处理所有读写请求的问题迟早都会出现, 这 ...
随机推荐
- 看起来很唬人,然而却简单实用的CAP理论
在做分布式系统开发时,经常会或多或少的听到CAP理论.或者是处理节点间数据一致性的问题.CAP理论很简单,但却是很多软件设计的宏观指导,因此也有人将之称为架构师必须掌握的理论之一.鉴于理论的东西相对来 ...
- myeclipse经常弹出Subversion Native Library Not Available框解决办法
两种解决方案:(1)在myeclipse中选择 "Windows" -> Perferences. 然后通过左上方的筛选,选出svn设置菜单,点解左侧的"SVN&q ...
- JavaWeb代码复用
servlet部分,可能用得到的复用的代码: 1.dopost设置字符 request.setCharacterEncoding("utf-8"); response.setCha ...
- Docker下配置KeepAlive支持nginx高可用
案例子任务一.安装配置keepalived 步骤1:使用nginx镜像生成nginx-keep镜像 1) 启动nginx容器并进入 docker run -d --privileged nginx / ...
- 自动化运维工具-Ansible之6-Jinja2模板
自动化运维工具-Ansible之6-Jinja2模板 目录 自动化运维工具-Ansible之6-Jinja2模板 Ansible Jinja2模板概述 Ansible Jinja2模板使用 Ansib ...
- eclipse中安装jetty插件并使用
一.eclipse中jetty插件安装: 打开eclipse,依次点击菜单Help->Eclipse Marketplace,在Find后面的框中输入jetty,选择第一项进行install即可 ...
- 面试官:Mysql 中主库跑太快,从库追不上怎么整?
写这篇文章是因为之前有一次删库操作,需要进行批量删除数据,当时没有控制好删除速度,导致产生了主从延迟,出现了一点小事故. 今天我们就来看看为什么会产生主从延迟以及主从延迟如何处理等相关问题. 坐好了, ...
- 软件“美不美”,UI测试一下就知道
摘要:软件测试的最高层次需求是:UI测试,也就是这个软件"长得好不好看". 为了让读者更好地理解测试,我们从最基础的概念开始介绍.以一个软件的"轮回"为例,下图 ...
- 一张脑图整理Docker常用命令
先上图: Dcoker基本概念 Docker 包括三个基本概念: 镜像(Image):Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序.库.资源.配置等文件外,还包含了一些为运行时 ...
- 附录 A ES6附加特性
目录 模板字符串 解构 对象的解构 数组的解构 增强版对象字面量 模板字符串 const student = { name: "Wango", age: 24, } // 普通字符 ...