1、增删改查:
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'})]
2、独立消费
可以在不定义消费组的情况下进行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'})]]]
3、创建消费组
消费组消费模型:
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'):
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'}]
4、消费
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减少了
5、控制消息的长度
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'})]