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服务器没办法存储所有数据或者没办法处理所有读写请求的问题迟早都会出现, 这 ...
随机推荐
- Centos7 搭建openldap完整详细教程(真实可用)
最近,由于公司需求,需要搭建openldap来统一用户名和密码,目前市面上几乎所有的工具都支持ldap协议,具体ldap的介绍这里就不详细说明了,这里主要记录一下如果部署openldap来实现Ldap ...
- Keras使用多个GPU并行
model = Model(inputs=[v_i, v_j], outputs=output_list) model = multi_gpu_model(model,4) model.compile ...
- CentOS安装TensorFlow
1.升级python 系统自带的python是2.6,不能用,升级到2.7,方法见:http://www.cnblogs.com/stAr-1/p/9055980.html 2.升级python带来的 ...
- 基于snort、barnyard2和base的 网络入侵检测系统的部署与应用
1.项目分析 1.1.项目背景 伴随着互联网产业的不迅猛发展,新兴技术层数不穷,互联网通讯技术逐渐成为了各行各业不可替代的基础设施,越来越多的业务都是依靠互联网来得以实现.随着我国科技产业的飞速发展, ...
- 01 . Go之从零实现Web框架(框架雏形, 上下文Context,路由)
设计一个框架 大部分时候,我们需要实现一个 Web 应用,第一反应是应该使用哪个框架.不同的框架设计理念和提供的功能有很大的差别.比如 Python 语言的 django和flask,前者大而全,后者 ...
- java使用正则的例子
package com.accord.util; import java.util.ArrayList; import java.util.List; import java.util.regex.M ...
- python3 处理列表嵌套字典去重
def list_dict(dictlist): def function(date): return date['ip'] dictlist_new = [] for list_dict in di ...
- Redis核心原理-简单动态字符串SDS
SDS简介 Redis是C语言编写的,但没有使用c语言的字符串结构,而是自己实现了一套简单动态字符串 simple dynamic string 简称SDS,SDS兼容C语言的字符串类型,原理类似Ja ...
- rm: cannot remove `/tmp/localhost-mysql_cacti_stats.txt': Operation not permitted
[root@DBslave tmp]# chown zabbix.zabbix /tmp/localhost-mysql_cacti_stats.txt
- leetcode 730. 统计不同回文子序列(区间dp,字符串)
题目链接 https://leetcode-cn.com/problems/count-different-palindromic-subsequences/ 题意 给定一个字符串,判断这个字符串中所 ...