Redis-Python交互:流Stream

Redis

  • 增删改查:
  • 独立消费
  • 创建消费组
    • 消费
    • 控制消息的长度

redis5.0 新特性,官方介绍:https://redis.io/topics/streams-intro
参考:https://blog.csdn.net/shellquery/article/details/80562422

增删改查:

  • xadd(self, name, fields, id=’*’, maxlen=None, approximate=True):追加消息
    • name:流的名字;
    • fields:追加的消息key-value,字典表形式;
    • id:*表示由服务器自动生成id,也可以自己生成,但后面加入的消息的ID要大于前面的消息ID;
    • maxlen:截断超出此大小的旧有的stream成员;
    • approximate:实际流长度可能略大于maxlen,
  • xdel(self, name, *ids):删除消息,这里的删除仅仅是设置了标志位,不影响消息总长度
  • xlen(self, name):Stream内消息的长度
  • xrange(self, name, min=’-’, max=’+’, count=None):获取消息列表,会自动过滤已经删除的消息
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没有新消息时,甚至可以阻塞等待。

  • xread(self, streams, count=None, block=None): 字典表形式{流的名称:消息id},其中id时已被读取的最后一条消息的id。
    • streams: 要进行读取的流;
    • count:读取的数据数量;
    • block:阻塞的时间,毫秒;
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'})]]]

创建消费组

消费组消费模型:

  • xgroup_create(self, name, groupname, id=’$’, mkstream=False):在stream上创建新的消费组;
    • name:stream的名字;
    • groupname:消费组名称;
    • id:起始消息ID,用来初始化last_delivered_id变量,默认$从尾部开始消费,只接受新消息,当前Stream消息会全部忽略;
  • xgroup_destroy(self, name, groupname):删除消费组;
  • xinfo_groups(self, name):stream的消费组信息;
  • xinfo_stream(self, name):流的信息;
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占用的内存就会放大。

  • xgroup_delconsumer(self, name, groupname, consumername):从消费组移除特定消费者
  • xinfo_consumers(self, name, groupname):消费者信息;
  • xreadgroup(self, groupname, consumername, streams, count=None, block=None):消费组的组内消费
  • xack(self, name, groupname, *ids): 正确处理消息后的响应
  • xpending(self, name, groupname):消费组内待处理的消息的信息
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'})]

你可能感兴趣的:(学习笔记)