源码 rocketmq/docs/cn at master · apache/rocketmq · GitHub
一、 初始RocketMQ
1. 介绍
- RocketMQ是一款分布式、队列模型的消息中间件,是阿里巴巴集团自主研发的专业消息中间件,借鉴参考了JMS规范的MQ实现,更参考了优秀的开源消息中间件KAFKA,实现了业务消峰、分布式事务的优秀框架。
- 其底层代码编写清晰优秀,采用Netty NIO框架进行数据通信
- 摒弃了Zookeeper,内部使用更轻量级的NameServer进行网络路由,提高服务性能,并且支持消息失败重试机制。
- 天然支持集群模型,消费者负载均衡、水平扩展能力,支持广播模式和集群模式。
- 采用零拷贝的原理、顺序写盘、支持亿级消息堆积能力。
- 提供丰富的消息机制,如顺序消息、事务消息等。
- 目前 RocketMQ 已经成为Apache 顶级项目 。 在阿里内部, RocketMQ很好地服务了集团大大小小上千个应用,在每年的双十一当天,更有不可思议的万亿级消息通过 RocketMQ 流转(在 2017 年的双十一当天,整个阿里巴巴集团通过 RocketMQ 流转的线上消息达到了 万亿级,峰值 TPS 达到 5600 万),在阿里中台策略上发挥着举足轻重的作用 。此外, RocketMQ 是使用 Java语言开发的,比起 Kafka 的 Scala语言和 RabbitMQ 的 Erlang 语 言,更容易找 到技术人员进行定制开发 。
2.配置环境
本文讲解下载并安装单机版RocketMq,win10 环境。
要求:
- Linux/Unix/Mac/(本博客 是基于Windows 开发)
- 64bit JDK 1.8+;
- Maven 3.2.x
3.功能概括图
多协议接入
- 支持 MQTT 协议:支持主动推送模型,多级 Topic 模型支持一次触达 1000万+ 终端,可广泛应用于物联网和社交即时通信场景。
- 支持 TCP 协议:区别于 HTTP 简单的接入方式,提供更为专业、可靠、稳定的 TCP 协议的 SDK 接入。
特色功能
- 事务消息:实现类似 X/Open XA 的分布事务功能,以达到事务最终一致性状态。
- 定时(延时)消息:允许消息生产者指定消息进行定时(延时)投递,最长支持40天。
- 大消息:目前默认支持最大 256KB 消息,华北2 地域支持最大 4MB 消息。
- 消息轨迹:通过消息轨迹,用户能清晰定位消息从发布者发出,经由 MQ 服务端,投递给消息订阅者的完整链路,方便定位排查问题。
- 广播消费:允许一个 Consumer ID 所标识的所有 Consumer 都会各自消费某条消息一次。
- 顺序消息:允许消息消费者按照消息发送的顺序对消息进行消费。
- 重置消费进度:根据时间重置消费进度,允许用户进行消息回溯或者丢弃堆积消息
消息收发模型:
二、 MQ基本概念:
1. 九大属性
- Message:消息,消息队列中信息传递的载体。
- Message ID:消息的全局唯一标识,由 MQ 系统自动生成,唯一标识某条消息。
- Message Key:消息的业务标识,由**消息生产者(Producer)设置**,唯一标识某个业务逻辑。三级消息类型
- Topic:消息主题,一级消息类型,通过 Topic 对消息进行分类。
- Tag:消息标签,二级消息类型,用来进一步区分某个 Topic 下的消息分类。
- Producer:消息生产者,也称为消息发布者,负责生产并发送消息。
- Producer ID:一类 Producer 的标识,这类 Producer 通常生产并发送一类消息,且发送逻辑一致。
- Consumer:消息消费者,也称为消息订阅者,负责接收并消费消息。
- Consumer ID:一类 Consumer 的标识,这类 Consumer 通常接收并消费一类消息,且消费逻辑一致。
2. RocketMQ的4个组件:
分别是nameserver、broker、producer和consumer。
- nameserver: 存储当前集群**所有Brokers信息、Topic跟Broker的对应关系**。
- Broker: 集群最核心的模块,主要负责Topic消息存储、消费者的消费位点管理(消费进度)。
- Producer: 消息生产者,每个生产者都有一个ID(编号),多个生产者实例可以共用同一个ID。同一个ID下所有实例组成一个生产者集群。
- Consumer: 消息消费者,每个订阅者也有一个ID(编号),多个消费者实例可以共用同一个ID。同一个ID下所有实例组成一个消费者集群。
3. 集群部署结构
4. 工作流程
- 启动Nameserver,Nameserver起来后监听端口,等待Broker、Produer、Consumer连上来,相当于一个路由控制中心。
- Broker启动,跟所有的Nameserver保持长连接,定时发送心跳包。心跳包中包含当前Broker信息(IP+端口等)以及存储所有topic信息。注册成功后,Nameserver集群中就有Topic跟Broker的映射关系。
- 收发消息前,先创建topic,创建topic时需要指定该topic要存储在哪些Broker上。也可以在发送消息时自动创建Topic。
- Producer发送消息,启动时先跟Namesrv集群中的其中一台建立长连接,并从Nameserver中获取当前发送的Topic存在哪些Broker上,然后跟对应的Broker建立长连接,直接向Broker发消息。
- Consumer跟Producer类似。跟其中一台Nameserver建立长连接,获取当前订阅Topic存在哪些Broker上,然后直接跟Broker建立连接通道,开始消费消息。
三、 下载与安装
1. 从官网下载rocketMq
http://rocketmq.apache.org/
下载如图,最新版
2. 把文件解压出来,如下图所示
四、 启动与测试
1. 启动nameserver
在window(也就是用管理员方式运行)上启动:用cmd进入bin目录下执行启动命令
start mqnamesrv.cmd
会弹出一个框,表示启动成功。
Bug1:赶紧去配置环境变量。
然后重新执行启动命令。弹出 成功的框框:
2. 启动 broker
然后在bin目录下再执行
start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true
//autoCreateTopicEnable=true 表示默认创建topic
回车后会出现下面的bug2。 bug2:修改bin目录下的runbroker.cmd文件。
最后一行中,CLASSPATH 加上双引号,变为“CLASSPATH”
然后重新执行启动命令。弹出 成功的框框:
总共三个界面
扩展:在linux中的启动命令:
nohup sh bin/mqnamesrv &
nohup sh bin/mqbroker -n 127.0.0.1:9876 &
五、 rocketMq可视化视图 下载与安装
1. 下载项目,自己打包
项目地址:https://github.com/apache/rocketmq-externals
2. 把下载的项目解压出来,找到rocketmq-console项目
修改rocketmq-console\src\main\resources文件下的application.properties文件
如下:
server.contextPath=
server.port=8088
#spring.application.index=true
spring.application.name=rocketmq-console
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true
logging.config=classpath:logback.xml
#if this value is empty,use env value rocketmq.config.namesrvAddr NAMESRV_ADDR | now, you can set it in ops page.default localhost:9876
rocketmq.config.namesrvAddr=127.0.0.1:9876
#if you use rocketmq version < 3.5.8, rocketmq.config.isVIPChannel should be false.default true
rocketmq.config.isVIPChannel=false
#rocketmq-console's data path:dashboard/monitor
rocketmq.config.dataPath=/tmp/rocketmq-console/data
#set it false if you don't want use dashboard.default true
rocketmq.config.enableDashBoardCollect=true
#set the message track trace topic if you don't want use the default one
rocketmq.config.msgTrackTopicName=
3. 打包项目
进入 \rocketmq-externals\rocketmq-console 文件夹,执行maven命令。
mvn clean package -Dmaven.test.skip=true
跳过测试,直接打包。
打包成功
4. 启动 打包好的项目
首先启动rocketMq的服务
项目本身就是springboot项目,直接启动:
D:\mq\rocketmq-externals\rocketmq-console\target>java -jar rocketmq-console-ng-1.0.0.jar &
命令:java -jar rocketmq-console-ng-1.0.0.jar –server.port=12581
注意:不指定端口的话,默认8080(避免与Tomcat冲突,最好指定)但是我在application.propertise 配置文件中,已经配置了端口,则这里不用加参数
5. 启动后的页面
一共打开三个服务,如图:
在浏览器输入:127.0.0.1:9876
六. 测试:用Java代码发送和接受信息
1. 测试
生产者:
package feng.rocketmq.quickstart;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingException;import java.io.UnsupportedEncodingException;
public class ProducerTest {
public static void main(String[] args) throws MQClientException, UnsupportedEncodingException, RemotingException, InterruptedException, MQBrokerException {
// while (true){
// 1. 创建 DefaultMQProducer
DefaultMQProducer mqProducer = new DefaultMQProducer("ProducerTest");
// 2. 设置 namesrv 地址
mqProducer.setNamesrvAddr("127.0.0.1:9876");
// 3. 开启 DefaultMQProducer
mqProducer.start();
// 4. 创建消息 Message String topic, String tags, String keys, byte[] body
Message message = new Message("TopicTest1", // 主题
"TagA", // 主要用于消息过滤
"key1", // 消息的唯一值
"HELLO WORLD".getBytes(RemotingHelper.DEFAULT_CHARSET));
// 5. 发送消息
SendResult sendResult = mqProducer.send(message);
System.out.printf(String.valueOf(sendResult));
// 6. 关闭 DefaultMQProducer
mqProducer.shutdown();
// }
}
}
消费者:
package feng.rocketmq.quickstart; import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.common.message.Message; import org.apache.rocketmq.common.message.MessageExt; import org.apache.rocketmq.remoting.common.RemotingHelper; import java.io.UnsupportedEncodingException; import java.util.List; public class ConsumerTest { public static void main(String[] args) throws MQClientException { // 1. 创建 DefaultMQPushConsumer DefaultMQPushConsumer mqPushConsumer = new DefaultMQPushConsumer("ConsumerTest"); // 2. 设置 namesrv mqPushConsumer.setNamesrvAddr("127.0.0.1:9876"); // 3. 设置 subscribe ,这里是要读取的主题信息 mqPushConsumer.subscribe("TopicTest1", // 指定要消费的主题 "*" // 过滤规则 ); // 设置 消息拉取 最大数 mqPushConsumer.setConsumeMessageBatchMaxSize(2); //设置Consumer第一次启动是从队列头部开始消费还是队列尾部开始消费 //如果非第一次启动,那么按照上次消费的位置继续消费 mqPushConsumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); // 字面意思:消费线程最小值 mqPushConsumer.setConsumeThreadMin(1); // 字面意思:消费线程最大值 mqPushConsumer.setConsumeThreadMax(1); //设置消费模型,集群还是广播,默认为集群 mqPushConsumer.setMessageModel(MessageModel.CLUSTERING); // 设置 mqPushConsumer.setPullThresholdForQueue(1); // 4. 创新消息监听 MessageListener 匿名内部类方式实现 mqPushConsumer.registerMessageListener(new MessageListenerConcurrently(){ @Override public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) { // 5. 获取消息信息 // 迭代 消息信息 for (MessageExt msg: list){ if (msg.getTopic().equals("TopicTest1")){ if (msg.getTags() != null && msg.getTags().equals("TagA")){ try { // 获取主题 String topic = msg.getTopic(); // 获取标签 String tags = msg.getTags(); // 获取信息 byte[] body = msg.getBody(); // String result = new String(body); String result = null; result = new String(body, RemotingHelper.DEFAULT_CHARSET); System.out.println("Consumer消费信息---topic:"+topic+",TagA:"+tags+",result"+result); } catch (UnsupportedEncodingException e) { e.printStackTrace(); //消息重试 消息消费失败之后,会重新再发一次。 return ConsumeConcurrentlyStatus.RECONSUME_LATER; } } } } // 6. 返回消息读取状态 // 消息消费完成 return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; } }); // 开启Consumer mqPushConsumer.start(); // lambda // mqPushConsumer.registerMessageListener(); } }
2. 小总结
到目前为止,开启的服务有
1、 namesrv
2、 broker
3、 rockermq-console 可视化工具
还可以在可视化工具中查看**消息和消费者**。
七、 RocketMQ顺序消息
消息有序指的是可以**按照消息的发送顺序来消费**。 RocketMQ可以严格的保证消息有序。但这个顺序,不是全局顺序,只是分区(queue)顺序。要全局顺序只能一个分区。
如何保证顺序?
在MQ的模型中,顺序需要由3个阶段去保障:
1.消息被发送时保持顺序
2.消息被存储时保持和发送的顺序一致
3.消息被消费时保持和存储的顺序一致
发送时保持顺序意味着对于有顺序要求的消息,用户应该在同一个线程中采用同步的方式发送。存储保持和发送的顺序一致则要求在同一线程中被发送出来的消息A和B,存储时在空间上A一定在B之前。而消费保持和存储一致则要求消息A、B到达Consumer之后必须按照先A后B的顺序被处理。
--------------------------------------------------------
参考