opic的有序消息已经成为mq的标配。而RocketMQ中是这样区分消息类型的, 普通消息也叫做无序消息,简单来说就是没有顺序的消息,而有序消息就是按照一定的先后顺序的消息类型。举个例子,producer 依次发送 order id 为 1、2、3 的消息到 broker,consumer 接到的消息顺序也就是 1、2、3 ,而不会出现普通消息那样的 2、1、3 等情况。

一、有序消息该如何实现

理论上:我们都知道消息首先由 producer 到 broker,再从 broker 到 consumer,分这两步走。那么要保证消息的有序,势必这两步都是要保证有序的,即要保证消息是按有序发送到 broker,broker 也是有序将消息投递给 consumer,两个条件必须同时满足,缺一不可。

1.1、全局有序消息

由于一个 topic 只有一个 queue ,即使我们有多个 producer 实例和 consumer 实例也很难提高消息吞吐量。就好比过独木桥,大家只能一个挨着一个过去,效率低下。

1.2、局部有序消息

常见做法就是将 order id 进行处理,将 order id 相同的消息发送到 topicB 的同一个 queue,假设我们 topicB 有 2 个 queue,那么我们可以简单的对 id 取余,奇数的发往 queue0,偶数的发往 queue1,消费者按照 queue 去消费时,就能保证 queue0 里面的消息有序消费,queue1 里面的消息有序消费。

二、RocketMQ的topic的补充

opic 只是消息的逻辑分类,内部实现其实是由 queue 组成。当 producer 把消息发送到某个 topic 时,默认是会消息发送到具体的 queue 上。由于一个 topic 可以有多个 queue,所以在性能比全局有序高得多。假设 queue 数是 n,理论上性能就是全局有序的 n 倍,当然 consumer 也要跟着增加才行。在实际情况中,这种局部有序消息是会比全局有序消息用的更多。

