Redis+threading实现多线程消息队列

目录

Redis

列表

lpush左插入、rpush右插入、lrange查询集合

python 复制代码
127.0.0.1:6379> lpush list v1
(integer) 1
127.0.0.1:6379> lpush list v2
(integer) 2
127.0.0.1:6379> lpush list v3
(integer) 3
127.0.0.1:6379> LRANGE list 0 -1
1) "v3"
2) "v2"
3) "v1"
127.0.0.1:6379> LRANGE list 0 1
1) "v3"
2) "v2"
127.0.0.1:6379> LRANGE list 0 0
1) "v3"
127.0.0.1:6379> rpush list rv0
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "v3"
2) "v2"
3) "v1"
4) "rv0"

lpop左移除、rpop右移除

python 复制代码
127.0.0.1:6379> lrange list 0 -1
1) "v3"
2) "v2"
3) "v1"
4) "rv0"
127.0.0.1:6379> lpop list
"v3"
127.0.0.1:6379> lrange list 0 -1
1) "v2"
2) "v1"
3) "rv0"
127.0.0.1:6379> rpop list
"rv0"
127.0.0.1:6379> lrange list 0 -1
1) "v2"
2) "v1"

lindex下标查询、llen长度查询

python 复制代码
127.0.0.1:6379> lrange list 0 -1
1) "v4"
2) "v3"
3) "v2"
4) "v1"
127.0.0.1:6379> lindex list 1
"v3"
127.0.0.1:6379> lindex list 0
"v4"
127.0.0.1:6379> llen list
(integer) 4

blpop、brpop

BRPOP 是 Redis 的一个阻塞式列表弹出命令,用于从指定的一个或多个列表中弹出最后一个元素。它和BLPOP 不同之处在于它是从列表的尾部弹出元素,而不是从头部。

这种阻塞式弹出操作通常用于实现消息队列。如果列表为空,就会阻塞等待直到有消息可供处理。timeout: 阻塞超时时间,如果所有指定的列表都为空,命令将阻塞直到有元素可弹出或超时。

python 复制代码
# 连接到本地 Redis 服务器
r = redis.Redis(host='127.0.0.1', port=6379, decode_responses=True)
a = ['item1','item2','item3','item4','item5','item6']
# 将元素推入列表
r.rpush('my_queue',*a)
# 使用 blpop 弹出元素
result = r.blpop('my_queue', timeout=5)  # 设置超时时间为 5 秒
python 复制代码
['item1', 'item2', 'item3', 'item4', 'item5', 'item6']
['item2', 'item3', 'item4', 'item5', 'item6']

字符串

set、incr递增、decr递减

  • 虽然输入是int类型,但是set会自动转换为string
python 复制代码
r.set('my_queue', 5)

# 对 key 为 'my_queue' 的值执行递减操作
value = r.decr('my_queue')
# 获取递减后的值
print(f'New value: {value}')

new_value = r.incr('my_queue')

print(f'New value: {new_value}')
python 复制代码
New value: 4
New value: 5

可以看到就算是赋值也已经改变了my_queue键的值。

keys取键、get取值、delete

python 复制代码
# 将元素推入列表
r.setnx('my_queue:ddd:count',123)
r.setnx('my_queue:aaa:count',456)
r.setnx('my_queue',789)
r.setnx('my_queue',101)
r.set('my_queue:kkk:count',159)

print(r.keys('*'))
keys = r.keys('my_queue:*:count')
# 打印匹配的键列表
print(keys)
value = r.get(keys[0])
print(value)
r.delete('my_queue:ddd:count')
print(r.keys('*'))
python 复制代码
['my_queue:kkk:count', 'my_queue', 'my_queue:aaa:count', 'my_queue:ddd:count']
['my_queue:kkk:count', 'my_queue:aaa:count', 'my_queue:ddd:count']
159
['my_queue:kkk:count', 'my_queue', 'my_queue:aaa:count']

