RocketMq-顺序消息

顺序消息

顺序消息是指消息的消费顺序和产生顺序相同,在有些业务逻辑下,必须保证顺序。比如订单的生
成、付款、发货,这3个消息必须按顺序处理才行。
顺序消息分为全局顺序消息和部分顺序消息:

  1. 全局顺序消息指某个Topic下的所有消息都要保证顺序;
  2. 部分顺序消息只要保证每一组消息被顺序消费即可,比如上面订单消息的例子,只要保证同一
    个订单ID的三个消息能按顺序消费即可。
    在多数的业务场景中实际上只需要局部有序就可以了

RocketMQ在默认情况下不保证顺序,比如创建一个Topic,默认八个写队列,八个读队列。这时
候一条消息可能被写入任意一个队列里;在数据的读取过程中,可能有多个Consumer,每个
Consumer也可能启动多个线程并行处理,所以消息被哪个Consumer消费,被消费的顺序和写入的顺
序是否一致是不确定的

要保证全局顺序消息,需要先把Topic的读写队列数设置为一,然后Producer和Consumer的并发
设置也要是一。简单来说,为了保证整个Topic的全局消息有序,只能消除所有的并发处理,各部分都
设置成单线程处理。
在这里插入图片描述
要保证部分消息有序,需要发送端和消费端配合处理。在发送端,要做到把同一业务ID的消息发送到同一个Message Queue;在消费过程中,要做到从同一个Message Queue读取的消息不被并发处理,这样才能达到部分有序。消费端通过使用MessageListenerOrderly类来解决单Message Queue的消息被并发处理的问题

Consumer使用MessageListenerOrderly的时候,下面四个Consumer的设置依旧可以使用:

  1. setConsumeThreadMin
  2. setConsumeThreadMax
  3. setPullBatchSize
  4. setConsumeMessageBatchMaxSize。
    前两个参数设置Consumer的线程数;
    PullBatchSize指的是一次从Broker的一个Message Queue获取消息的最大数量,默认值是32;

ConsumeMessageBatchMaxSize指的是这个Consumer的Executor(也就是调用
MessageListener处理的地方)一次传入的消息数(Listmsgs这个链表的最大长度),
默认值是1。
上述四个参数可以使用,说明MessageListenerOrderly并不是简单地禁止并发处理。在MessageListenerOrderly的实现中,为每个Consumer Queue加个锁,消费每个消息前,需要先获得
这个消息对应的Consumer Queue所对应的锁,这样保证了同一时间,同一个Consumer Queue的消
息不被并发消费,但不同Consumer Queue的消息可以并发处理

部分有序:
顺序消息的生产和消费

# 创建主题,88[root@node1 ~]# mqadmin updateTopic -b node1:10911 -n localhost:9876 -r 8 -t
tp_demo_07 -w 8
# 删除主题的操作:
[root@node1 ~]# mqadmin deleteTopic -c DefaultCluster deleteTopic -n
localhost:9876 -t tp_demo_07
# 主题描述
[root@node1 ~]# mqadmin topicStatus -n localhost:9876 -t tp_demo_07

OrderProducer

