# 使用 Redis 做为进程间通信解决方案

Redis 常用于缓存、消息队列、实时数据分析等高性能、低延迟的应用场景。

它有着使用简单、体积小、性能强、多平台兼容的特点。作为一个内存数据存储,Redis 支持多种数据结构,如字符串、哈希、列表、集合、有序集合等,能够满足不同场景下的数据存储需求。Redis 提供了高效的读写性能,能够在高并发环境下处理大量的请求。

Redis 的设计强调简洁性和高效性,支持持久化机制,可以将数据持久化到磁盘,确保系统重启时数据不丢失。此外,Redis 支持高可用性部署和分布式集群,能够在大规模系统中提供一致性和可靠性。

Redis 是跨平台的,支持 Linux、macOS、Windows 等操作系统,并且有丰富的客户端库,几乎支持所有主流编程语言,方便开发者进行集成。由于其高性能和低资源消耗,Redis 已成为全球范围内最受欢迎的开源数据存储系统之一。

# 一、Windows 环境下 Redis 的安装

Windows 版本 Redis GitHub:https://github.com/tporadowski/redis (opens new window)

下载 msi 安装程序,或者 zip 压缩包,mis 安装程序会自动配置好环境变量,zip 需要手动配置,如果不配置环境变量那么后续的操作指令必须进入 zip 解压后的文件夹操作才行。

在命令行环境直接运行 Redis

redis-server redis.windows.conf

将 Redis 部署为 Windows 服务

redis-server --service-install redis.windows.conf

启动 Redis 服务

redis-server --service-start

停止 Redis 服务

redis-server --service-stop

卸载 Redis 服务

redis-server --service-uninstall

# 二、Redis 客户端基础操作

Redis 常用操作可以包含为以下几类:

  1. 键操作
    • SET:设置键的值;
    • GET:获取键的值;
    • DEL:删除键;
    • EXISTS:检查键是否存在;
    • KEYS:查找符合模式的键;
    • TTL:查看键的剩余过期时间;
    • EXPIRE:设置键的过期时间;
  2. 字符串操作
    • INCR / DECR:对键的数值进行递增或递减;
    • APPEND:将值追加到字符串后面;
  3. 哈希操作
    • HSET:设置哈希表中的字段值;
    • HGET:获取哈希表中的字段值;
    • HGETALL:获取哈希表中的所有字段和值;
    • HDEL:删除哈希表中的字段;
  4. 列表操作
    • LPUSH / RPUSH:将值插入到列表的头部或尾部;
    • LPOP / RPOP:移除并返回列表的第一个或最后一个元素;
    • LRANGE:获取列表中指定范围的元素;
  5. 集合操作
    • SADD:将元素加入集合;
    • SMEMBERS:获取集合中的所有元素;
    • SREM:从集合中移除元素;
    • SISMEMBER:判断元素是否在集合中;
  6. 有序集合操作
    • ZADD:将元素及其分数加入有序集合;
    • ZRANGE:获取有序集合中指定范围的元素;
    • ZREM:移除有序集合中的元素;
  7. 事务操作
    • MULTI:开启事务;
    • EXEC:执行事务中的所有命令;
    • DISCARD:放弃事务中的命令;
  8. 持久化操作
    • SAVE:手动保存数据(阻塞操作);
    • BGSAVE:后台保存数据;
    • SLAVEOF:设置主从复制;
  9. 服务器管理
    • INFO:查看 Redis 服务器的运行信息。
    • SHUTDOWN:关闭 Redis 服务。
    • CONFIG:查看或设置 Redis 配置项。

以上操作覆盖了 Redis 的核心功能,涉及数据存储、数据类型操作、事务管理、持久化以及服务器管理等多个方面。

在 Redis 安装目录下,有一个 redis-cli.exe 文件,即为 Redis 客户端。以下是客户端操作实例:

# 连接服务 redis-cli -h <hostname> -p <port>
redis-cli.exe -h 127.0.0.1 -p 6379

# 连通性测试
127.0.0.1:6379> ping
PONG

# 选择对应的数据库 默认配置16个 select db
127.0.0.1:6379> select 1
OK

# 设置一个键值对 set key value
127.0.0.1:6379> set name "Alice"
OK

# 获取指定键的值 get key
127.0.0.1:6379> get name
"Alice"

# 删除键 del key
127.0.0.1:6379> del name
(integer) 1

# 整数键值自增一 incr key
127.0.0.1:6379> incr count
(integer) 2