关于为什么delete了还能取到值(

delete只是删除了redis队列的键值对,keys是已经赋过值了所以不受影响。

python 复制代码
r.setnx(f'rtp_task:{1}:{123}:count',123)
r.setnx(f'rtp_task:{2}:{456}:count',456)
r.setnx(f'rtp_task:{3}:{789}:count',789)
r.setnx(f'rtp_task:{4}:{101}:count',101)
r.setnx(f'rtp_task:{5}:{159}:count',159)


keys = r.keys('rtp_task:*:count')
r.delete('rtp_task:1:123:count')
print(r.keys("*"))
print(keys)
python 复制代码
['rtp_task:4:101:count', 'rtp_task:2:456:count', 'rtp_task:3:789:count', 'rtp_task:5:159:count']
['rtp_task:4:101:count', 'rtp_task:2:456:count', 'rtp_task:3:789:count', 'rtp_task:1:123:count', 'rtp_task:5:159:count']

setnx

含义(setnx = SET if Not eXists):如果不存在,则set。

python 复制代码
r.setnx('my_queue:ddd:count',123)
print(r.get('my_queue:ddd:count'))
123
python 复制代码
r.setnx('my_queue:ddd:count',123)
r.setnx('my_queue:ddd:count',456)
print(r.get('my_queue:ddd:count'))
123

threading

Thread

创建线程

在创建线程时,传递参数需要是一个可迭代的对象,如果只有一个参数,需要在参数后面添加逗号,以表示它是一个元组而不是一个单一的值。

如果写成 args=(a,),它会被解释为一个包含单一元素的元组,而如果你写成 args=(a),它会被解释为 args=a,这样就不再是一个元组。

python 复制代码
a = "this is message"

def iptest(message):
    print(message)

t = threading.Thread(target=iptest, args=(a,))

start、join

python 复制代码
a = "this is message"

def iptest(message):
    print(message)

t = threading.Thread(target=iptest, args=(a,))

t.start()
python 复制代码
this is message

join方法的作用是确保thread子线程执行完毕后才能执行下一个线程。

没加join前:

python 复制代码
def iptest():
    print("message1\n")
    for i in range(10):
        # time.sleep() 函数推迟调用线程的运行,可通过参数secs指秒数,表示进程挂起的时间。
        time.sleep(0.1)
    print('message2')
def main():
    add_thread = threading.Thread(target=iptest, name="T2")
    add_thread.start()
    print("done")
    
if __name__ == '__main__':
    main()
python 复制代码
message1
done

message2

加join后

python 复制代码
message1

message2
done

消息队列

python 复制代码
import redis
import threading
import time
import json

# 连接到本地 Redis 服务器
r = redis.Redis(host='127.0.0.1', port=6379, decode_responses=True)

def producer(queue_name):
    # 生产者线程,模拟向队列中推送任务
    for i in range(5):
        message = {'task_id': i, 'data': f'Task {i}'}
        r.rpush(queue_name, json.dumps(message))
        time.sleep(1)

def consumer(queue_name, worker_id):
    while True:
        # 消费者线程,使用 blpop 从队列中阻塞获取任务
        message = r.blpop(queue_name, timeout=10)
        if message:
            task = json.loads(message[1])
            print(f"Worker {worker_id} processing task: {task} \n")
            # 模拟任务处理时间
            time.sleep(2)
            # 模拟任务处理完成后,更新任务计数
            r.decr(f'rtp_task:{task["task_id"]}:count')

            num_task = r.get(f'rtp_task:{task["task_id"]}:count')
            print(f'taskid{task["task_id"]},num_task{num_task}')

if __name__ == '__main__':
    # 设置初始任务计数
    for i in range(5):
        r.setnx(f'rtp_task:{i}:count', 3)

    # 创建一个生产者线程
    producer_thread = threading.Thread(target=producer, args=('product',))
    producer_thread.start()

    # 创建多个消费者线程
    num_consumers = 3
    consumer_threads = []
    for i in range(num_consumers):
        consumer_thread = threading.Thread(target=consumer, args=('product', i + 1))
        consumer_threads.append(consumer_thread)
        consumer_thread.start()

    # 等待生产者线程和消费者线程完成
    producer_thread.join()
    for consumer_thread in consumer_threads:
        consumer_thread.join()
python 复制代码
Worker 2 processing task: {'task_id': 0, 'data': 'Task 0'} 

Worker 1 processing task: {'task_id': 1, 'data': 'Task 1'} 

Worker 3 processing task: {'task_id': 2, 'data': 'Task 2'} 

taskid0,num_task2
taskid1,num_task2
Worker 2 processing task: {'task_id': 3, 'data': 'Task 3'} 

taskid2,num_task2
Worker 1 processing task: {'task_id': 4, 'data': 'Task 4'} 

taskid3,num_task2
taskid4,num_task2
相关推荐
傲笑风5 小时前
【openvino】tinybert基于openvino服务化部署(四)
人工智能·python·自然语言处理·nlp·bert·openvino
维基框架6 小时前
WIKI 知识库 v1.1.1 正式发布
人工智能·python
接着奏乐接着舞。9 小时前
【2026】73道Redis 常见面试题与参考答案
数据库·redis·后端·缓存
谢白羽10 小时前
SGLang的AWQ量化笔记
笔记·python·sglang
迷迭香yy10 小时前
基金档案数据工程实战从收入分析到持仓穿透的Python解析 IG50免费开源股票数据API接口
开发语言·python
清水白石00810 小时前
Python 类型设计深度解析:TypedDict 能否替代 dataclass?从 JSON 数据边界到 API 设计的最佳实践
java·python·json
️学习的小王11 小时前
Git项目提交忽略文件怎么做?以Python项目为例,详解.gitignore
git·python·elasticsearch
前端 贾公子11 小时前
第09章:上下文与记忆 (6)
开发语言·前端·python
evans在进步11 小时前
Java 常用设计模式入门:建造者、工厂、单例、外观与代理
java·python·设计模式
jayson.h12 小时前
PDF 合并+添加页码 相关库、类、函数
开发语言·前端·python