redigo订阅发布以及stream

文章介绍了Redis的发布订阅模式,强调其一对多、松耦合和实时性的优点,同时揭示了其潜在的问题。此外,文中详细解读了RedisStream作为新引入的数据结构,用于持久化、有序的消息处理,提供了多消费者支持和消息确认机制。
摘要由CSDN通过智能技术生成

redigo订阅发布以及stream

redis 订阅发布

Redis 订阅发布(Pub/Sub)是一种消息传递模式,用于实现消息的发布和订阅。在这种模式下,消息的发送者称为发布者(Publisher),而消息的接收者称为订阅者(Subscriber)。发布者将消息发送到一个特定的频道(Channel),而订阅者则可以订阅一个或多个频道,以接收发布者发送的消息。
为了支持消息的多播机制,redis 引入了发布订阅模块;
生产者生产一次消息,由redis负责将消息复制到多个消息队列中,每个消息队列由相应的消费者进行消费;
它是分布式系统中常用的一种解耦方式,用于将多个消费者的逻辑进行拆分。多个消费者的逻辑就可以放到不同的子系统中完成;
在这里插入图片描述
Redis 的发布订阅模式具有以下特点:

  • 一对多的消息传递:发布者可以将消息发送给多个订阅者,从而实现一对多的消息传递。
  • 松耦合:发布者和订阅者之间是松耦合的关系,它们不需要直接知道彼此的存在,而是通过共享的频道进行通信。
  • 实时性:消息是实时发送的,订阅者可以立即收到发布者发送的消息。
  • 动态扩展:可以动态地添加和移除订阅者,以适应系统的变化。

Redis 的发布订阅模式使用以下几个命令来实现:

  • PUBLISH channel message:发布一条消息到指定的频道。

  • SUBSCRIBE channel [channel …]:订阅一个或多个频道,接收发布者发送的消息。

  • UNSUBSCRIBE [channel [channel …]]:取消订阅一个或多个频道,停止接收发布者发送的消息。

  • PSUBSCRIBE pattern [pattern …]:订阅与给定模式匹配的一个或多个频道。

  • PUNSUBSCRIBE [pattern [pattern …]]:取消订阅与给定模式匹配的一个或多个频道。

以下是一个简单的示例,演示了如何在 Redis 中使用发布订阅模式:

发布者发送消息到频道:
127.0.0.1:6379> PUBLISH channel1 "Hello, subscribers!"
(integer) 1
订阅者订阅频道,并接收消息:
127.0.0.1:6379> SUBSCRIBE channel1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel1"
3) (integer) 1
1) "message"
2) "channel1"
3) "Hello, subscribers!"

在这个示例中,发布者将消息 “Hello, subscribers!” 发送到频道 “channel1”,而订阅者订阅了 “channel1” 频道,并成功接收到了发布者发送的消息。

缺点

发布订阅的生产者传递过来一个消息,redis会直接找到相应的消费者并传递过去;假如没有消费
者,消息直接丢弃;假如开始有2个消费者,一个消费者突然挂掉了,另外一个消费者依然能收到
消息,但是如果刚挂掉的消费者重新连上后,在断开连接期间的消息对于该消费者来说彻底丢失
了;
另外,redis停机重启,pubsub的消息是不会持久化的,所有的消息被直接丢弃;

stream

Redis Stream 是 Redis 5.0 引入的新数据结构,用于支持高性能的消息流处理。它提供了**持久化的、有序的、可扩展的消息传递机制,**适用于实时数据处理、消息队列、日志处理等场景。
Redis Stream 包含以下关键概念:

  1. 消息(Message):消息是 Stream 中的基本单元,它包含了一个唯一标识符(ID)和一个由键值对组成的数据体。消息的 ID
    是递增的,确保了消息的有序性。

  2. Stream:Stream 是由一系列消息组成的持久化数据流。每个 Stream 都有一个名称,可以通过该名称对 Stream进行操作。

  3. 消费者组(Consumer Group):消费者组是一组消费者的集合,它们共同消费一个 Stream中的消息。消费者组由一个唯一的名称标识,并维护了消费者之间的消息分配信息。

  4. 消费者(Consumer):消费者是消费者组中的一个成员,它负责从 Stream读取消息并进行处理。每个消费者都有一个唯一的名称,并且属于某个消费者组。

Redis 提供了一系列命令用于操作 Stream,包括:

