RocketMQ(一)介绍

1.RocketMq

RocketMQ的前身是Metaq,当Metaq3.0发布时,产品名称改为RocketMQ,有以下特点: 
1. 能够保证严格的消息顺序

2. 提供丰富的消息拉取模式

3. 高效的订阅者水平扩展能力

4. 实时的消息订阅机制

5. 亿级消息堆积能力


2.核心原理

2.1. 数据结构

这里写图片描述

这里写图片描述

(1)所有数据单独储存到commit Log ,完全顺序写,随机读

(2)对最终用户展现的队列实际只储存消息在Commit Log 的位置信息,并且串行方式刷盘

(3)按照MessageId查询消息

这里写图片描述

(4)根据查询的key的hashcode%slotNum得到具体的槽位置

这里写图片描述

(5)根据slotValue(slot对应位置的值)查找到索引项列表的最后一项

(6)遍历索引项列表返回查询时间范围内的结果集

2.2. 刷盘策略

rocketmq中的所有消息都是持久化的,先写入系统pagecache,然后刷盘,可以保证内存与磁盘都有一份数据,访问时,可以直接从内存读取

2.3. 内存机制

这里写图片描述

2.4. 工作模式

这里写图片描述

集群方式 运维特点 消息可靠性(master宕机情况) 服务可用性(master宕机情况) 其他特点 备注
单Master 结构简单,扩容方便,机器要求低 同步刷盘消息一条都不会丢 整体可用
未被消费的消息无法取得,影响实时性
性能最高 适合消息可靠性最高、实时性低的需求
多Master   异步有毫秒级丢失
同步双写不丢失
差评,主备不能自动切换,且备机只能读不能写,会造成服务整体不可写。   不考虑,除非自己提供主从切换的方案
Master-Slave(异步复制) 结构复杂,扩容方便 故障时会丢失消息 整体可用,实时性影响毫秒级别
该组服务只能读不能写
性能很高 适合消息可靠性中等,实时性中等的要求
Master-Slave(同步双写) 结构复杂,扩容方便 不丢消息 整体可用,不影响实时性
该组服务只能读不能写
性能比异步低10%,所以实时性也并不比异步方式太高 适合消息可靠性略高,实时性中等、性能要求不高的需求
tip

第四种的官方介绍上,比第三种多说了一句:“不支持主从自动切换”。这句话让我很恐慌,因为第三种也是不支持的,干嘛第四种偏偏多说这一句,难道可用性上比第三种差?

于是做了实验,证明第三种和第四种可用性是一模一样的。那么不支持主从切换是什么意思?推断编写者是这个意图:

因为是主从双写的,所以数据一致性非常高,那么master挂了之后,slave本是可以立刻切换为主的,这一点与异步复制不一样。异步复制并没有这么高的一致性,所以这一句话并不是提醒,而是一个后续功能的备注,可以在双写的架构上继续发展出自动主从切换的功能。

3. 环境安装

3.1. RocketMq安装

https://github.com/alibaba/RocketMQ/releases下载3.2.6,解压

3.2. 环境变量配置
export JAVA_HOME=/usr/local/jdk1.7.0_79
export ROCKETMQ_HOME=/usr/local/alibaba-rocketmq
export CLASSPATH=/usr/local/jdk1.7.0_79/lib  
export PATH=$JAVA_HOME/bin:$PATH:$ROCKETMQ_HOME/bin
   
   
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

4. 测试网络拓扑

这里写图片描述

因为手里没有其他服务器,105那台缺少一个slave,在同步双写模式下,发送消息会返回 
SLAVE_NOT_AVAILABLE,不过消息已经发送成功,只是slave没有写成功。


5. 启停操作

这里只给出一个基本的示例,各个模式的启停在本文最后的参考文献中会有详细的说明。这里不再赘述。

新建日志文件夹

cd /usr/local/alibaba-rocketmq
mkdir log
touch log/ng.log
touch log/ng-error.log
touch log/mq.log
    
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5
  • 启动nameserver
nohup sh mqnamesrv 1>$ROCKETMQ_HOME/log/ng.log 2>$ROCKETMQ_HOME/log/ng-error.log &
   
   
  • 1
  • 1
  • 验证nameserver是否启动
$tail -f $ROCKETMQ_HOME/log/ng.log
The Name Server boot success.
   
   
  • 1
  • 2
  • 1
  • 2
  • 停止nameServer
sh mqshutdown namesrv
The mqnamesrv(12248) is running...
Send shutdown request to mqnamesrv(12248) OK
   
   
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3
  • 启动broker(单master)(多master,多master+slave)对应的(异步复制,同步双写)
