RocketMQ消息类型

RocketMQ集群架构

一个完整的RocketMQ集群中,有如下几个角色

Producer:消息的发送者;举例:发信者
Consumer:消息接收者;举例:收信者
Broker:暂存和传输消息;举例:邮局
NameServer:管理Broker;举例:各个邮局的管理机构
Topic:区分消息的种类;一个发送者可以发送消息给一个或者多个Topic;一个消息的接收者可以
订阅一个或者多个Topic消息

RocketMQ的消息样例

1、基本样例:

W基本样例部分我们使用消息生产者分别通过三种方式发送消息,同步发送、异步发送以及单向发送

这个示例有个比较有趣的地方就是引入了一个countDownLatch来保证所有消息回调方法都执行
完了再关闭Producer。 所以从这里可以看出,RocketMQ的Producer也是一个服务端,在往
Broker发送消息的时候也要作为服务端提供服务。

单向发送消息的样例:

public class OnewayProducer {
  public static void main(String[] args) throws Exception{
    //Instantiate with a producer group name.
    DefaultMQProducer producer = new
DefaultMQProducer("please_rename_unique_group_name");
    // Specify name server addresses. producer.setNamesrvAddr("localhost:9876");
    //Launch the instance.
    producer.start();
    for (int i = 0; i < 100; i++) {
      //Create a message instance, specifying topic, tag and message
body.
      Message msg = new Message("TopicTest" /* Topic */,
        "TagA" /* Tag */,
       ("Hello RocketMQ " +
          i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message
body */
     );
      //Call send message to deliver message to one of brokers.
      producer.sendOneway(msg);
   }
    //Wait for sending to complete
    Thread.sleep(5000);    
    producer.shutdown();
 }
}

关键点就是使用producer.sendOneWay方式来发送消息,这个方法没有返回值,也没有回调。
就是只管把消息发出去就行了

使用消费者消费消息。
消费者消费消息有两种模式,一种是消费者主动去Broker上拉取消息的拉模式,另一种是消费者等待Broker把消息推送过来的推模式。
通常情况下,用推模式比较简单。
实际上RocketMQ的推模式也是由拉模式封装出来的。
4.7.1版本中DefaultMQPullConsumerImpl这个消费者类已标记为过期,但是还是可以使用的。
替换的类是DefaultLitePullConsumerImpl。

2顺序消息
RocketMQ保证的是消息的局部有序,而不是全局有序。

首先在发送者端:在默认情况下,消息发送者会采取Round Robin轮询方式把消息发送到不同的
MessageQueue(分区队列),而消费者消费的时候也从多个MessageQueue上拉取消息,这种情
况下消息是不能保证顺序的。而只有当一组有序的消息发送到同一个MessageQueue上时,才能
利用MessageQueue先进先出的特性保证这一组消息有序。
而Broker中一个队列内的消息是可以保证有序的。
然后在消费者端:消费者会从多个消息队列上去拿消息。这时虽然每个消息队列上的消息是有序
的,但是多个队列之间的消息仍然是乱序的。消费者端要保证消息有序,就需要按队列一个一个
来取消息,即取完一个队列的消息后,再去取下一个队列的消息。而给consumer注入的
MessageListenerOrderly对象,在RocketMQ内部就会通过锁队列的方式保证消息是一个一个队
列来取的。MessageListenerConcurrently这个消息监听器则不会锁队列,每次都是从多个Message中取一批数据(默认不超过32条)。因此也无法保证消息有序。