/**
* 有序消息
*/
public class OrderedProducer {
public static final String NAME_SERVER_ADDR = "192.168.32.128:9876"; public static void main(String[] args) throws MQClientException, InterruptedException, RemotingException, MQBrokerException, UnsupportedEncodingException {
// 1:创建生产者对象,并指定组名
DefaultMQProducer producer = new DefaultMQProducer("GROUP_TEST"); // 2:指定NameServer地址
producer.setNamesrvAddr(NAME_SERVER_ADDR); // 3:启动生产者
producer.start();
producer.setRetryTimesWhenSendAsyncFailed(0); // 设置异步发送失败重试次数,默认为2 // 4:定义消息队列选择器
MessageQueueSelector messageQueueSelector = new MessageQueueSelector() { /**
* 消息队列选择器,保证同一条业务数据的消息在同一个队列
* @param mqs topic中所有队列的集合
* @param msg 发送的消息
* @param arg 此参数是本示例中producer.send的第三个参数
* @return
*/
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
Integer id = (Integer) arg;
// id == 1001
int index = id % mqs.size();
// 分区顺序:同一个模值的消息在同一个队列中
return mqs.get(index); // 全局顺序:所有的消息都在同一个队列中
// return mqs.get(mqs.size() - 1);
}
}; String[] tags = new String[]{"TagA", "TagB", "TagC"}; List<Map> bizDatas = getBizDatas(); // 5:循环发送消息
for (int i = 0; i < bizDatas.size(); i++) {
Map bizData = bizDatas.get(i);
// keys:业务数据的ID,比如用户ID、订单编号等等
Message msg = new Message("TopicTest", tags[i % tags.length], "" + bizData.get("msgType"), bizData.toString().getBytes(RemotingHelper.DEFAULT_CHARSET));
// 发送有序消息
SendResult sendResult = producer.send(msg, messageQueueSelector, bizData.get("msgType")); System.out.printf("%s, body:%s%n", sendResult, bizData);
} // 6:关闭生产者
producer.shutdown();
} public static List<Map> getBizDatas() {
List<Map> orders = new ArrayList<Map>(); HashMap orderData = new HashMap();
orderData.put("msgType", 1001);
orderData.put("userId", "张三");
orderData.put("desc", "存钱1000");
orders.add(orderData); orderData = new HashMap();
orderData.put("msgType", 2001);
orderData.put("userId", "张三");
orderData.put("desc", "取钱1000");
orders.add(orderData); orderData = new HashMap();
orderData.put("msgType", 3001);
orderData.put("userId", "张三");
orderData.put("desc", "存钱2000");
orders.add(orderData); orderData = new HashMap();
orderData.put("msgType", 4001);
orderData.put("userId", "张三");
orderData.put("desc", "存钱3000");
orders.add(orderData); orderData = new HashMap();
orderData.put("msgType", 5001);
orderData.put("userId", "张三");
orderData.put("desc", "存钱4000");
orders.add(orderData); orderData = new HashMap();
orderData.put("msgType", 6001);
orderData.put("userId", "张三");
orderData.put("desc", "取钱5000");
orders.add(orderData); orderData = new HashMap();
orderData.put("msgType", 7001);
orderData.put("userId", "张三");
orderData.put("desc", "取钱6000");
orders.add(orderData); orderData = new HashMap();
orderData.put("msgType", 8001);
orderData.put("userId", "张三");
orderData.put("desc", "取钱2000");
orders.add(orderData); orderData = new HashMap();
orderData.put("msgType", 9001);
orderData.put("userId", "张三");
orderData.put("desc", "存钱9000");
orders.add(orderData); return orders;
}
}
/**
* 顺序消息消费者
*/
public class OrderedConsumer {
public static final String NAME_SERVER_ADDR = "192.168.32.128:9876";
public static void main(String[] args) throws Exception {
// 1. 创建消费者(Push)对象
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("GROUP_TEST"); // 2. 设置NameServer的地址,如果设置了环境变量NAMESRV_ADDR,可以省略此步
consumer.setNamesrvAddr(NAME_SERVER_ADDR); /**
* 设置Consumer第一次启动是从队列头部开始消费还是队列尾部开始消费<br>
* 如果非第一次启动,那么按照上次消费的位置继续消费
* 这里设置的是一个consumer的消费策略
* CONSUME_FROM_LAST_OFFSET 默认策略,从该队列最尾开始消费,即跳过历史消息
* CONSUME_FROM_FIRST_OFFSET 从队列最开始开始消费,即历史消息(还储存在broker的)全部消费一遍
* CONSUME_FROM_TIMESTAMP 从某个时间点开始消费,和setConsumeTimestamp()配合使用,默认是半个小时以前
*/
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); // 3. 订阅对应的主题和Tag String[] tags = new String[]{"TagA", "TagB", "TagC"};
consumer.subscribe("TopicTest", "TagA || TagB || TagC"); // 4. 注册消息接收到Broker消息后的处理接口
// 注1:普通消息消费 [[
// consumer.registerMessageListener(new MessageListenerConcurrently() {
// AtomicInteger count = new AtomicInteger(0);
//
// public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
// doBiz(list.get(0));
// return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
// }
// });
// ]] 注1:普通消息消费 // consumer
consumer.setMaxReconsumeTimes(-1);
// 延时 level 3 // 注2:顺序消息消费 [[
consumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeOrderlyContext context) {
context.setAutoCommit(true);
doBiz(msgs.get(0)); return ConsumeOrderlyStatus.SUCCESS;
}
}); // 5. 启动消费者(必须在注册完消息监听器后启动,否则会报错)
consumer.start(); System.out.println("已启动消费者");
} /**
* 模拟处理业务
*
* @param message
*/
public static void doBiz(Message message) {
try {
System.out.printf("线程:%-25s 接收到新消息 %s --- %s %n", Thread.currentThread().getName(), message.getTags(), new String(message.getBody(), RemotingHelper.DEFAULT_CHARSET));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}

rocketmq有序消息的(四)的更多相关文章

  1. rocketmq有序消息

    RocketMQ提供的顺序消费消息实现是使用的FIFO 先进先出算法 Producer消息发送 public class Producer { public static void main(Stri ...

  2. rocketmq总结(消息的顺序、重复、事务、消费模式)

    rocketmq总结(消息的顺序.重复.事务.消费模式) 参考: http://www.cnblogs.com/wxd0108/p/6038543.html https://www.cnblogs.c ...

  3. RocketMQ生产者消息篇

    系列文章 RocketMQ入门篇 RocketMQ生产者流程篇 RocketMQ生产者消息篇 前言 上文RocketMQ生产者流程篇中详细介绍了生产者发送消息的流程,本文将重点介绍发送消息的通信模式以 ...

  4. rocketmq总结(消息的高可用、中间件选型)

    rocketmq总结(消息的高可用.中间件选型) 参考: https://blog.csdn.net/meilong_whpu/article/details/76922456 http://blog ...

  5. RocketMQ详解(四)核心设计原理

    专题目录 RocketMQ详解(一)原理概览 RocketMQ详解(二)安装使用详解 RocketMQ详解(三)启动运行原理 RocketMQ详解(四)核心设计原理 RocketMQ详解(五)总结提高 ...

  6. RocketMQ事务消息学习及刨坑过程

    一.背景 MQ组件是系统架构里必不可少的一门利器,设计层面可以降低系统耦合度,高并发场景又可以起到削峰填谷的作用,从单体应用到集群部署方案,再到现在的微服务架构,MQ凭借其优秀的性能和高可靠性,得到了 ...

  7. RocketMQ 事务消息示例分析

    @ 目录 1 示例模式 2 安装与配置 RocketMQ 3 运行服务 3.1 启动 NameServer 3.2 启动 broker 4 生产者 4.1 事务监听器 4.2 事务消息生产者 5 消费 ...

  8. RocketMQ源码 — 九、 RocketMQ延时消息

    上一节消息重试里面提到了重试的消息可以被延时消费,其实除此之外,用户发送的消息也可以指定延时时间(更准确的说是延时等级),然后在指定延时时间之后投递消息,然后被consumer消费.阿里云的ons还支 ...

  9. 聊一聊顺序消息(RocketMQ顺序消息的实现机制)

    当我们说顺序时,我们在说什么? 日常思维中,顺序大部分情况会和时间关联起来,即时间的先后表示事件的顺序关系. 比如事件A发生在下午3点一刻,而事件B发生在下午4点,那么我们认为事件A发生在事件B之前, ...

随机推荐

  1. 关于selenium添加使用代理ip

    最近在爬某个网站,发现这个网站的反爬太厉害了,正常时候的访问有时候都会给你弹出来验证,验证你是不是蜘蛛,而且requests发的请求携带了请求头信息,cookie信息,代理ip,也能识别是爬虫,他应该 ...

  2. YbtOJ#791-子集最值【三维偏序】

    正题 题目链接:http://www.ybtoj.com.cn/contest/123/problem/1 题目大意 给出\(3\)个长度为\(n\)的排列\(A,B,C\).然后一个下标集合\(S\ ...

  3. 题解 「BZOJ3636」教义问答手册

    题目传送门 Description 作为泉岭精神的缔造者.信奉者.捍卫者.传承者,Pear决定印制一些教义问答手册,以满足泉岭精神日益增多的信徒.Pear收集了一些有关的诗选.语录,其中部分内容摘录在 ...

  4. 双系统升win11(grub启动问题修复与讲解)?!?

    起 最近win11不是出来了吗.(着急修复的可以直接跳到最后一步) 于是我就突发奇想给我半年没进去的windows升个级........ 于是我找到了我win11的升级包(从我一个同学哪儿) 工具都集 ...

  5. Longhorn 云原生容器分布式存储 - 故障排除指南

    内容来源于官方 Longhorn 1.1.2 英文技术手册. 系列 Longhorn 是什么? Longhorn 云原生容器分布式存储 - 设计架构和概念 Longhorn 云原生容器分布式存储 - ...

  6. Java(18)抽象类

    作者:季沐测试笔记 原文地址:https://www.cnblogs.com/testero/p/15201625.html 博客主页:https://www.cnblogs.com/testero ...

  7. 航胥:北航教务助手——Beta阶段发布声明

    下载地址在文章末尾! 这里是"航胥",一款更想要了解你的北航教务助手 Beta阶段,我们进化了! Beta阶段我们的新功能有: 课程评价功能 所有用户选过的课程都会在课程评价页面进 ...

  8. [对对子队]Scrum Meeting 博客汇总

    对对子队 博客目录 一.Scrum Meeting 1. Alpha Scrum Meeting 1(会议记录4.10) Scrum Meeting 2(会议记录4.11) Scrum Meeting ...

  9. OO_JAVA_JML系列第三次作业__架构之谈

    OO_JAVA_JML系列第三次作业 ## ----架构之谈 目录 OO_JAVA_JML系列第三次作业 出发点 操作的可分离性 操作本身的多样性 实现手段:表驱动编程 储存 注册 出发点 操作的可分 ...

  10. 基于jpa的specification实现动态查询

    spring data jpa为我们实现简单的crud操作提供了极大的方便.但大部分情况下,系统中都存在大量的动态查询操作,这个时候就可以借助spring data jpa的 Specificatio ...