RocketMQ:理解

399 阅读1分钟

常识

  • 一个消息队列可以被多个消费者组订阅,一条消息只有被所有订阅它的消费者组消费了,才算被消费成功。 验证
package main

import (
   "context"
   "fmt"
   "github.com/apache/rocketmq-client-go/v2"
   "github.com/apache/rocketmq-client-go/v2/consumer"
   "github.com/apache/rocketmq-client-go/v2/primitive"
   "os"
   "rocketMQ_demo/common"
   "time"
)

func main() {

   c := make(chan struct{})
   go startC1(c)
   go startC2(c)

   <-c
   <-c
}

func startC1(ch chan struct{}) {
   c, _ := rocketmq.NewPushConsumer(
      consumer.WithGroupName("consumer_group_01"),
      consumer.WithNameServer(primitive.NamesrvAddr{common.NameServerAddress}),
      consumer.WithConsumerModel(consumer.Clustering),
      consumer.WithConsumeFromWhere(consumer.ConsumeFromLastOffset),
   )

   //订阅topic为test的消息
   err := c.Subscribe("test", consumer.MessageSelector{},
      func(ctx context.Context,
         msgs ...*primitive.MessageExt) (consumer.ConsumeResult, error) {

         for _, msg := range msgs {
            fmt.Printf("c1 subscribe callback: %v \n", msg)
         }

         return consumer.ConsumeSuccess, nil
      })

   if err != nil {
      fmt.Println(err.Error())
   }

   // Note: start after subscribe
   err = c.Start()
   if err != nil {
      fmt.Println(err.Error())
      os.Exit(-1)
   }
   time.Sleep(time.Minute)
   err = c.Shutdown()
   if err != nil {
      fmt.Printf("shundown Consumer c1 error: %s", err.Error())
   }
   ch <- struct{}{}

}

func startC2(ch chan struct{}) {
   c, _ := rocketmq.NewPushConsumer(
      consumer.WithGroupName("consumer_group_02"),
      consumer.WithNameServer(primitive.NamesrvAddr{common.NameServerAddress}),
      consumer.WithConsumerModel(consumer.Clustering),
      consumer.WithConsumeFromWhere(consumer.ConsumeFromLastOffset),
   )

   //订阅topic为test的消息
   err := c.Subscribe("test", consumer.MessageSelector{},
      func(ctx context.Context,
         msgs ...*primitive.MessageExt) (consumer.ConsumeResult, error) {

         for _, msg := range msgs {
            fmt.Printf("c2 subscribe callback: %v \n", msg)
         }

         return consumer.ConsumeSuccess, nil
      })

   if err != nil {
      fmt.Println(err.Error())
   }

   // Note: start after subscribe
   err = c.Start()
   if err != nil {
      fmt.Println(err.Error())
      os.Exit(-1)
   }
   time.Sleep(time.Minute)
   err = c.Shutdown()
   if err != nil {
      fmt.Printf("shundown Consumer c2 error: %s", err.Error())
   }
   ch <- struct{}{}
}
  • 订阅关系的一致性指的是,同一个消费者组(Group ID相同)下所有Consumer实例所订阅的Topic与 Tag及对消息的处理逻辑必须完全一致。否则,消息消费的逻辑就会混乱,甚至导致消息丢失。
  • 顺序消息的实现是让producer在发送消息时选择特定的queue来实现的。

example:订单的消息,订单的状态有: 待付款 -> 已支付 -> 已发货 -> 已签收 -> ...,对于同一订单,系统在逻辑上要按合理的状态去处理,所以producer只要把orderID相同的order都发到特定的queue,这样就可以保证consumer在同一个queue拉取到的message的order的状态顺序肯定是合理的。

  • 一级分类:topic; 二级分类:tag。 最小粒度:用户属性,producer发送message时为message添加自定义的用户属性,consumer订阅时可用sql去筛选(筛选用户属性)符合要求的message。