# 整数键值自减一 decr key
127.0.0.1:6379> decr count
(integer) 1

# 查看 Redis 中 key 的数量
127.0.0.1:6379> dbsize
(integer) 0

# 查看 Redis 服务器的运行信息 
127.0.0.1:6379> info

# 删除当前数据库中的所有键
127.0.0.1:6379> flushdb

# 关闭 Redis 服务器 shutdown
127.0.0.1:6379> shutdown

# 退出 Redis 客户端
127.0.0.1:6379> exit

# 三、Redis 持久化功能关闭

要设置 Redis 的持久化策略或关闭持久化功能,可以通过修改 Redis 的配置文件(通常是 redis.conf)或在运行时使用命令进行配置。在常规单机系统中,并不需要考虑数据持久化,关闭持久化可以减少系统资源的占用。

# 1. 关闭 RDB(快照持久化)

# 在 redis.windows.conf 中找到 save 相关内容,按以下方式修改

# 注释以下三行
# save 900 1
# save 300 10
# save 60 10000

# 打开该行注释,或手工添加
save ""

# 2. 关闭 AOF(追加日志持久化)

# 在 redis.windows.conf 中找到 appendonly 将值改为 no
appendonly yes

# 四、Redis 多实例启动

虽然 Redis 是单线程的,但这并不意味着它就是性能瓶颈。单线程的优势在于避免了多线程编程中常见的锁竞争问题(如线程同步和上下文切换的开销)。在多线程程序中,多个线程访问共享资源时需要使用锁,而锁会增加额外的开销。Redis 采用了事件循环模型,通过单线程顺序地处理每个请求,从而避免了这些问题,提升了性能。但是如果在多个不相关的业务系统中,用同一个 Redis 实例,并不是最好的选择,又或者如果单个 Redis 实例无法满足性能需求(例如,需要更多的 CPU 核心或内存),那么可以启动多个 Redis 实例,来达到业务隔离,或者性能扩展的目的。

在一个 PC 上启动多个 Redis 实例,可以通过以下几个步骤来实现。

# 1. 准备多个配置文件

复制 Redis 配置文件

复制 Redis 的默认配置文件(redis.windows.conf)作为每个实例的配置文件。

redis.windows.01.conf
redis.windows.02.conf
redis.windows.03.conf
……

修改每个配置文件


# 每个文件单独修改端口号
port 6379
port 6380
port 6381
……

# 每个文件单独修改存储路径
# 注意这地方必须是文件夹,如果文件夹不存在,需要手工创建,否则报错
dir ./instance/instance-01
dir ./instance/instance-02
dir ./instance/instance-03
……

# 每个文件单独修改日志路径
logfile ./logs/instance-01.log
logfile ./logs/instance-02.log
logfile ./logs/instance-03.log
……

# 2. 启动多个 Reids 实例

redis-server redis.windows.01.conf
redis-server redis.windows.02.conf
redis-server redis.windows.03.conf

# 3. Windows 系统部署多个 Redis 服务

# 多实例操作与单实例操作,只多了一个 --service-name 参数来指定 Reis 服务名称,其他没区别

# 安装服务
# 由于配置文件指定了日志输出文件,所以此处命令行并不会有 Reids 日志出现,需要在日志文件中查看执行状态
.\redis-server.exe --service-install redis.windows.01.conf --service-name redis-01
.\redis-server.exe --service-install redis.windows.02.conf --service-name redis-02
.\redis-server.exe --service-install redis.windows.03.conf --service-name redis-03
……

# 启动服务
.\redis-server.exe --service--start --service-name redis-01
.\redis-server.exe --service--start --service-name redis-02
.\redis-server.exe --service--start --service-name redis-03
……

# 停止服务
.\redis-server.exe --service--stop --service-name redis-01
.\redis-server.exe --service--stop --service-name redis-02
.\redis-server.exe --service--stop --service-name redis-03
……

# 卸载服务
.\redis-server.exe --service--uninstall --service-name redis-01
.\redis-server.exe --service--uninstall --service-name redis-02
.\redis-server.exe --service--uninstall --service-name redis-03
……

# 五、在 Python 中使用 Redis

python 自身并没有集成 redis 库,所以首先需要单独安装 redis 库。

pip install redis

# 1. 使用 Redis 进行视频流传输

