使用Redis构建简易社交网站(3)-状态与信息流
目的
本文目的:实现获取主页时间线和状态推送功能。(完整代码附在文章末尾)
相关知识
在我上一篇文章 《使用Redis构建简易社交网站(2)-处理用户关系》中提到了实现用户关注和取消关注功能。
那这篇文章将教会你掌握:1.redis基本命令,2.python基本命令。
redis基本命令
zadd:将成员加入到有序集合中,并确保其在正确的位置上。
conn = redis.Redis()
conn.zadd("testzset", "member2", 3)
conn.zadd("testzset", "member1", 2)
conn.zadd("testzset", "member3", 1)
执行后:
member3member1member2
执行结果:1,1,1
zrange:返回有序集合中指定区间内的成员。
conn = redis.Redis()
conn.zrange("testzset", 0, 1)
执行结果:['member3', 'member1']
zrevrange:按分值递减的顺序返回有序集合中指定区间内的成员。
conn = redis.Redis()
conn.zrevrange("testzset", 0, -1)
执行结果:['member2', 'member1', 'member3']
hgetall:返回哈希表中所有的域-值对。
conn = redis.Redis()
conn.hgetall("testhash")
执行结果:{'field1': '2'}
hget:从哈希中获取指定域的值。
conn = redis.Redis()
conn.hget("testhash", "field1")
执行结果:2
pipeline:将多条命令按照先后顺序放进一个队列中,一般配合execute一同使用,原子性(atomic)地执行队列里的命令。
conn = redis.Redis()
pipe = conn.pipeline(True) # 事务开始
pipe.incr("counter")
pipe.incr("counter")
pipe.incr("counter")
pipe.execute() # 事务执行
执行结果:[1, 2, 3],通过下标即可获取对应命令的执行结果。
python基本命令
使用格式化拼接字符串:
"My name is %s, I'm %i years old"%('educoder', 2)
执行结果:"My name is educoder, I'm 2 years old"
将字符串转换为浮点数:
float("1.23")
执行结果:1.23
实战例题
编写 get_home_timeline(uid) 函数,实现获得主页时间线的功能,具体参数与要求如下:
- 方法参数
uid为要获取主页时间线的用户编号; - 获取动态编号的实现:从存储用户主页时间线的有序集合
home:{uid}中按照分值递减的顺序取出所有成员; - 获取动态详情的实现:遍历动态编号,使用事务一次性获取每个动态编号对应动态详情哈希键
post:{pid}的所有域-值对; - 返回主页时间线的实现:返回事务执行的结果。
编写 post(uid, content) 函数,实现发布动态并将动态推送给粉丝的功能,具体参数与要求如下:
- 方法参数
uid为要发布动态的用户编号,content为要发布的动态内容; - 发布动态的实现:调用第一关中实现的
create_post方法,并接收返回的动态编号,若发布失败,则取消发布,返回None; - 获取发布时间的实现:从新发布的动态编号对应的动态详情哈希键
post:{pid}中获取posted域; - 更新个人主页的实现:将新发布的动态编号存储到个人主页有序集合键
profile:{uid}中,分值为转为浮点数后的发布时间; - 更新粉丝主页时间线的实现:遍历用户的粉丝列表
followers:{uid},将新发布的动态编号存储到每个粉丝的主页时间线的有序集合home:{follower_id}中,分值为转为浮点数后的发布时间; - 返回发布结果的实现:返回新发布的动态编号。
测试说明
测试输入:4;
预期输出:
用户 4 关注 用户 1
关注结果: True测试 post 方法...
创建动态: 1
创建动态: 2
用户 1 的动态列表: ['2', '1']
用户 4 的主页时间线动态编号: ['2', '1']测试 get_home_timeline 方法...
用户 4 的主页时间线: [{'content': 'NEW post from user 1!!!', 'uid': '1', 'user_name': 'test_user1', 'id': '2'}, {'content': 'This is the first post from user 1', 'uid': '1', 'user_name': 'test_user1', 'id': '1'}]
code.py
#code.py
#-*- coding:utf-8 -*-import re
import time
import redisconn = redis.Redis()# 获得主页时间线
def get_home_timeline(uid, page=1, count=30):# 请在下面完成要求的功能#********* Begin *********#post_ids = conn.zrevrange("home:%s"%(uid), 0, -1)pipe = conn.pipeline(True)for pid in post_ids:pipe.hgetall("post:%s"%(pid))return pipe.execute()#********* End *********## 发布动态并将动态推送给粉丝
def post(uid, content):# 请在下面完成要求的功能#********* Begin *********#pid = create_post(uid, content)if not pid:return Noneposted = conn.hget("post:%s"%(pid), "posted")conn.zadd("profile:%s"%(uid), pid, float(posted))followers = conn.zrange("followers:%s"%(uid), 0, -1)pipe = conn.pipeline(False)for follower in followers:pipe.zadd("home:%s"%(follower), pid, float(posted))pipe.execute()return pid#********* End *********## 关注用户
def follow(uid, other_uid):fkey1 = "following:%s"%(uid)fkey2 = "followers:%s"%(other_uid)if conn.zscore(fkey1, other_uid):return Nonenow = time.time()pipe = conn.pipeline(True)pipe.zadd(fkey1, other_uid, now)pipe.zadd(fkey2, uid, now)following, followers = pipe.execute()posts = conn.zrevrange("profile:%s"%(other_uid), 0, 100, withscores=True)if posts:pipe.zadd("home:%s"%(uid), **dict(posts))pipe.hincrby("user:%s"%(uid), 'following', int(following))pipe.hincrby("user:%s"%(other_uid), 'followers', int(followers))pipe.execute()return True# 取消关注
def unfollow(uid, other_uid):fkey1 = "following:%s"%(uid)fkey2 = "followers:%s"%(other_uid)if not conn.zscore(fkey1, other_uid):return Nonepipe = conn.pipeline(True)pipe.zrem(fkey1, other_uid)pipe.zrem(fkey2, uid)following, followers = pipe.execute()posts = conn.zrevrange("profile:%s"%(other_uid), 0, -1)if posts:pipe.zrem("home:%s"%(uid), *posts)pipe.hincrby("user:%s"%(uid), 'following', -int(following))pipe.hincrby("user:%s"%(other_uid), 'followers', -int(followers))pipe.execute()return True# 创建新用户
def create_user(login_name, real_name):login_name = login_name.lower()if conn.hget("users", login_name):return Noneuid = conn.incr("user:id")pipe = conn.pipeline(True)pipe.hset("users", login_name, uid)pipe.hmset("user:%i"%(uid), {'login_name': login_name,'id': uid,'real_name': real_name,'followers': 0,'following': 0,'posts': 0,'last_signup': time.time(),})pipe.execute()return uid# 为用户创建新动态
def create_post(uid, content):pipe = conn.pipeline(True)pipe.hget("user:%i"%(uid), 'login_name')pipe.incr("post:id")login_name, pid = pipe.execute()if not login_name:return Nonepipe.hmset("post:%i"%(pid), {'id': pid,'uid': uid,'content': content,'posted': time.time(),'user_name': login_name,})pipe.hincrby("user:%i"%(uid), 'posts')pipe.execute()return pid
read.py
#read.py
#-*- coding:utf-8 -*-import os
import sys
import time
import redis
import pprint
from code import *conn = redis.Redis()
retry_time = 0
while True:try:conn.ping()breakexcept redis.exceptions.ConnectionError:os.system("redis-server > /dev/null 2>&1 &")retry_time += 1if retry_time > 3:breakpipe = conn.pipeline(True)
pipe.delete("users", "user:id")
keys = (conn.keys("user:*") + conn.keys("followers:*") + conn.keys("following:*") +conn.keys("post:*") + conn.keys("profile:*") + conn.keys("home:*")
)
if keys:pipe.delete(*keys)
pipe.execute()# 创建测试数据
join_str = " "
for i in xrange(10):login_name = "test_user%i"%(i+1)real_name = join_str.join(login_name.split("_")).capitalize()create_user(login_name, real_name)uid = int(sys.stdin.readline().strip())print "用户 %i 关注 用户 1"%(uid)
f_result = follow(uid, 1)
print "关注结果: " + str(f_result)
printprint "测试 post 方法..."
content = "This is the first post from user 1"
pid = post(1, content)
print "创建动态: " + str(pid)
content = "NEW post from user 1!!!"
pid = post(1, content)
print "创建动态: " + str(pid)
my_profile = conn.zrevrange("profile:1", 0, -1)
print "用户 1 的动态列表: " + str(my_profile)
home_timeline = conn.zrevrange("home:%i"%(uid), 0, -1)
print "用户 %i 的主页时间线动态编号: "%(uid) + str(home_timeline)
printprint "测试 get_home_timeline 方法..."
my_home = get_home_timeline(uid)
for info in my_home:info.pop("posted", "404")
print "用户 %i 的主页时间线: "%(uid) + str(my_home)
相关文章:
使用Redis构建简易社交网站(3)-状态与信息流
目的 本文目的:实现获取主页时间线和状态推送功能。(完整代码附在文章末尾) 相关知识 在我上一篇文章 《使用Redis构建简易社交网站(2)-处理用户关系》中提到了实现用户关注和取消关注功能。 那这篇文章将教会你掌握:1&#x…...
Python,非二进制的霍夫曼编码
一般来说,霍夫曼编码是二进制的,但是非二进制的也可以。本文中,通过修改N,可以得到任意进制的霍夫曼编码。 非二进制编码的作用:例如,设计九键输入法,希望根据拼音的概率来编码,常用…...
详解—[C++数据结构]—红黑树
目录 一、红黑树的概念 编辑二、红黑树的性质 三、红黑树节点的定义 四、红黑树结构 五、红黑树的插入操作 5.1. 按照二叉搜索的树规则插入新节点 5.2、检测新节点插入后,红黑树的性质是否造到破坏 情况一: cur为红,p为红,g为黑&…...
甘草书店记:6# 2023年10月31日 星期二 「梦想从来不是一夜之间实现的」
甘草书店 今天收到甘草书店第二版装修设计平面图,与理想空间越来越近。 于我而言,每一次世俗意义上所谓的成功都不如文艺作品中表现的那样让人欢腾雀跃。当你用尽120分努力,达到了冲刺满分的实力时,得个优秀的成绩也并不意外。 …...
基于Java SSM车辆租赁管理系统
现代生活方式下,人们经常需要租赁车辆,比如婚庆、自驾游等,车辆租赁公司应运而生,车辆租赁管理系统就是借助计算机对车辆租赁情况进行全面管理。系统的主要管理对象及操作有: 车辆信息:包括车辆类型、车辆名…...
侯捷C++八部曲(一,面向对象)
头文件和类的声明 inline inline修饰函数,是给编译器的一个建议,到底是否为inline由编译器来决定,inline修饰的函数在使用时是做简单的替换,这样就避免了一些函数栈空间的使用,从能提升效率。从另一种角度看ÿ…...
《数据库系统概论》学习笔记——王珊 萨师煊
第一章 绪论 一、数据库系统概述 1.数据库的4个基本概念 (1)数据 描述事物的符号记录称为数据 (2)数据库 存放数据的仓库 (3)数据库管理系统 主要功能: (1)数据定…...
关于使用百度开发者平台处理语音朗读问题排查
错误信息:"convert_offline": false, "err_detail": "16: Open api characters limit reach 需要领取完 识别和合成都要有...
安全认证 | CISP和CISP-PTE的区别在哪里?
CISP和CISP-PTE的区别在哪里? 在国内安全信息认证体系中,虽然CISP认证与CISP-PTE认证都是中国信息安全测评中心负责颁发,均获得政府背景的认可,但二者还是有区别的。 今天就详细为大家介绍一下。 01 定义不同 ★ 注册信息安全专…...
Unity3D 导出的apk进行混淆加固、保护与优化原理(防止反编译)
目录 前言: 准备资料: 正文: 1:打包一个带有签名的apk 2:对包进行反编译 3:使用ipaguard来对程序进行加固 前言: 对于辛辛苦苦完成的apk程序被人轻易的反编译了,那就得不偿…...
C语言扫雷小游戏
以下是一个简单的C语言扫雷小游戏的示例代码: #include <stdio.h>#include <stdlib.h>#include <time.h>#define BOARD_SIZE 10#define NUM_MINES 10int main() { int board[BOARD_SIZE][BOARD_SIZE]; int num_flags, num_clicks; int …...
用取样思想一探AIX上进程性能瓶颈
本篇文章也是我在解决客户问题时的一些思路,希望对读者有用。 本文与GDB也与DBX(AIX上的调试工具)无关,只是用到了前文《GDB技巧》中的思想:取样思想 客户问题: 原始问题是磁盘被占满了,通过…...
分布式搜索引擎elasticsearch(二)
1.DSL查询文档 elasticsearch的查询依然是基于JSON风格的DSL来实现的。 1.1.DSL查询分类 Elasticsearch提供了基于JSON的DSL(Domain Specific Language)来定义查询。常见的查询类型包括: 查询所有:查询出所有数据,一般测试用。例如:match_all 全文检索(full text)查…...
Tecplot绘制涡结构(Q准则)
文章目录 目的步骤1步骤2步骤3步骤4步骤5步骤6结果 目的 Tecplot绘制涡结构(Q准则判别)并用温度进行染色 Q准则计算公式 步骤1 步骤2 步骤3 步骤4 步骤5 步骤6 结果...
Whisper
文章目录 使后感Paper Review个人觉得有趣的Log Mel spectrogram & STFT Trainingcross-attention输入cross-attention输出positional encoding数据 Decoding为什么可以有时间戳的信息 Test code 使后感 因为运用里需要考虑到时效和准确性,类似于YOLOÿ…...
Android系统分析
Android工程师进阶第八课 AMS、WMS和PMS 一、Binder通信 【Android Framework系列】第2章 Binder机制大全_android binder-CSDN博客 Android Binder机制浅谈以及使用Binder进行跨进程通信的俩种方式(AIDL以及直接利用Binder的transact方法实现)_bind…...
五、关闭三台虚拟机的防火墙和Selinux
目录 1、关闭每台虚拟机的防火墙 2、关闭每台虚拟机的Selinux 2.1 什么是SELinux...
【从零开始学习Redis | 第六篇】爆改Setnx实现分布式锁
前言: 在Java后端业务中, 如果我们开启了均衡负载模式,也就是多台服务器处理前端的请求,就会产生一个问题:多台服务器就会有多个JVM,多个JVM就会导致服务器集群下的并发问题。我们在这里提出的解决思路是把…...
Kubernetes学习笔记-Part.05 基础环境准备
目录 Part.01 Kubernets与docker Part.02 Docker版本 Part.03 Kubernetes原理 Part.04 资源规划 Part.05 基础环境准备 Part.06 Docker安装 Part.07 Harbor搭建 Part.08 K8s环境安装 Part.09 K8s集群构建 Part.10 容器回退 第五章 基础环境准备 5.1.SSH免密登录 在master01、…...
语义分割 DeepLab V1网络学习笔记 (附代码)
论文地址:https://arxiv.org/abs/1412.7062 代码地址:GitHub - TheLegendAli/DeepLab-Context 1.是什么? DeepLab V1是一种基于VGG模型的语义分割模型,它使用了空洞卷积和全连接条件随机(CRF)来提高分割…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...
3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...
MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...
uni-app学习笔记三十五--扩展组件的安装和使用
由于内置组件不能满足日常开发需要,uniapp官方也提供了众多的扩展组件供我们使用。由于不是内置组件,需要安装才能使用。 一、安装扩展插件 安装方法: 1.访问uniapp官方文档组件部分:组件使用的入门教程 | uni-app官网 点击左侧…...
密码学基础——SM4算法
博客主页:christine-rr-CSDN博客 专栏主页:密码学 📌 【今日更新】📌 对称密码算法——SM4 目录 一、国密SM系列算法概述 二、SM4算法 2.1算法背景 2.2算法特点 2.3 基本部件 2.3.1 S盒 2.3.2 非线性变换 编辑…...