XADD:向 Stream 中添加消息。
XLEN:获取 Stream 中的消息数量。
XRANGE、XREVRANGE:获取指定范围内的消息。
XREAD、XREADGROUP:从 Stream 中读取消息。
XGROUP:创建、销毁和管理消费者组。
XACK、XPENDING:对消息进行确认和查询未确认消息。
XDEL:删除消息。
在这里插入图片描述
支持多播的可持久化消息队列;
一个消息链表将加入的消息都串起来,每个消息都有一个唯一的消息ID和对应的内容;消息都是持久化的,redis 重启后,内容还在;
每个 stream 对象通过一个 key 来唯一索引;每个 stream 都可以挂多个消费组(consumer group),每个消费组会有个游标 last_delivered_id 在 stream 数组之上往前移动,表示当前消费
组已经消费到哪条消息了。
stream 在第一次使用 xadd 命令后自动创建;而消费组不会自动创建,需要通过命令 xgroup create 进行创建,并且需要指定从 stream 的某个消息 ID 开始消费;
每个消费组都是相互独立的,互相不受影响;也就是同一份 stream 内部的消息会被每个消费组都消费到;
同一个消费组可以挂接多个消费者,这些消费者之间是竞争关系,任意一个消费者读取了消息都会使游标往前移动;
消费者内部会有一个状态变量 pending_ids,它记录了当前已经被客户端读取,但是还没有 ack 的消息。当客户端 ack 一条消息后,pending_ids 将会删除该消息 ID;它用来确保客户端至少消费了消息一次,而不会在网络传输的中途丢失了而没有被处理;

# 向 stream 追加消息
XADD key ID field string [field string ...]
 # 从 stream 中删除消息
XDEL key ID [ID ...]
 # 获取 stream 中消息列表,会自动过滤已经删除的消息
XRANGE key start end [COUNT count]
 # 获取 stream 消息长度
XLEN key
 # 删除 stream 中所有消息
DEL key
 # 独立消费
XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...]
 # 创建消费者
XGROUP [CREATE key groupname id-or-$] [SETID key id-or-$] [DESTROY key 
groupname] [DELCONSUMER key groupname consumername]
 # 消费消息
XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds] STREAMS 
key [key ...] ID [ID ...]
 # > 意味着消费者希望只接收从未发送给任何其他消费者的消息。最新的消息
# 任意其他id  发送待处理的消息
# 确认消费消息
XACK key group ID [ID ...]

例子:

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/go-redis/redis/v8"
)

func main() {
	// 创建 Redis 客户端连接
	rdb := redis.NewClient(&redis.Options{
		Addr: "localhost:6379", // Redis 服务器地址
	})

	// 定义消息发送函数
	sendMessage := func(ctx context.Context, streamName string, message map[string]interface{}) error {
		// 将消息发送到指定的 Stream
		_, err := rdb.XAdd(ctx, &redis.XAddArgs{
			Stream: streamName, // Stream 名称
			Values: message,    // 消息内容
		}).Result()
		return err
	}

	// 定义消息接收函数
	receiveMessages := func(ctx context.Context, streamName string, consumerGroup string, consumerName string) {
		for {
			// 从 Stream 中读取消息
			msgs, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
				Group:    consumerGroup,             // 消费者组名称
				Consumer: consumerName,              // 消费者名称
				Streams:  []string{streamName, ">"}, // 读取指定 Stream 中的消息
				Count:    10,                        // 最多读取 10 条消息
				Block:    0,                         // 阻塞时间,0 表示一直阻塞直到有消息到达
			}).Result()
			if err != nil {
				fmt.Println("Error reading messages:", err)
				continue
			}

			// 处理接收到的消息
			for _, msg := range msgs {
				stream := msg.Stream
				for _, message := range msg.Messages {
					id := message.ID
					data := message.Values
					fmt.Printf("Received message from stream %s, ID: %s, Data: %v\n", stream, id, data)
				}
			}
		}
	}

	// 创建一个新的消费者组
	const streamName = "my_stream"
	const consumerGroup = "my_consumer_group"
	const consumerName = "my_consumer"
	ctx := context.Background()
	if err := rdb.XGroupCreate(ctx, streamName, consumerGroup, "$").Err(); err != nil {
		fmt.Println("Error creating consumer group:", err)
		return
	}

	// 启动消息接收协程
	go receiveMessages(ctx, streamName, consumerGroup, consumerName)

	// 发送一些测试消息
	for i := 1; i <= 5; i++ {
		message := map[string]interface{}{
			"number": i,
			"time":   time.Now().Unix(),
		}
		if err := sendMessage(ctx, streamName, message); err != nil {
			fmt.Println("Error sending message:", err)
			return
		}
		fmt.Println("Sent message:", message)
		time.Sleep(time.Second)
	}

	// 等待接收消息
	select {}
}

创建了一个简单的 Redis 客户端,然后定义了一个消息发送函数 sendMessage 和一个消息接收函数 receiveMessages。在 main 函数中,我们创建了一个名为 my_stream 的 Stream,并创建了一个消费者组 my_consumer_group,然后启动了一个消息接收协程,并在主线程中发送了一些测试消息。消息接收协程会一直阻塞并等待接收消息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值