redis5.0 新特性,官方介绍:https://redis.io/topics/streams-intro
参考:https://blog.csdn.net/shellquery/article/details/80562422
*
表示由服务器自动生成id,也可以自己生成,但后面加入的消息的ID要大于前面的消息ID;~
if r.exists('stream_1'):
r.delete('stream_1')
message1 = {'name': 'Flask', 'price': 10}
message1_id = r.xadd('stream_1', message1)
message2 = {'name': 'Django', 'price': 15}
message2_id = r.xadd('stream_1', message2)
print(f"message1_id:{message1_id}\nmessage2_id: {message2_id}")
# message1_id:b'1543370219288-0'
# message2_id: b'1543370219288-1'
print(f"stream len:{r.xlen('stream_1')}")
# 2
print(f"stream messages:{r.xrange('stream_1')}")
#stream messages:[(b'1543370219288-0', {b'name': b'Flask', b'price': b'10'}), (b'1543370219288-1', {b'name': b'Django', b'price': b'15'})]
r.xdel('stream_1', message1_id)
print(f"stream messages:{r.xrange('stream_1')}")
# stream messages:[(b'1543370219288-1', {b'name': b'Django', b'price': b'15'})]
可以在不定义消费组的情况下进行Stream消息的独立消费,当Stream没有新消息时,甚至可以阻塞等待。
最后
一条消息的id。
if r.exists('stream_2'):
r.delete('stream_2')
countrys = ['CN', 'US', 'UK', 'EU', 'JP']
i = 0
ret = []
for country in countrys:
ret.append(r.xadd('stream_2', {'country': country, 'id': i}))
print(r.xread({'stream_2': ret[1], 'stream_1': 0}, 2))
# [['stream_2', [(b'1543373133225-2', {b'country': b'UK', b'id': b'0'}), (b'1543373133226-0', {b'country': b'EU', b'id': b'0'})]], ['stream_1', [(b'1543373133224-1', {b'name': b'Django', b'price': b'15'})]]]
# 0-0 从头开始
print(r.xread({'stream_2': '0-0'}))
# [['stream_2', [(b'1543373133225-0', {b'country': b'CN', b'id': b'0'}), (b'1543373133225-1', {b'country': b'US', b'id': b'0'}), (b'1543373133225-2', {b'country': b'UK', b'id': b'0'}), (b'1543373133226-0', {b'country': b'EU', b'id': b'0'}), (b'1543373133226-1', {b'country': b'JP', b'id': b'0'})]]]
阻塞模式
# 从尾端读取数据, 阻塞时间60s
print(r.xread({'stream_2': '$'}, block=1000 * 60))
等待60s后返回
[]
在阻塞的过程中,另一界面
r.xadd('stream_2', {'name': 'jack', 'age': 11})
读取到stream_2尾部的消息:
[['stream_2', [(b'1543373840860-0', {b'name': b'jack', b'age': b'11'})]]]
消费组消费模型:
last_delivered_id
变量,默认$
从尾部开始消费,只接受新消息,当前Stream消息会全部忽略;if r.exists('stream_3'):
r.delete('stream_3')
r.xadd('stream_3', {'id': 0})
r.xadd('stream_3', {'id': 1})
# 从头部开始消费
r.xgroup_create('stream_3', 'group_2', id=0)
# 从尾部开始消费
r.xgroup_create('stream_3', 'group_1', id="$")
# 流的信息
print(r.xinfo_stream('stream_3'))
#{'length': 2, 'radix-tree-keys': 1, 'radix-tree-nodes': 2, 'groups': 2, 'last-generated-id': b'1543375608869-1', 'first-entry': (b'1543375608869-0', {b'id': b'0'}), 'last-entry': (b'1543375608869-1', {b'id': b'1'})}
# 消费组信息
print(r.xinfo_groups('stream_3'))
# pending 待处理消息
# [{'name': b'group_1', 'consumers': 0, 'pending': 0, 'last-delivered-id': b'1543375608869-1'}, {'name': b'group_2', 'consumers': 0, 'pending': 0, 'last-delivered-id': b'0-0'}]
# 移除消费组
r.xgroup_destroy('stream_3', 'group_2')
print(r.xinfo_groups('stream_3'))
# [{'name': b'group_1', 'consumers': 0, 'pending': 0, 'last-delivered-id': b'1543375608869-1'}]
Stream提供了xreadgroup指令可以进行消费组的组内消费,需要提供消费组名称、消费者名称和起始消息ID。它同xread一样,也可以阻塞等待新消息。读到新消息后,对应的消息ID就会进入消费者的PEL(正在处理的消息)结构里,客户端处理完毕后使用xack指令通知服务器,本条消息已经处理完毕,该消息ID就会从PEL中移除。如果消费者收到了消息处理完了但是没有回复ack,就会导致PEL列表不断增长,如果有很多消费组的话,那么这个PEL占用的内存就会放大。
if r.exists('stream_4'):
r.delete('stream_4')
r1 = r.xadd('stream_4', {'name': 'jack'})
r2 = r.xadd('stream_4', {'name': 'Tom'})
r3 = r.xadd('stream_4', {'name': 'Will'})
r.xgroup_create('stream_4', 'group_1', id=0)
# >号表示从当前消费组的last_delivered_id后面开始读
# 每当消费者读取一条消息,last_delivered_id变量就会前进
ret = r.xreadgroup('group_1', 'consumer_1', {'stream_4': ">"}, count=1)
print(ret)
# [['stream_4', [(b'1543455084777-0', {b'name': b'jack'})]]]
r.xreadgroup('group_1', 'consumer_1', {'stream_4': ">"}, count=2)
print(r.xinfo_consumers('stream_4', 'group_1'))
# idle空闲了多长时间ms没有读取消息了
# [{'name': b'consumer_1', 'pending': 3, 'idle': 1}]
print(r.xpending('stream_4', 'group_1'))
# {'pending': 3, 'min': b'1543455084777-0', 'max': b'1543455084777-2', 'consumers': [{'name': b'consumer_1', 'pending': 3}]}
# ack 2条消息
r.xack('stream_4', 'group_1', *[r1, r2])
print(r.xinfo_consumers('stream_4', 'group_1'))
# [{'name': b'consumer_1', 'pending': 1, 'idle': 1}] pending减少了
xadd 的maxlen
参数可以将消息控制在一定长度,但要设置approximate=False
;
xtrim(self, name, maxlen, approximate=True):同样可以将stream内的消息修剪为指定长度
if r.exists('stream_5'):
r.delete('stream_5')
for i in range(0, 10):
r.xadd('stream_5', {'id': i})
print(r.xlen('stream_5'))
# 10
r.xadd('stream_5', {'id': 10}, maxlen=5, approximate=False)
print(r.xlen('stream_5'))
# 5
r.xtrim('stream_5', maxlen=3, approximate=False)
print(r.xlen('stream_5'))
# 3
print(r.xrange('stream_5'))
# [(b'1543455600180-4', {b'id': b'8'}), (b'1543455600181-0', {b'id': b'9'}), (b'1543455600181-1', {b'id': b'10'})]