Redis 本身是一个轻量级的内存数据库,适用于快速的键值存储和轻量级数据传输,因此对资源的占用相对较低,同时,Redis 是单线程模型,但由于其事件驱动的架构,可以高效处理大量请求,非常适合需要高并发和低延迟的进程间通信。对于视频帧的传输来说非常合适。

# Python 跨进程的视频传输例程

Server 端 (写视频)

import redis
import cv2
import time

# 初始化 Redis 连接
r = redis.StrictRedis(host='localhost', port=6379, db=0)

# 打开视频文件
cap = cv2.VideoCapture('./test.mp4')

# 检查视频是否成功打开
if not cap.isOpened():
    print("Error: Could not open video.")
    exit()

# 获取视频帧率(fps)
fps = cap.get(cv2.CAP_PROP_FPS)

while True:
    ret, frame = cap.read()
    if not ret:
        break

    # 将帧的原始像素数据直接转换为二进制
    frame_bytes = frame.tobytes()

    # 将二进制数据写入 Redis,覆盖原来的帧
    r.set('video', frame_bytes)
    
    # 控制帧的写入间隔,确保与视频帧率一致
    time.sleep(1 / fps)
    
# 释放视频捕捉对象
cap.release()

Client 端(读队列)

import cv2
import redis
import time
import numpy as np

# 初始化 Redis 连接
r = redis.StrictRedis(host='localhost', port=6379, db=0)

while True:
    # 从 Redis 中读取最新的帧数据
    frame_bytes = r.get('video_1')
    
    # 如果没有读取到数据,说明还没有视频帧写入Redis
    if not frame_bytes:
        print("Waiting for video frame to be available in Redis...", end='\r')
        time.sleep(0.01)  # 等待 0.01 秒后再次尝试
        continue

    # 将 Redis 二进制数据转换为视频帧
    frame = np.frombuffer(frame_bytes, dtype=np.uint8).reshape((480, 852, 3))  # 注意分辨率
    
    cv2.imshow('Video Playback', frame)
    
    # 等待 0.01 秒,如果用户按下 'q' 键则退出
    if cv2.waitKey(10) & 0xFF == ord('q'):
        break

# 释放 OpenCV 窗口
cv2.destroyAllWindows()

# 2. 使用 Redis 队列进行控制流传输

Redis 提供了队列机制,可以用来实现先进先出(FIFO)的队列操作,队列机制保证了控制流数据的先后顺序,Redis 列表会持久化到磁盘(如果你启用了持久化配置),因此队列数据不会丢失,除非显式删除或 Redis 数据库被清空,非常适合控制流的通信。

# 基本操作

对于 先进先出(FIFO)队列,通常使用 RPUSH 来添加元素到队列尾部,然后使用 LPOP 从队列头部获取并删除元素。

  • LPUSH:将元素添加到列表的左端(队列的头部);
  • RPUSH:将元素添加到列表的右端(队列的尾部);
  • LPOP:从列表的左端移除并返回元素(队列的头部);
  • RPOP:从列表的右端移除并返回元素(队列的尾部);

# 阻塞队列

可以使用 Redis 提供的阻塞队列命令 BLPOPBRPOP 阻塞等待新数据到来。

  • BLPOP:在队列为空时,阻塞直到有元素可以被 LPOP
  • BRPOP:在队列为空时,阻塞直到有元素可以被 RPOP

# Python 跨进程的队列读写例程

A 程序(写队列)

import redis

# 连接 Redis
r = redis.StrictRedis(host='localhost', port=6379, db=0)

# 向队列 'my_queue' 添加元素
r.rpush('my_queue', 'item1')
r.rpush('my_queue', 'item2')
r.rpush('my_queue', 'item3')

print("Items added to the queue.")

B 程序(读取队列)

import redis

# 连接 Redis
r = redis.StrictRedis(host='localhost', port=6379, db=0)

# 从队列 'my_queue' 读取并移除元素
while True:
    item = r.lpop('my_queue')
    if item:
        print(f"Item retrieved from queue: {item.decode()}")
    else:
        print("Queue is empty.")
        break

B 程序(阻塞模式读取队列)

import redis

# 连接 Redis
r = redis.StrictRedis(host='localhost', port=6379, db=0)

# 使用 BLPOP 来实现阻塞式队列读取
while True:
    item = r.blpop('my_queue', timeout=0)  # timeout=0 表示一直阻塞直到有元素
    if item:
        print(f"Item retrieved from queue: {item[1].decode()}")
    else:
        print("Queue is empty.")