rocketMQ5.0定时/延迟消息实战

在新的版本中定时消息等价于延迟消息

  • 定时消息:例如,当前系统时间为2022-06-09 17:30:00,您希望消息在下午19:20:00定时投递,则定时时间为2022-06-09 19:20:00,转换成时间戳格式为1654773600000。

  • 延时消息:例如,当前系统时间为2022-06-09 17:30:00,您希望延时1个小时后投递消息,则您需要根据当前时间和延时时长换算成定时时刻,即消息投递时间为2022-06-09 18:30:00,转换为时间戳格式为1654770600000

Notice:在实战中,如果当前系统时间存在毫秒时间戳,在数据投递的时候,会向上取整

以下是每两秒发送一个延迟10毫秒的时间戳,自然时间的消费者收到的投递时间约为11秒

以下是秒级时间戳去除毫秒时间戳影响,我们看到消费者10秒之后收到消息投递

消息的延迟时间限制

在rocketMQ4.0版本中延迟消息支持18个等级阶梯如图

rocketMQ5.0中,延迟消息不再对阶梯进行限制,使用云消息RocketMQ5.0的延迟时间按照不同的产品规格支持最大可到7到40天。

普通按照rocketMQ5.0最大支持24小时。

定时时长最大值默认为24小时,不支持自定义修改,

定时时间设置原则

  • 定时时长最大值默认为24小时(单机安装非云消息),不支持自定义修改。
  • 定时时间必须设置为当前时间之后,若设置到当前时间之前,则定时不生效,服务端会立即投递消息
  • 定时时间的格式为毫秒级的Unix时间戳

延迟消息的设置

首先我们来docker 部署rocketMQ与rocketMQDashBoard

(具体过程参考)rocketMQ5.0事务消息实战-CSDN博客

 message.type:DELAY                                                                                                                     docker exec -it rmqnamesrv /bin/bash       

sh mqadmin updateTopic -c DefaultCluster -t DelayTopic -n 127.0.0.1:9876 -a +message.type=DELAY


sh mqadmin updateTopic -n <nameserver_address> -t <topic_name> -c <cluster_name> -a +message.type=DELAY

消费者重试

当我们不给消息返回ack,触发消息重试,golang最新版本SimpleConsumer最大不可见时间为20秒。官方文档为10秒钟。超过最大不可见时间未返回ack,触发消费者重试!

消息发生重试

 在消息重试的时,在dashboard中可以看到该ConsumeGroup下面的的重试队列信息

消费者重试达到最大次数

   达到最大重试16次之后,按照消费者的最大重试周期之后,将被投向死信队列,在dashboard中可以查看(如下),死信队列概念:RocketMQ5.0死信队列-CSDN博客 死信队列发生重试

生产者

package main

import (
	"context"
	"fmt"
	"github.com/apache/rocketmq-clients/golang/v5"
	_ "github.com/apache/rocketmq-clients/golang/v5"
	"github.com/apache/rocketmq-clients/golang/v5/credentials"
	"log"
	"strconv"
	"time"
)

const (
	Topic     = "DelayTopic"
	GroupName = "testG"
	Endpoint  = "localhost:8081"
	Region    = "xxxxxx"
	AccessKey = "xxxxxx"
	SecretKey = "xxxxxx"
)

func main() {
	//SendNormalTopic()
	SendDelayTopic()
	time.Sleep(time.Minute * 10)
}

func SendDelayTopic() {
	producer, err := golang.NewProducer(&golang.Config{
		Endpoint:    Endpoint,
		Credentials: &credentials.SessionCredentials{},
	},
		golang.WithTopics(Topic),
	)
	if err != nil {
		log.Fatal(err)
	}
	// start producer
	err = producer.Start()
	if err != nil {
		log.Fatal(err)
	}
	// gracefule stop producer
	defer producer.GracefulStop()
	var now = time.Now()
	for i := 0; i < 10; i++ {
		now = time.Unix(time.Now().Unix(), 0)
		msg := &golang.Message{
			Topic: Topic,
			Body:  []byte("this is a message : " + strconv.Itoa(i) + now.Format(time.DateTime)),
		}
		msg.SetDelayTimestamp(now.Add(time.Second * 10))
		fmt.Println(now)
		// send message in sync
		resp, err := producer.Send(context.TODO(), msg)
		if err != nil {
			log.Fatal(err)
		}
		for i := 0; i < len(resp); i++ {
			fmt.Printf("%#v\n", resp[i])
		}
		// wait a moment
		time.Sleep(time.Second * 2)
	}
}

   消费者

package main

import (
	"context"
	"fmt"
	"github.com/apache/rocketmq-clients/golang/v5"
	"github.com/apache/rocketmq-clients/golang/v5/credentials"
	"log"
	"os"
	"time"
)

const (
	Topic     = "DelayTopic"
	GroupName = "testG"
	Endpoint  = "localhost:8081"
)

var (
	// maximum waiting time for receive func
	awaitDuration = time.Second * 5
	// maximum number of messages received at one time
	maxMessageNum int32 = 16
	// invisibleDuration should > 20s
	invisibleDuration = time.Second * 20
	// receive messages in a loop
)

func main() {
	Broadcasting()
}
func Broadcasting() {
	// log to console
	os.Setenv("mq.consoleAppender.enabled", "true")
	golang.ResetLogger()
	// new simpleConsumer instance
	simpleConsumer, err := golang.NewSimpleConsumer(&golang.Config{
		Endpoint:      Endpoint,
		Credentials:   &credentials.SessionCredentials{},
		ConsumerGroup: "string",
	},

		golang.WithAwaitDuration(awaitDuration),
		golang.WithSubscriptionExpressions(map[string]*golang.FilterExpression{
			Topic: golang.SUB_ALL,
		}),
	)
	if err != nil {
		log.Fatal(err)
	}

	// start simpleConsumer
	err = simpleConsumer.Start()
	if err != nil {
		log.Fatal(err)
	}
	// gracefule stop simpleConsumer
	defer simpleConsumer.GracefulStop()

	for {
		fmt.Println("start recevie message")
		mvs, err := simpleConsumer.Receive(context.TODO(), maxMessageNum, invisibleDuration)
		if err != nil {
			fmt.Println(err)
			continue
		}
		// ack message
		go func(mvs []*golang.MessageView) {
			for _, mv := range mvs {
				simpleConsumer.Ack(context.TODO(), mv)
				fmt.Println(string(mv.GetBody()) + "  " + time.Now().Format(time.DateTime))
			}
		}(mvs)

	}
	// run for a while
	time.Sleep(time.Minute * 20)
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值