nohup sh mqbroker -n 192.168.1.101:9876 -c $ROCKETMQ_HOME/conf/2m-noslave/broker-a.properties >$ROCKETMQ_HOME/log/mq.log &
   
   
  • 1
  • 1
  • 验证mqbroker是否启动
tail -f $ROCKETMQ_HOME/log/mq.log

Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=128m; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=320m; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: UseCMSCompactAtFullCollection is deprecated and will likely be removed in a future release.
The broker[e010125001186.bja, 10.125.1.186:10911] boot success. and name server is 10.125.1.186:9876
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 停止broker
sh mqshutdown broker
The mqbroker(13634) is running...
Send shutdown request to mqbroker(13634) OK
   
   
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

6. 运维指令

  • 查看集群情况
./mqadmin clusterList -n 127.0.0.1:9876
   
   
  • 1
  • 1
  • 查看broker状态
./mqadmin brokerStatus -n 127.0.0.1:9876 -b 192.168.146.105:10911
   
   
  • 1
  • 1
  • 查看topic列表
./mqadmin topicList -n 127.0.0.1:9876
   
   
  • 1
  • 1
  • 查看topic状态
./mqadmin topicStatus -n 127.0.0.1:9876 -t PushTopic
   
   
  • 1
  • 1
  • 查看topic路由
./mqadmin topicRoute  -n 127.0.0.1:9876 -t PushTopic
   
   
  • 1
  • 1

7. 基本测试

基本测试采用Java直接编码的方式生产和消费消息,例子来源于参考文献的《RocketMQ开发教程》。

  • Producer
package com.somnus.rocketmq;

import java.util.concurrent.TimeUnit;

import com.alibaba.rocketmq.client.exception.MQClientException;
import com.alibaba.rocketmq.client.producer.DefaultMQProducer;
import com.alibaba.rocketmq.client.producer.SendResult;
import com.alibaba.rocketmq.common.message.Message;

public class Producer {