 3、广播消息
广播消息并没有特定的消息消费者样例,这是因为这涉及到消费者的集群消费模式。在集群状态
(MessageModel.CLUSTERING)下,每一条消息只会被同一个消费者组中的一个实例消费到(这跟
kafka和rabbitMQ的集群模式是一样的)。而广播模式则是把消息发给了所有订阅了对应主题的消
费者,而不管消费者是不是同一个消费者组。

4、延迟消息

延迟消息实现的效果就是在调用producer.send方法后,消息并不会立即发送出去,而是会等一
段时间再发送出去。这是RocketMQ特有的一个功能。
那会延迟多久呢?延迟时间的设置就是在Message消息对象上设置一个延迟级别

开源版本的RocketMQ中,对延迟消息并不支持任意时间的延迟设定(商业版本中支持),而是只支
持18个固定的延迟级别,1到18分别对应messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m
6m 7m 8m 9m 10m 20m 30m 1h 2h。这从哪里看出来的?其实从rocketmq-console控制台就能看出来。而这18个延迟级别也支持自行定义,不过一般情况下最好不要自定义修改。

5、批量消息
批量消息是指将多条消息合并成一个批量消息,一次发送出去。这样的好处是可以减少网络IO,提升吞吐量。
相信大家在官网以及测试代码中都看到了关键的注释:如果批量消息大于1MB就不要用一个批次
发送,而要拆分成多个批次消息发送。也就是说,一个批次消息的大小不要超过1MB实际使用时,这个1MB的限制可以稍微扩大点,实际最大的限制是4194304字节,大概4MB。但是使用批量消息时,这个消息长度确实是必须考虑的一个问题。而且批量消息的使用是有一定限制的,这些消息应该有相同的Topic,相同的waitStoreMsgOK。而且不能是延迟消息、事务消息等。

6、过滤消息
在大多数情况下,可以使用Message的Tag属性来简单快速的过滤信息。
主要是看消息消费者。consumer.subscribe("TagFilterTest", "TagA || TagC"); 这句只订阅TagA和TagC的消息。
TAG是RocketMQ中特有的一个消息属性。RocketMQ的最佳实践中就建议,使用RocketMQ时,
一个应用可以就用一个Topic,而应用中的不同业务就用TAG来区分。
但是,这种方式有一个很大的限制,就是一个消息只能有一个TAG,这在一些比较复杂的场景就有点不足了。 这时候,可以使用SQL表达式来对消息进行过滤。

这个模式的关键是在消费者端使用MessageSelector.bySql(String sql)返回的一个MessageSelector。这里面的sql语句是按照SQL92标准来执行的。sql中可以使用的参数有默认的
TAGS和一个在生产者中加入的a属性。
SQL语法:
RocketMQ只定义了一些基本语法来支持这个特性。你也可以很容易地扩展它。
数值比较,比如:>,>=,<,<=,BETWEEN,=;
字符比较,比如:=,<>,IN;
IS NULL 或者 IS NOT NULL;
逻辑符号 AND,OR,NOT;
常量支持类型为:
数值,比如:123,3.1415;

字符,比如:'abc',必须用单引号包裹起来;
NULL,特殊的常量
布尔值,TRUE 或 FALSE
使用注意:只有推模式的消费者可以使用SQL过滤。拉模式是用不了的。

7、事务消息
这个事务消息是RocketMQ提供的一个非常有特色的功能。
首先,我们了解下什么是事务消息。官网的介绍是:事务消息是在分布式系统中保证最终一致性的两阶段提交的消息实现。他可以保证本地事务执行与消息发送两个操作的原子性,也就是这两个操作一起成功或者一起失败。
其次,我们来理解下事务消息的编程模型。事务消息只保证消息发送者的本地事务与发消息这两个操作的原子性,因此,事务消息的示例只涉及到消息发送者,对于消息消费者来说,并没有什么特别的。
事务消息的关键是在TransactionMQProducer中指定了一个TransactionListener事务监听器,这
个事务监听器就是事务消息的关键控制器。源码中的案例有点复杂,我这里准备了一个更清晰明
了的事务监听器示例

public class TransactionListenerImpl implements TransactionListener {
//在提交完事务消息后执行。
//返回COMMIT_MESSAGE状态的消息会立即被消费者消费到。
//返回ROLLBACK_MESSAGE状态的消息会被丢弃。
//返回UNKNOWN状态的消息会由Broker过一段时间再来回查事务的状态。
  @Override
  public LocalTransactionState executeLocalTransaction(Message msg,
Object arg) {
    String tags = msg.getTags();
    //TagA的消息会立即被消费者消费到
    if(StringUtils.contains(tags,"TagA")){
      return LocalTransactionState.COMMIT_MESSAGE;
    //TagB的消息会被丢弃
   }else if(StringUtils.contains(tags,"TagB")){
      return LocalTransactionState.ROLLBACK_MESSAGE;
    //其他消息会等待Broker进行事务状态回查。
   }else{
      return LocalTransactionState.UNKNOW;
   }
 }
//在对UNKNOWN状态的消息进行状态回查时执行。返回的结果是一样的。
  @Override
  public LocalTransactionState checkLocalTransaction(MessageExt msg) {
String tags = msg.getTags();
    //TagC的消息过一段时间会被消费者消费到
    if(StringUtils.contains(tags,"TagC")){
      return LocalTransactionState.COMMIT_MESSAGE;
    //TagD的消息也会在状态回查时被丢弃掉
   }else if(StringUtils.contains(tags,"TagD")){
      return LocalTransactionState.ROLLBACK_MESSAGE;
    //剩下TagE的消息会在多次状态回查后最终丢弃
   }else{
      return LocalTransactionState.UNKNOW;
   }
 }
}

然后,我们要了解下事务消息的使用限制:
1、事务消息不支持延迟消息和批量消息。
2、为了避免单个消息被检查太多次而导致半队列消息累积,我们默认将单个消息的检查次数限制为15 次,但是用户可以通过 Broker 配置文件的 transactionCheckMax 参数来修改此限制。如果已经检查某条消息超过 N 次的话( N = transactionCheckMax ) 则 Broker 将丢弃此消息,并在默认情况下同时打印错误日志。用户可以通过重写 AbstractTransactionCheckListener 类来修改这个行为。
3、事务消息将在 Broker 配置文件中的参数 transactionMsgTimeout 这样的特定时间长度之后被检
查。当发送事务消息时,用户还可以通过设置用户属性CHECK_IMMUNITY_TIME_IN_SECONDS 来改变这个限制,该参数优先于 transactionMsgTimeout 参数。
4、事务性消息可能不止一次被检查或消费。
5、提交给用户的目标主题消息可能会失败,目前这依日志的记录而定。它的高可用性通过
RocketMQ 本身的高可用性机制来保证,如果希望确保事务消息不丢失、并且事务完整性得到保证,建议使用同步的双重写入机制。
6、事务消息的生产者 ID 不能与其他类型消息的生产者 ID 共享。与其他类型的消息不同,事务消息允许反向查询、MQ服务器能通过它们的生产者 ID 查询到消费者。
接下来,我们还要了解下事务消息的实现机制,参见下图:

 事务消息机制的关键是在发送消息时,会将消息转为一个half半消息,并存入RocketMQ内部的一个RMQ_SYS_TRANS_HALF_TOPIC 这个Topic,这样对消费者是不可见的。再经过一系列事务检查通过后,再将消息转存到目标Topic,这样对消费者就可见了。
最后,我们还需要思考下事务消息的作用。
大家想一下这个事务消息跟分布式事务有什么关系?为什么扯到了分布式事务相关的两阶段提交上
了?

事务消息只保证了发送者本地事务和发送消息这两个操作的原子性,但是并不保证消费者本地事务
的原子性,所以,事务消息只保证了分布式事务的一半。但是即使这样,对于复杂的分布式事务,
RocketMQ提供的事务消息也是目前业内最佳的降级方案。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值