目录
一、先进先出任务队列
rpush
命令
rpush:将一个值插入到列表尾部,保证后插入的在最尾部。
conn = redis.Redis() conn.rpush("testlist", "end")
执行前:a b
执行后:a b end
blpop
命令
blpop:从列表头部阻塞式的弹出一个值,若列表中没有元素可共弹出时,则阻塞该命令直到超过timeout
或发现可弹出元素为止。
conn = redis.Redis() conn.blpop("alist", 5)
执行前:a b
执行后:b
执行结果:'alist' # 列表键名 'a' # 被弹出的值
如果是空列表,那么 blpop 命令将阻塞连接,直到等待超时,或有另一个客户端对列表执行 lpush 或 rpush 命令为止:
conn = redis.Redis()
conn.blpop("blist", 5)
等待2
秒后,另一个客户端执行了:
conn.lpush("blist", "waiting 2 sec and got it")
则执行结果为:'blist' 'waiting 2 sec and got it'
编程要求
在
Begin-End
区域编写add_task(task_name)
函数,实现将任务加入队列的功能,具体参数与要求如下:
- 方法参数
task_name
是要加入的任务名称;- 推入任务的实现:将新的任务从列表
task:list
的尾部插入。编写
pop_task()
函数,实现获取一个任务的功能,具体参数与要求如下:
- 获取任务的实现:从列表
task:list
的头部阻塞式的弹出一个值,最长等待10
秒,若超时仍未获取到,则重试该操作,直至弹出一个值;- 任务返回的实现:获取任务成功后,返回该值中弹出的元素值。
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import redis
conn = redis.Redis()
# 将任务加入队列
def add_task(task_name):
# 请在下面完成要求的功能
#********* Begin *********#
conn.rpush("task:list",task_name)
#********* End *********#
# 获取一个任务
def pop_task():
# 请在下面完成要求的功能
#********* Begin *********#
while True:
task = conn.blpop("task:list",10)
if not task:
continue
return task[1]
#********* End *********#
二、优先级任务队列
程序有时会需要让特定的操作优先于其他操作执行,使用多个队列可以最简便的实现优先级特性。
一般来说,将任务分为高、中、低三种优先级别,高优先级任务会在第一时间被执行,在执行完所有高优先级任务后才会执行中等优先级任务,依次类推。
本关任务:使用
Redis
构建一个区分优先级的任务队列。
rpush命令
将一个值插入到列表尾部,保证后插入的在最尾部。
conn = redis.Redis() conn.rpush("testlist", "end")
执行前:a b
执行后:a b end
blpop命令
从列表头部阻塞式的弹出一个值,给定多个列表时,按列表的先后顺序依次检查,弹出第一个非空列表的头元素。
conn = redis.Redis() conn.blpop("blist", "alist", 5)
执行前:# alist a b # blist c d
执行后:# alist a b # blist d
执行结果:'blist' # 列表键名 'c' # 被弹出的值
如果是空列表,那么 blpop 命令将阻塞连接,直到等待超时,或有另一个客户端对列表执行 lpush 或 rpush 命令为止:
conn = redis.Redis()
conn.blpop("clist", 5)
等待2
秒后,另一个客户端执行了:
conn.lpush("clist", "waiting 2 sec and got it")
则执行结果为:'clist' 'waiting 2 sec and got it'
zrevrange
返回有序集合中指定区间内的成员。其中成员的位置按分值递减(从大到小)排列。
conn = redis.Redis() conn.zadd("testzset", "member1", 3) conn.zadd("testzset", "member2", 1) conn.zadd("testzset", "member3", 2) conn.zrevrange("testzset", 0, -1)
执行结果:member1 member3 member2
zadd
将成员加入到有序集合中,并确保其在正确的位置上。
conn = redis.Redis() conn.zadd("testzset", "member2", 3) conn.zadd("testzset", "member1", 2) conn.zadd("testzset", "member3", 1)
执行后:member3 member1 member2
编程要求
在
Begin-End
区域编写add_task_list(list_name, priority)
函数,实现设置任务队列优先级的功能,具体参数与要求如下:
- 方法参数
list_name
是要加入的任务队列名字,priority
是要设置的优先权重,值越大则级别越高;- 设置队列优先级的实现:为了能按照优先权重排列任务队列,将任务队列加入到有序集合
task:priority
中,分值为priority
。编写
add_task(list_name, task_name)
函数,实现将任务加入队列的功能,具体参数与要求如下:
- 方法参数
list_name
是要加入的任务队列名字,task_name
是要加入的任务名称;- 推入任务的实现:将新的任务从指定的列表尾部插入。
编写
pop_task()
函数,实现获取一个任务的功能,具体参数与要求如下:
- 排序任务队列的实现:将有序集合
task:priority
中的所有成员按照分值递减的顺序排列;- 获取任务的实现:按照上述队列的顺序,从第一个非空列表的头部阻塞式的弹出一个值,最长等待
10
秒,若超时仍未获取到,则重试该操作,直至弹出一个值;- 任务返回的实现:获取任务成功后,返回该值中弹出的元素值。
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import redis
conn = redis.Redis()
# 新建任务队列/改变任务队列优先级
def add_task_list(list_name, priority):
# 请在下面完成要求的功能
#********* Begin *********#
conn.zadd("task:priority",list_name,priority)
#********* End *********#
# 将任务加入队列
def add_task(list_name, task_name):
# 请在下面完成要求的功能
#********* Begin *********#
conn.rpush(list_name,task_name)
#********* End *********#
# 获取一个任务
def pop_task():
# 请在下面完成要求的功能
#********* Begin *********#
queues = conn.zrevrange("task:priority",0,-1)
while True:
task = conn.blpop(queues,10)
if not task:
continue
return task[1]
#********* End *********#
三、定时任务队列
redis基本命令
zadd:将成员加入到有序集合中,并确保其在正确的位置上。
conn = redis.Redis() conn.zadd("testzset", "member2", 3) conn.zadd("testzset", "member1", 2) conn.zadd("testzset", "member3", 1)
执行后:member3 member1 member2
rpush:将一个值插入到列表尾部,保证后插入的在最尾部。
conn = redis.Redis() conn.rpush("testlist", "end")
执行前:a b
执行后:a b end
zrange:返回有序集合中指定区间内的成员。
conn = redis.Redis()
conn.zrange("testzset", 0, 1, withscores=True)
Redis
中的内容:
member3 1 member1 2 member2 3
执行结果:[('member3', 1.0), ('member1', 2.0)]
zrem:从有序集合中移除指定成员。
conn = redis.Redis() conn.zrem("testzset", "member1")
执行前:member3 member1 member2
执行后:member3member2
执行结果:1
python基本命令
返回当前时间的时间戳:
time.time()
使进程休眠 0.5
秒:
time.sleep(0.5)
编程要求
在
Begin-End
区域编写execute_later(task_name, delay=0)
函数,实现添加定时任务的功能,具体参数与要求如下:
- 方法参数
task_name
是要加入的任务名称,delay
是任务的延迟时间;- 添加定时任务的实现:若延迟时间大于
0
,则将任务加入到有序集合task:delayed
中,分值为任务的执行时间,等于当前时间戳加上延迟时间;- 添加普通任务的实现:若延迟时间小于等于
0
,则将其插入到列表task:list
的尾部。编写
pop_task()
函数,实现转移可执行任务的功能,具体参数与要求如下:
- 获取队列中第一个任务的实现:不断尝试获取有序集合
task:delayed
中按分值递增顺序的第一个元素;- 判断该任务是否可执行的实现:若未取到任务或者任务的执行时间未到,则休眠
0.01
秒,然后继续尝试获取第一个任务;- 任务转移的实现:从有序集合
task:delayed
中移除该任务,成功后,将该任务插入到列表task:list
的尾部。
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import time
import redis
conn = redis.Redis()
# 添加定时任务
def execute_later(task_name, delay=0):
# 请在下面完成要求的功能
#********* Begin *********#
if delay > 0:
conn.zadd("task:delayed",task_name,time.time()+delay)
else:
conn.rpush("task:list",task_name)
#********* End *********#
# 转移可执行任务
def pop_task():
# 请在下面完成要求的功能
#********* Begin *********#
while True:
task = conn.zrange("task:delayed",0,0,withscores=True)
if not task or task[0][1] > time.time():
time.sleep(0.01)
continue
task = task[0][0]
if conn.zrem("task:delayed",task):
conn.rpush("task:list",task)
#********* End *********#
四、创建用户与动态
redis基本命令
hget:从哈希中获取指定域的值。
conn = redis.Redis() conn.hget("testhash", "field1")
执行结果:2
incr:将 key
中储存的数字值增一。
conn = redis.Redis() conn.incr("testcount")
执行前:不存在 testcount 键。
执行后:testcount 1
hset:将哈希中域 field
的值设为 value
。
conn = redis.Redis() conn.hset("testhash", "field1", 2) conn.hset("testhash", "field2", 4)
执行前:{'field1': '1'}
执行后:{'field1': '2', 'field2': '4'}
hmset:同时将多个域-值对设置到哈希表中。
conn = redis.Redis() conn.hmset("test_hash", { 'id': 1, 'name': 'educoder', 'age': 2, })
执行后:{'age': '2', 'id': '1', 'name': 'educoder'}
hincrby:为哈希中指定域的值增加增量 increment
,用于统计。
conn = redis.Redis() conn.hincrby("testhash", "field1", 1)
执行前:{'field1': '1'}
执行后:{'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基本命令
将字符串全小写化:
'Hello, Educoder'.lower()
执行结果:'hello, educoder'
返回当前时间的时间戳。
time.time()
使用格式化拼接字符串:
"My name is %s, I'm %i years old"%('educoder', 2)
执行结果:"My name is educoder, I'm 2 years old"
编程要求
在
Begin-End
区域编写create_user(login_name, real_name)
函数,实现创建新用户的功能,具体参数与要求如下:
- 方法参数
login_name
为用户登录名,real_name
为用户真名;- 用户登录名预处理的实现:将用户登录名转换成全小写格式;
- 重名检测的实现:查询哈希键
users
中是否存在与用户登录名同名的域,若存在,则不允许重新创建该用户,返回None
;- 分配用户编号的实现:对计数器
user:id
递增1
,并将递增后的值作为新用户的编号;- 存储用户信息的实现:使用事务一次性提交:
- 存储登录名的实现:将用户登录名记录到哈希键
users
当中,值为该用户编号;- 存储详情的实现:按照如下示意将用户信息存储到哈希键
user:{id}
中:
- 返回创建结果的实现:返回新创建用户的编号。
编写
create_post(uid, content)
函数,实现创建新动态的功能,具体参数与要求如下:
- 方法参数
uid
为发布动态的用户编号,content
为要发布的动态内容;- 用户合法性检测的实现:查找用户编号对应详情信息哈希
user:{uid}
是否存在login_name
域,若存在,则记录,若不存在,则不允许创建新动态,返回None
;- 分配动态编号的实现:对计数器
post:id
递增1
,并将递增后的值作为新动态的编号;- 存储动态信息的实现:按照如下示意将动态信息存储到哈希键
post:{id}
中:
- 更新用户动态数的实现:为该用户编号对应的详情信息哈希
user:{uid}
中的posts
域的值加1
;- 返回创建结果的实现:返回新创建动态的编号。
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import re
import time
import redis
conn = redis.Redis()
# 创建新用户
def create_user(login_name, real_name):
# 请在下面完成要求的功能
#********* Begin *********#
login_name = login_name.lower()
if conn.hget("users",login_name):
return None
uid = 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
#********* End *********#
# 为用户创建新动态
def create_post(uid, content):
# 请在下面完成要求的功能
#********* Begin *********#
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 None
pipe.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
#********* End *********#
五、处理用户关系
redis基本命令
zscore:返回有序集合中指定成员的分值。
conn = redis.Redis()
conn.zscore("testzset", "member1")
conn.zscore("testzset", "not_exists_member")
testzset
内容如下:
执行结果:
100.0
None
zadd:将成员加入到有序集合中,并确保其在正确的位置上。
conn = redis.Redis() conn.zadd("testzset", "member2", 3) conn.zadd("testzset", "member1", 2) conn.zadd("testzset", "member3", 1)
执行后:member3 member1 member2
执行结果:1,1,1
hincrby:为哈希中指定域的值增加增量 increment
,用于统计。
conn = redis.Redis() conn.hincrby("testhash", "field1", 1)
执行前:'field1': '1'}
执行后:{'field1': '2'}
zrem:从有序集合中移除指定成员。
conn = redis.Redis() conn.zrem("testzset", "member1")
执行前:member3 member1 member2
执行后:member3 member2
执行结果:1
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"
返回当前时间的时间戳。
time.time()
将字符串转换为整型数据:
int("1")
执行结果:1
取一个数的相反数:
a = 1 b = -a print b
执行结果:-1
编程要求
在
Begin-End
区域编写follow(uid, other_uid)
函数,实现关注用户的功能,具体参数与要求如下:
- 方法参数
uid
为当前用户编号,other_uid
为被关注的用户编号;- 避免重复关注的实现:如果被关注的用户编号已经在当前用户的关注列表
following:{uid}
中,则不重复关注,直接返回None
;- 建立关注关系的实现:使用事务一次性提交:
- 将被关注的用户编号加入到当前用户的关注列表
following:{uid}
中,分值为当前时间戳。- 将当前用户编号加入到被关注用户的粉丝列表中
followers:{other_uid}
,分值为当前时间戳。- 修改统计数据的实现:若关系建立成功,则使用事务一次性提交:
- 将当前用户详情
user:{uid}
中的关注数following
加1
- 将被关注用户详情
user:{other_uid}
中的粉丝数followers
加1
- 返回执行结果的实现:返回
True
编写
unfollow(uid, other_uid)
函数,实现取消关注的功能,具体参数与要求如下:
- 方法参数
uid
为当前用户编号,other_uid
为被取消关注的用户编号;- 避免重复取消关注的实现:如果被关注的用户编号已经不在当前用户的关注列表
following:{uid}
中,则不重复取消关注,直接返回None
;- 删除关注关系的实现:使用事务一次性提交:
- 从当前用户的关注列表
following:{uid}
中移除被取消关注用户编号。- 从被取消关注用户的粉丝列表中
followers:{other_uid}
移除当前用户编号。- 修改统计数据的实现:若关系删除成功,则使用事务一次性提交:
- 将当前用户详情
user:{uid}
中的关注数following
减1
- 将被取消关注用户详情
user:{other_uid}
中的粉丝数followers
减1
- 返回执行结果的实现:返回
True
注意: 关注列表和粉丝列表均为有序集合,存储成员时,分值均为当前时间戳; 用户详情为上一关中创建的哈希结构。
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import re
import time
import redis
conn = redis.Redis()
# 关注用户
def follow(uid, other_uid):
# 请在下面完成要求的功能
#********* Begin *********#
fkey1 = "following:%s"%(uid)
fkey2 = "followers:%s"%(other_uid)
if conn.zscore(fkey1,other_uid):
return None
now = time.time()
pipe = conn.pipeline(True)
pipe.zadd(fkey1,other_uid,now)
pipe.zadd(fkey2,uid,now)
following,followers = pipe.execute()
pipe.hincrby("user:%s"%(uid),'following',int(following))
pipe.hincrby("user:%s"%(other_uid),'followers',int(followers))
pipe.execute()
return True
#********* End *********#
# 取消关注
def unfollow(uid, other_uid):
# 请在下面完成要求的功能
#********* Begin *********#
fkey1 = "following:%s"%(uid)
fkey2 = "followers:%s"%(other_uid)
if not conn.zscore(fkey1, other_uid):
return None
pipe = conn.pipeline(True)
pipe.zrem(fkey1, other_uid)
pipe.zrem(fkey2, uid)
following, followers = pipe.execute()
pipe.hincrby("user:%s"%(uid), 'following', -int(following))
pipe.hincrby("user:%s"%(other_uid), 'followers', -int(followers))
pipe.execute()
return True
#********* End *********#
# 创建新用户
def create_user(login_name, real_name):
login_name = login_name.lower()
if conn.hget("users", login_name):
return None
uid = 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 None
pipe.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
六、状态与信息流
redis基本命令
zadd:将成员加入到有序集合中,并确保其在正确的位置上。
conn = redis.Redis() conn.zadd("testzset", "member2", 3) conn.zadd("testzset", "member1", 2) conn.zadd("testzset", "member3", 1)
执行后:member3 member1 member2
执行结果: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
编程要求
在
Begin-End
区域编写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}
中,分值为转为浮点数后的发布时间;- 返回发布结果的实现:返回新发布的动态编号。
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import re
import time
import redis
conn = redis.Redis()
# 获得主页时间线
def get_home_timeline(uid):
# 请在下面完成要求的功能
#********* 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 None
posted = 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 None
now = 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 None
pipe = 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 None
uid = 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 None
pipe.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
构建任务队列_文章投票网站----完整代码展示
# _*_ coding : utf-8 _*_
import time
ONE_WEEK_IN_SECONDS = 7*24*60*60
def article_vote(r, user_id, article_id):
# time.time()获取当前时间,减去最大时效与文章发布时间比较
cutoff = time.time() - ONE_WEEK_IN_SECONDS
if r.zscore("time",article_id) < cutoff:
return False
if r.sadd('voted:' + article_id, user_id):
# 创建成功会返回1,否则返回0
r.zincrby("score", 1, article_id)
# 这个不能搞反python3中是zincrby(key,score,member)
return True
return False
# 创建文章数据
# 把文章数据存储到redis中。setps:创建文章id;文章作者加入到改文章已投票名单中;信息to_redis
# 将article发布time和init_vote加入到time和score两个zset中
def post_article(r, user, title, link):
# 创建新的文章id,整数计数器对article键执行自增
# 如果键不存在,article的值会被初始化为0,然后执行自增命令
article_id = str(r.incr("article"))
voted = "voted:" + article_id
r.sadd(voted, user)
# 为键设置过期时间,投票超一周到期,到期后数据已投票用户名单用处不大删除减少redis内存消耗
# r.expire(key,seconds)
r.expire(voted, ONE_WEEK_IN_SECONDS)
now = time.time()
article = "article:" + article_id
r.hmset(article,{
'title':title,
'link':link,
'poster':user
})
# 初始化投票数和创建时间,分数在前面
r.zadd("score",{article_id:1})
r.zadd("time",{article_id:now})
return article_id
# 获取排序为start到end的文章id,根据id得到文章数据,id作为文章的键
def get_articles(r, start, end, order='score'):
articles = []
# zrevrange返回有序集合内指定区间的成员
# r.zrevrange(key,start,stop):start开始的数组下标,stop结束的数组下标
ids = r.zrevrange(order, start, end)
for id in ids:
# 获取文章的全部数据关卡里有问题,键为id的hash键是没有的,应该是article:id键
article_data = r.hgetall('article:'+id)
article_data['id'] = id
articles.append(article_data)
return articles
# 清空
def to_del(r):
to_del = (r.keys('time') + r.keys('voted:*') + r.keys('score') + r.keys('article*'))
r.delete(*to_del)
def insert_data(r):
try:
user,title,link = input("请输入文章作者、标题和链接(空格隔开)").split()
except:
print("请重新输入内容:")
user = input("文章作者:")
title = input("标题:")
link = input("链接:")
article_id = str(post_article(r, user, title, link))
print("我们用id:{}发布了一篇新文章".format(article_id))
print('内容为:',end='')
# r.hdel("article:" + article_id, 'time')
hash_article = r.hgetall("article:" + article_id)
print(hash_article)
article_vote(r, user, article_id)
print("这篇文章现在的票数为:",end='')
v_nums = len(r.smembers('voted:'+article_id))
print(v_nums)
print("目前文章的票数由高到低的排序是:")
articles = get_articles(r,0,-1)
print(articles)
import redis
import warnings
warnings.filterwarnings("ignore")
if __name__ == '__main__':
pool = redis.ConnectionPool(host="localhost",port=6379,decode_responses=True)
r = redis.Redis(connection_pool=pool)
print("创建文章请输入:1")
print("投票请输入:2")
print("删除已有数据请输入:3")
print("查看文章票数排名请输入:4")
print("操作结束请输入:任意字符")
while True:
try:
operation = eval(input("请输入需要执行的功能:"))
if operation == 1:
insert_data(r)
elif operation == 2:
try:
user_id,article_id = input("请输入用户id、文章id:").split()
except:
print("输入格式错误,请重新操作")
continue
flag = article_vote(r,user_id,article_id)
if flag:
print("投票成功")
# v_nums = len(r.smembers('voted:'+article_id))#得到集合的成员数
v_nums = int(r.zscore('score',article_id))
print("这篇文章现在的票数为:",v_nums)
else:
print("投票失败")
elif operation == 3:
try:
to_del(r)
print("数据已清除")
except:
print("数据清除失败")
elif operation == 4:
print("目前文章的票数由高到低的排序是:")
articles = get_articles(r, 0, -1)
print(articles)
else:
print("操作结束")
break
except:
print("操作结束")
break
构建简单的社交网站----完整代码展示
#!/usr/bin/env python
# coding: utf-8
# redis client-server mode使用pipeline的好处,减少io次数,
# 可以执行多个命令后,一次性返回所有结果,大大减少网络上的消耗
# users 存储login_name 值为uid 密码不存数据库里
import redis
import time
import warnings
warnings.filterwarnings("ignore")
# 创建用户
def create_user(conn, login_name, real_name):
login_name = login_name.lower() # 转化为小写
if not conn.hexists("users", login_name):
pipe = conn.pipeline(True)
userid = conn.incr("user:id") # 获得用户id
conn.hset("users", login_name, userid) # 加入用户列表
conn.hmset(f"user:{userid}", { # 用户信息
"login_name": login_name,
"id": userid,
"real_name": real_name,
"followers": 0,
"following": 0,
"posts": 0,
"last_signup": time.time()
})
pipe.execute()
print("创建用户成功")
return userid
print(f"已存在用户{login_name},创建用户失败") # 如果存在该怎么做?加循环
return None
# 查看全部用户
def show_users(r):
try:#格式化后没有user:id,r.get('user:id')返回空,None
id_count = eval(r.get('user:id'))
except:
id_count = 0
print(f"一共有{id_count}名用户")
if id_count: #等于0无用户
for i in range(1,id_count+1):
print("-"*15,f"第{i}个用户的用户信息","-"*15)
show_user(r,i,'id')
print("-"*50)
# 查看用户信息 # 可以根据用户id或用户名login_name查看用户信息
def show_user(r,var,flag='login_name'):
if flag == 'id':
# hexists()判断字段是否存在
if not r.exists(f'user:{var}'): # 用户信息不存在
print("没有此用户!")
return
data = r.hvals(f"user:{var}")
if flag == 'login_name':
if not r.hexists('users',var):
print("没有此用户!")
return
uid = r.hget('users',var)
data = r.hvals(f'user:{uid}')
print("用户登录名:" + data[0])
print("用户id:" + data[1])
print("用户真名:" + data[2])
print("用户粉丝数:" + data[3])
print("用户关注数:" + data[4])
print("用户动态数:" + data[5])
# time.strftime() 格式化时间,返回以可读字符串表示的当地时间
# time.localtime()默认时间为本地的当前时间的,1970-1-1 08:00:00以来经过的秒数
tm = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(float(data[6])))
print(f"用户最后登录时间:{tm}")
def user_exists(r, uid):
id_max = eval(r.get('user:id')) # 是数字在范围内且是整数存在,否则不存在
try:
if isinstance(uid, str): # isinstance()函数来判断uid是否是str类型
uid = eval(uid)
if (uid > 0) and (uid <= id_max) and isinstance(uid, int):
return True
else:
return False
except:
return False
# 关注用户
def follow(r, uid, other_uid):
if r.zscore(f'following:{uid}', other_uid):
print('已关注该用户,请勿重复关注')
return
if not user_exists(r, other_uid):
print("不存在该用户,取关失败")
return
now = time.time()
pipe = r.pipeline(True) # 事务开始
pipe.zadd(f'following:{uid}', {other_uid: now})
pipe.zadd(f'followers:{other_uid}', {uid: now})
following, followers = pipe.execute() # 事务执行
posts = r.zrevrange(f'profile:{other_uid}', 0, 100,withscores=True)
# 拉取other_uid的前100条动态(不满100就是所有),[(postid:time),...]
if posts:
pipe.zadd(f"home:{uid}", dict(posts))
# 将关注的用户的动态加入到用户的主页时间线中
pipe.hincrby(f'user:{uid}', 'following', int(following))
pipe.hincrby(f'user:{other_uid}', 'followers', int(followers))
pipe.execute()
print("关注成功")
return True
# 取消关注用户
def unfollow(r, uid, other_uid):
if not user_exists(r, other_uid):
print("不存在该用户,取关失败")
return
if not r.zscore(f'following:{uid}', other_uid):
print("已取消关注该用户")
return
pipe = r.pipeline(True) # 开始事务
pipe.zrem(f'following:{uid}', other_uid)
pipe.zrem(f'followers:{other_uid}', uid)
following, followers = pipe.execute()
posts = r.zrevrange(f'profile:{other_uid}', 0, -1)
# 获取所有与other_uid有关的动态,只获取动态编号
if posts:
pipe.zrem(f'home:{uid}', *posts)
# 删除uid主页时间线中other_uid的动态线 #带星号参数不传参时默认空元组
pipe.hincrby(f'user:{uid}', 'following', -int(following))
pipe.hincrby(f'user:{other_uid}', 'followers', -int(followers))
pipe.execute()
print("取关成功")
return True
# 查看关注列表
def show_followings(r, uid):
if not eval(r.hget(f'user:{uid}', 'following')): # 判断是否为空
print("关注列表为空")
return
print("-" * 10, "关注列表", '-' * 10)
for id in r.zrange(f'following:{uid}', 0, -1): # other_uid
show_user(r, id, 'id')
print('-' * 12, 'end', '-' * 13)
# 查看粉丝列表
def show_followers(r, uid):
if not eval(r.hget(f'user:{uid}', 'followers')): # 判断粉丝列表是否为空
print("粉丝列表为空")
return
print("-" * 10, "粉丝列表", '-' * 10)
for id in r.zrange(f'followers:{uid}', 0, -1): # other_uid
show_user(r, id, 'id')
print('-' * 12, 'end', '-' * 13)
# 创建动态
def create_post(r, uid, content):
if 'login_name' not in r.hkeys(f'user:{uid}'):
return None # 如果不存在用户信息
login_name = r.hget(f'user:{uid}', 'login_name')
# 自增id,默认为0
pipe = r.pipeline(True)
# 这里不用直接postid=pipe.incr("post:id"),
# postid接受的是Pipeline<ConnectionPool<Connection<host=localhost,port=6379,db=0>>>
postid = r.incr("post:id")
pipe.hmset(f"post:{postid}", {
"id": postid,
"uid": uid,
"content": content,
"posted": time.time(),
"user_name": login_name
})
pipe.hincrby(f"user:{uid}", "posts", 1)
pipe.execute()
print("发布动态成功")
return postid
# 发布动态并将动态推送给粉丝
def post(r, uid, content):
pid = create_post(r, uid, content) # 获得动态id,int类型
if pid is None:
print("动态发布失败,请重新检查输入信息")
return None
tm = r.hget(f'post:{pid}', "posted") # 获取动态创建时间
r.zadd(f'profile:{uid}', {pid: float(tm)})
fids = r.zrange(f'followers:{uid}', 0, -1) # 粉丝列表
for id in fids:
r.zadd(f'home:{id}', {pid: float(time.time())}) # 加入到用户的主页时间线中
print("动态推送成功")
return pid
# 删除动态
def del_post(r, uid, pid):
if r.exists(f'post:{pid}'):
r.delete(f'post:{pid}')
r.hincrby(f'user:{uid}', 'posts', -1)
# 删除动态时间线中的该动态信息
r.zrem(f'profile:{uid}', pid)
# 删除粉丝主页中的该动态
fids = r.zrange(f'followers:{uid}', 0, -1)
for id in fids:
r.delete(f'home:{id}') # 从用户主页线中删除
print("动态删除成功")
return True
else:
print("动态删除失败")
return None
# 删除用户 #用户注销了,有关信息删除掉
def del_user(r, uid):
if r.exists(f"user:{uid}"):
# 删除动态
for i in r.zrange(f"profile:{uid}", 0, -1):
del_post(r, uid, i)
login_name = r.hget(f'user:{uid}', 'login_name')
r.delete(f"user:{uid}") # 删除用户详细信息
r.hdel('users', login_name) # 删除用户信息
print("删除用户成功")
return True
print("没有找到此用户,删除失败")
return None
# 获取主页时间线
def get_home_timeline(r, uid):
if not r.exists(f'home:{uid}'):
print(f"用户{uid}的主页线为空")
return
desc_pids = r.zrevrange(f'home:{uid}', 0, -1) # 主页线动态,最新动态在前面
pipe = r.pipeline(True) # 开启事务
for pid in desc_pids:
pipe.hgetall(f'post:{pid}')
uid_home = pipe.execute()
print(f'用户{uid}的主页时间线:{uid_home}')
# 删除所有有关数据---格式化
def init(r):
pipe = r.pipeline(True)
pipe.delete("users")
keys = (
r.keys("user:*") + r.keys("followers:*") + r.keys("following:*") +
r.keys("post:*") + r.keys("profile:*") + r.keys("home:*")
)
if keys:
pipe.delete(*keys)
pipe.execute()
def main():
pool = redis.ConnectionPool(host='localhost', port=6379, decode_responses=True)
r = redis.Redis(connection_pool=pool)
function = ['创建用户', '查看全部用户信息', '查看用户信息', '查看关注列表', '查看粉丝列表', '关注', '取关', '发布动态', '删除动态', '获取主页时间线', '删除用户', '格式化']
print(f"暂时拥有的功能:{function}")
print("输入拥有功能以外的参数信息则退出操作")
while True:
try:
operation = input("请输入需要执行的操作:")
if operation == "创建用户":
login_name = input("用户名:")
real_name = input("真实姓名:")
create_user(r, login_name, real_name)
elif operation == "查看全部用户信息":
show_users(r)
elif operation == "查看用户信息":
flag = input("查询方式:id/login_name:")
var = ""
if flag == 'id':
var = input("id:")
elif flag == 'login_name':
var = input("login_name:")
show_user(r, var, flag)
elif operation == "查看关注列表":
uid = input("用户id:")
show_followings(r, uid)
elif operation == "查看粉丝列表":
uid = input("用户id:")
show_followers(r, uid)
elif operation == "关注":
uid = input("用户id:")
other_uid = input("要关注的用户的id:")
follow(r, uid, other_uid)
elif operation == "取关":
uid = input("用户id:")
other_uid = input("要取关的用户的id:")
unfollow(r, uid, other_uid)
elif operation == "发布动态":
uid = input("用户id:")
content = input("要发布的内容:")
post(r, uid, content)
elif operation == "删除动态":
uid = input("用户id:")
pid = input("动态编号:")
del_post(r, uid, pid)
elif operation == "获取主页时间线":
uid = input("用户id:")
get_home_timeline(r, uid)
elif operation == "删除用户":
uid = input("删除用户id:")
del_user(r, uid)
elif operation == "格式化":
init(r)
print("格式化成功")
else:
print("操作结束")
break
except:
print("操作结束")
break
if __name__ == '__main__':
main()