在新的版本中定时消息等价于延迟消息
-
定时消息:例如,当前系统时间为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)
}