    public static void main(String[] args) throws MQClientException,
            InterruptedException {
        /**
         * 一个应用创建一个Producer,由应用来维护此对象,可以设置为全局对象或者单例<br>
         * 注意:ProducerGroupName需要由应用来保证唯一<br>
         * ProducerGroup这个概念发送普通的消息时,作用不大,但是发送分布式事务消息时,比较关键,
         * 因为服务器会回查这个Group下的任意一个Producer
         */
        DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName");
        producer.setNamesrvAddr("172.16.235.77:9876;172.16.235.78:9876");
        producer.setInstanceName("Producer");

        /**
         * Producer对象在使用之前必须要调用start初始化,初始化一次即可<br>
         * 注意:切记不可以在每次发送消息时,都调用start方法
         */
        producer.start();

        /**
         * 下面这段代码表明一个Producer对象可以发送多个topic,多个tag的消息。
         * 注意:send方法是同步调用,只要不抛异常就标识成功。但是发送成功也可会有多种状态,<br>
         * 例如消息写入Master成功,但是Slave不成功,这种情况消息属于成功,但是对于个别应用如果对消息可靠性要求极高,<br>
         * 需要对这种情况做处理。另外,消息可能会存在发送失败的情况,失败重试由应用来处理。
         */
        for (int i = 0; i < 10; i++) {
            try {
                {
                    Message msg = new Message("TopicTest11",// topic
                            "TagA",                         // tag
                            "OrderID001",                   // key
                            ("Hello MetaQ").getBytes());    // body
                    SendResult sendResult = producer.send(msg);
                    System.out.println(sendResult);
                }

                {
                    Message msg = new Message("TopicTest12",// topic
                            "TagB",                         // tag
                            "OrderID002",                   // key
                            ("Hello MetaQ").getBytes());    // body
                    SendResult sendResult = producer.send(msg);
                    System.out.println(sendResult);
                }

                {
                    Message msg = new Message("TopicTest13",// topic
                            "TagC",                         // tag
                            "OrderID003",                   // key
                            ("Hello MetaQ").getBytes());    // body
                    SendResult sendResult = producer.send(msg);
                    System.out.println(sendResult);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            TimeUnit.MILLISECONDS.sleep(1000);
        }

        /**
         * 应用退出时,要调用shutdown来清理资源,关闭网络连接,从MetaQ服务器上注销自己
         * 注意:我们建议应用在JBOSS、Tomcat等容器的退出钩子里调用shutdown方法
         */
        producer.shutdown();
    }
}

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • Consumer
package com.somnus.rocketmq;

import java.util.List;

import com.alibaba.rocketmq.client.consumer.DefaultMQPushConsumer;  
import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;  
import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;  
import com.alibaba.rocketmq.client.consumer.listener.MessageListenerConcurrently;  
import com.alibaba.rocketmq.client.exception.MQClientException;
import com.alibaba.rocketmq.common.message.MessageExt;  

public class Consumer {

    /**
     * 当前例子是PushConsumer用法,使用方式给用户感觉是消息从RocketMQ服务器推到了应用客户端。<br>
     * 但是实际PushConsumer内部是使用长轮询Pull方式从MetaQ服务器拉消息,然后再回调用户Listener方法<br>
     */
    public static void main(String[] args) throws InterruptedException,
            MQClientException {
        /**
         * Consumer组名,多个Consumer如果属于一个应用,订阅同样的消息,且消费逻辑一致,则应该将它们归为同一组
         * 一个应用创建一个Consumer,由应用来维护此对象,可以设置为全局对象或者单例<br>
         * 注意:ConsumerGroupName需要由应用来保证唯一
         */
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("ConsumerGroupName");
        consumer.setNamesrvAddr("172.16.235.77:9876;172.16.235.78:9876");
        consumer.setInstanceName("Consumber");

        /**
         * 订阅指定topic下tags分别等于TagA或TagB或TagC
         */
        consumer.subscribe("TopicTest11", "TagA || TagB || TagC");
        /**
         * 订阅指定topic下所有消息<br>
         * 注意:一个consumer对象可以订阅多个topic
         */
        /*consumer.subscribe("TopicTest12", "*");*/

        consumer.registerMessageListener(new MessageListenerConcurrently() {

            /**
             * 默认msgs里只有一条消息,可以通过设置consumeMessageBatchMaxSize参数来批量接收消息
             * consumeThreadMin:消费线程池数量 默认最小值10
             * consumeThreadMax:消费线程池数量 默认最大值20
             */
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(
                    List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                System.out.println(Thread.currentThread().getName()
                        + " Receive New Messages: " + msgs.size());

                MessageExt msg = msgs.get(0);
                if (msg.getTopic().equals("TopicTest11")) {
                    // 执行TopicTest1的消费逻辑
                    if (msg.getTags() != null 
                            && msg.getTags().equals("TagA")) {
                        // 执行TagA的消费
                        System.out.println("TopicTest11------>"+new String(msg.getBody()));
                    } else if (msg.getTags() != null 
                            && msg.getTags().equals("TagB")) {
                        // 执行TagC的消费
                    } else if (msg.getTags() != null
                            && msg.getTags().equals("TagC")) {
                        // 执行TagD的消费
                    }
                } else if (msg.getTopic().equals("TopicTest12")) {
                    /*System.out.println("TopicTest2------>"+new String(msg.getBody()));*/
                }

                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });

        /**
         * Consumer对象在使用之前必须要调用start初始化,初始化一次即可<br>
         */
        consumer.start();

        System.out.println("Consumer Started.");
    }
}

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82

运行结果: 
这里写图片描述

这里写图片描述


8.宕机实验

这里写图片描述


9.参考文献

1) 《RocketMq入门(上)》 
2) 《RocketMq入门(下)》 
3) 《Rokectmq开发教程》 
4) 《阿里Rocketmq Quict Start》 
5) 《RocketMQ与Kafka对比(18项差异)》 
6) 《RocketMq命令整理》 
7) 《RocketMq原理简介》

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RocketMQ是一个分布式消息中间件,由阿里巴巴开源的。它提供了可靠的、可伸缩的、高吞吐量的消息发布/订阅服务。RocketMQ具有以下特点: 1. 分布式架构:RocketMQ采用了主从复制的架构,通过Broker和Name Server构建分布式消息队列集群。这种架构使得RocketMQ具备高可用性和可伸缩性。 2. 消息模型:RocketMQ支持发布/订阅和点对点的消息模型。发布/订阅模型中,消息生产者将消息发布到一个或多个主题,而消息消费者通过订阅主题来接收消息。点对点模型中,消息生产者将消息发送到一个队列中,而消息消费者从队列中消费消息。 3. 消息顺序保证:RocketMQ提供了严格的消息顺序保证。在发送端,可以选择同步发送或异步发送消息,保证发送顺序。在消费端,可以通过设置顺序消费模式来保证消息消费的顺序性。 4. 高吞吐量:RocketMQ是为了处理大规模数据流设计的,具有很高的吞吐量和低延迟。它支持批量消息发送和消费,能够快速处理大量的消息。 5. 消息过滤:RocketMQ支持根据消息的属性进行过滤,只有满足条件的消息才会被消费。这样可以提高消息消费的效率。 6. 消息可靠性:RocketMQ提供了多种机制来保证消息的可靠性。它采用了主从复制架构来实现消息的持久化和高可用性。此外,RocketMQ还支持消息的重试、死信队列等机制,保证了消息的可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值