public class OrderProducer {
public static void main(String[] args) throws MQClientException,
RemotingException, InterruptedException, MQBrokerException {
DefaultMQProducer producer = new
DefaultMQProducer("producer_grp_07_01");
producer.setNamesrvAddr("node1:9876");
producer.start();
Message message = null;
List<MessageQueue> queues =
producer.fetchPublishMessageQueues("tp_demo_07");
System.err.println(queues.size());
MessageQueue queue = null;
for (int i = 0; i < 100; i++) {
queue = queues.get(i % 8);
message = new Message("tp_demo_07", ("hello lagou - order
create" + i).getBytes());
producer.send(message, queue);
message = new Message("tp_demo_07", ("hello lagou - order
payed" + i).getBytes());
producer.send(message, queue);
message = new Message("tp_demo_07", ("hello lagou - order ship"
+ i).getBytes());
producer.send(message, queue);
} p
roducer.shutdown();
}
}
OrderConsumer
public class OrderConsumer {
public static void main(String[] args) throws MQClientException,
RemotingException, InterruptedException, MQBrokerException {
DefaultMQPullConsumer consumer = new
DefaultMQPullConsumer("consumer_grp_07_01");
consumer.setNamesrvAddr("node1:9876");
consumer.start();
Set<MessageQueue> messageQueues =
consumer.fetchSubscribeMessageQueues("tp_demo_07");
System.err.println(messageQueues.size());
for (MessageQueue messageQueue : messageQueues) {
long nextBeginOffset = 0;
System.out.println("===============================");
do {
PullResult pullResult = consumer.pull(messageQueue, "*",
nextBeginOffset, 1);
if (pullResult == null || pullResult.getMsgFoundList() ==
null) break;
nextBeginOffset = pullResult.getNextBeginOffset();
List<MessageExt> msgFoundList =
pullResult.getMsgFoundList();
System.out.println(messageQueue.getQueueId() + "\t" +
msgFoundList.size());
for (MessageExt messageExt : msgFoundList) {
System.out.println(
messageExt.getTopic() + "\t" +
messageExt.getQueueId() + "\t" +
messageExt.getMsgId() + "\t" +
new String(messageExt.getBody())
);
}
} while (true);
} 
consumer.shutdown();
}
}
全局有序:

顺序消息的生产和消费:

# 创建主题,8写8读
[root@node1 ~]# mqadmin updateTopic -b node1:10911 -n localhost:9876 -r 1 -t
tp_demo_07_01 -w 1
# 删除主题的操作:
[root@node1 ~]# mqadmin deleteTopic -c DefaultCluster deleteTopic -n
localhost:9876 -t tp_demo_07_01
# 主题描述
[root@node1 ~]# mqadmin topicStatus -n localhost:9876 -t tp_demo_07_01
  • GlobalOrderProduer
public class GlobalOrderProducer {
public static void main(String[] args) throws MQClientException,
RemotingException, InterruptedException, MQBrokerException {
DefaultMQProducer producer = new
DefaultMQProducer("producer_grp_07_02");
producer.setNamesrvAddr("node1:9876");
producer.start();
Message message = null;
for (int i = 0; i < 100; i++) {
message = new Message("tp_demo_07_01", ("hello lagou" +i).getBytes());
producer.send(message);
} 
producer.shutdown();
}
}
  • GlobalOrderConsumer
public class GlobalOrderConsumer {
public static void main(String[] args) throws MQClientException {
DefaultMQPushConsumer consumer = new
DefaultMQPushConsumer("consumer_grp_07_03");
consumer.setNamesrvAddr("node1:9876");
consumer.subscribe("tp_demo_07_01", "*");
consumer.setConsumeThreadMin(1);
consumer.setConsumeThreadMax(1);
consumer.setPullBatchSize(1);
consumer.setConsumeMessageBatchMaxSize(1);
consumer.setMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt>
msgs, ConsumeOrderlyContext context) {
for (MessageExt msg : msgs) {
System.out.println(new String(msg.getBody()));
} 
return ConsumeOrderlyStatus.SUCCESS;
}
});
consumer.start();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RocketMQ是一款开源的分布式消息队列系统,而rocketmq-client-java是RocketMQ的Java客户端。 rocketmq-client-java提供了丰富的API,使得开发者可以方便地使用RocketMQ进行消息的发布和订阅。它支持多种消息发送模式,包括同步发送、异步发送和单向发送。同步发送保证了消息的可靠性和顺序性,异步发送提高了发送效率,而单向发送则不需要等待服务器的响应。此外,rocketmq-client-java还支持批量发送和延迟发送等特性,满足了不同场景下的需求。 rocketmq-client-java提供了多种消息消费模式,包括集群模式和广播模式。在集群模式下,多个消费者可以共同消费一个队列的消息,实现消息的负载均衡和高可用性;而在广播模式下,每个消费者都能收到所有队列的消息,适用于需要实时获得消息的场景。 rocketmq-client-java还提供了自动负载均衡和故障转移的功能。当消费者增减或者下线时,RocketMQ会自动进行负载均衡,保证每个消费者都能平均地消费消息。而当某个消费者出现故障时,RocketMQ会将该消费者的消息重新分配给其他正常工作的消费者,保证消息的可靠性和高可用性。 此外,rocketmq-client-java还支持消息过滤、消息事务和顺序消息等高级特性。消息过滤可以根据指定的条件对消息进行过滤,只消费符合条件的消息消息事务可以保证消息的原子性,要么一起成功,要么一起失败。顺序消息可以保证按照发送顺序进行消费,避免了消息的乱序问题。 综上所述,rocketmq-client-java是RocketMQ的Java客户端,提供了丰富的API和多种特性,使得开发者可以方便地使用RocketMQ进行消息的发布和订阅,并满足不同场景下的需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值