1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
| import redis
import json
import time
r = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)
# === 生产者 ===
def add_to_stream(stream: str, data: dict):
"""向 Stream 添加消息"""
msg_id = r.xadd(stream, data, maxlen=10000) # 限制最大长度
print(f"[✓] 消息 ID: {msg_id}")
return msg_id
# === 创建消费者组 ===
def create_consumer_group(stream: str, group: str):
"""创建消费者组"""
try:
r.xgroup_create(stream, group, id='0', mkstream=True)
print(f"[✓] 消费者组 {group} 已创建")
except redis.exceptions.ResponseError as e:
if 'BUSYGROUP' in str(e):
print(f"[*] 消费者组 {group} 已存在")
else:
raise
# === 消费者 ===
def stream_consumer(stream: str, group: str, consumer_name: str):
"""消费者组模式消费"""
while True:
# 读取新消息
messages = r.xreadgroup(
groupname=group,
consumername=consumer_name,
streams={stream: '>'}, # '>' 表示只读取新消息
count=10,
block=5000, # 阻塞 5 秒
)
if not messages:
continue
for stream_name, stream_messages in messages:
for msg_id, fields in stream_messages:
print(f"[{consumer_name}] 处理: {msg_id} -> {fields}")
# 处理完成后确认
r.xack(stream, group, msg_id)
# === 处理未确认消息(故障恢复) ===
def claim_pending_messages(stream: str, group: str, consumer: str,
min_idle_time: int = 60000):
"""认领超时未确认的消息"""
pending = r.xpending_range(stream, group, '-', '+', count=10)
for entry in pending:
msg_id = entry['message_id']
idle = entry['time_since_delivered']
if idle > min_idle_time:
claimed = r.xclaim(stream, group, consumer, min_idle_time, [msg_id])
print(f"[*] 已认领超时消息: {claimed}")
# 使用示例
if __name__ == '__main__':
STREAM = 'hook_results'
GROUP = 'analysis_group'
create_consumer_group(STREAM, GROUP)
# 生产者:写入 Hook 抓取结果
add_to_stream(STREAM, {
'package': 'com.example.app',
'method': 'javax.crypto.Cipher.doFinal',
'input': 'aGVsbG8=',
'output': 'ZW5jcnlwdGVk',
'timestamp': str(time.time()),
})
# 消费者:处理结果
stream_consumer(STREAM, GROUP, 'worker-1')
|