一、概览

1. 简介:

   Apache RocketMQ是一个采用Java语言开发的分布式的消息系统,由阿里巴巴团队开发,与2016年底贡献给 Apache,成为了Apache的一个顶级项目。

   在阿里内部,RocketMQ 很好地服务了 集 团大大小小上千个应用,在每年的双十一当天,更有不可思议的万亿级 消息通过 RocketMQ 流转(在 2017 年的双十一当天,整个阿里巴巴集团通过 RocketMQ 流转的线上消息达到了 万 亿级,峰值 TPS 达到 5600 万),在阿里大中台策略上发挥着举足轻重的作用 。

官网地址:http://rocketmq.apache.org/

2. 核心概念:

rocketmq docker compose 单机 rocketmq apache_分布式

  • Producer
  • 消息生产者,负责产生消息,一般由业务系统负责生产消息。
  • Producer Group:生产者组,简单来说就是多个发送同一类消息的生产者称之为一个生产者组。在这里可以不用关心,只要知道有这么一个概念即可。
  • Consumer
  • 消息消费者,负责消费消息,一般由后台系统异步消费消息。
  • Consumer Group:消费者组,简单来说就是多个消费同一类消息的消费者称之为一个消费者组。在这里可以不用关心,只要知道有这么一个概念即可。
  • Push Consumer:broker主动推送消息给consumer
  • Pull Consumer:consumer主动去broker拉取消息
  • NameServer
  • 集群架构中的组织协调员
  • 负责收集Broker的工作情况
  • 不负责处理消息
  • Broker
  • 负责消息的发送、接收、高可用等(真正干活的)
  • 需要定时发送自身状况到NameServer(默认每10秒发送一次),超过2分钟未发送就会认为该Broker失效。
  • Topic
  • 区分不同类型的消息,如user、order
  • Message Queue
  • 消息队列,储存消息

二、安装

1. 非docker方式安装:

先去官网下载RocketMQ压缩包

下载地址:https://www.apache.org/dyn/closer.cgipath=rocketmq/4.3.2/rocketmq-all-4.3.2-bin-release.zip

## 1.解压 RocketMQ的压缩包
cd /RocketMQ
unzip rocketmq-all-4.3.2-bin-release.zip

## 2.调整默认的内存大小(因为 RocketMQ默认占用的内存太大,故需要调整)
cd bin/ vim runserver.sh JAVA_OPT="${JAVA_OPT} -server -Xms128m -Xmx128m -Xmn128m -XX:MetaspaceSize=128m XX:MaxMetaspaceSize=128m" 
cd bin/ vim runbroker.sh JAVA_OPT="${JAVA_OPT} -server -Xms128m -Xmx128m -Xmn128m" 

## 3.启动 NameServer
##  The Name Server boot success. serializeType=JSON 看到这个表示已经提供成功 
bin/mqnamesrv 

## 4.启动 broker
bin/mqbroker -n 172.16.185.55:9876  #-n 指定nameserver地址和端口

## 5.测试发送消息
export NAMESRV_ADDR=127.0.0.1:9876 cd bin sh tools.sh org.apache.rocketmq.example.quickstart.Producer 
# 测试结果成功,可以正常发送消息
SendResult [sendStatus=SEND_OK, msgId=AC110001473C7D4991AD336AEA5703E0, offsetMsgId=AC11000100002A9F00000000000E8580, messageQueue=MessageQueue [topic=TopicTest, brokerName=itcast, queueId=3], queueOffset=1323] SendResult [sendStatus=SEND_OK, msgId=AC110001473C7D4991AD336AEA5903E1, offsetMsgId=AC11000100002A9F00000000000E8634, messageQueue=MessageQueue [topic=TopicTest, brokerName=itcast, queueId=0], queueOffset=1323] SendResult [sendStatus=SEND_OK, msgId=AC110001473C7D4991AD336AEA5F03E2, offsetMsgId=AC11000100002A9F00000000000E86E8, messageQueue=MessageQueue [topic=TopicTest, brokerName=itcast, queueId=1], queueOffset=1323] SendResult [sendStatus=SEND_OK, msgId=AC110001473C7D4991AD336AEA6103E3, offsetMsgId=AC11000100002A9F00000000000E879C, messageQueue=MessageQueue [topic=TopicTest, brokerName=itcast, queueId=2], queueOffset=1323]

## 6.测试接收消息
sh tools.sh org.apache.rocketmq.example.quickstart.Consumer
# 测试结果成功,接收消息正常
ConsumeMessageThread_7 Receive New Messages: [MessageExt [queueId=2, storeSize=180, queueOffset=1322, sysFlag=0, bornTimestamp=1544456244818, bornHost=/172.16.55.185:33702, storeTimestamp=1544456244819, storeHost=/172.17.0.1:10911, msgId=AC11000100002A9F00000000000E84CC, commitLogOffset=951500, bodyCRC=684865321, reconsumeTimes=0, preparedTransactionOffset=0, toString()=Message{topic='TopicTest', flag=0, properties={MIN_OFFSET=0, MAX_OFFSET=1325, CONSUME_START_TIME=1544456445397, UNIQ_KEY=AC110001473C7D4991AD336AEA5203DF, WAIT=true, TAGS=TagA}, body=[72, 101, 108, 108, 111, 32, 82, 111, 99, 107, 101, 116, 77, 81, 32, 57, 57, 49], transactionId='null'}]] ConsumeMessageThread_6 Receive New Messages: [MessageExt [queueId=2, storeSize=180, queueOffset=1323, sysFlag=0, bornTimestamp=1544456244833, bornHost=/172.16.55.185:33702, storeTimestamp=1544456244835, storeHost=/172.17.0.1:10911, msgId=AC11000100002A9F00000000000E879C, commitLogOffset=952220, bodyCRC=801108784, reconsumeTimes=0, preparedTransactionOffset=0, toString()=Message{topic='TopicTest', flag=0, properties={MIN_OFFSET=0, MAX_OFFSET=1325, CONSUME_START_TIME=1544456445397, UNIQ_KEY=AC110001473C7D4991AD336AEA6103E3, WAIT=true, TAGS=TagA}, body=[72, 101, 108, 108, 111, 32, 82, 111, 99, 107, 101, 116, 77, 81, 32, 57, 57, 53], transactionId='null'}]]



2. docker方式安装:
## 1.拉取镜像 
docker pull foxiswho/rocketmq:server-4.3.2 
docker pull foxiswho/rocketmq:broker-4.3.2
 
## 2.创建nameserver容器 
docker create -p 9876:9876 --name rmqserver -e "JAVA_OPT_EXT=-server -Xms128m -Xmx128m -Xmn128m" -e "JAVA_OPTS=-Duser.home=/opt"  -v /haoke/rmq/rmqserver/logs:/opt/logs  -v /haoke/rmq/rmqserver/store:/opt/store foxiswho/rocketmq:server-4.3.2
 
## 3.创建broker容器 
docker create -p 10911:10911 -p 10909:10909 --name rmqbroker  -e "JAVA_OPTS=-Duser.home=/opt"  -e "JAVA_OPT_EXT=-server -Xms128m -Xmx128m -Xmn128m" -v /haoke/rmq/rmqbroker/conf/broker.conf:/etc/rocketmq/broker.conf  -v /haoke/rmq/rmqbroker/logs:/opt/logs  -v /haoke/rmq/rmqbroker/store:/opt/store  foxiswho/rocketmq:broker-4.3.2
 
## 4.启动容器 
docker start rmqserver rmqbroker
 
## 5.停止、删除容器 
docker stop rmqbroker rmqserver 
docker rm rmqbroker rmqserver



三、重要特性

1. 分布式事务消息:

① 什么是事务?

  聊一下什么是事务,最经典的例子就是转账操作,皮卡丘转账给杰尼龟1000元操作如下:

  • 皮卡丘发起转账请求,皮卡丘账户减少1000元
  • 杰尼龟账户增加1000元

  如果皮卡丘的账户减少了1000元之后,出现了故障(如网络故障),那么需要回滚操作,再把1000元加回到皮卡丘的账户上,这就是事务。

② RocketMQ事务原理

  • Half Message(半消息)

二次确认,此时这条消息并不能被投递出去,处于这种状态的消息被称为“半消息”。

  • Message Status Check(消息状态回查)

二次确认,这时MQ服务端需要主动向生产者询问该条消息的最终状态(commit 或 rollback),该过程被称为“消息状态回查”。



③ 完整执行流程

rocketmq docker compose 单机 rocketmq apache_后端_02

  • 生产者第一次发消息给MQ服务端
  • MQ服务端向生产者确认接收消息成功(此时处于半消息状态)
  • 生产者执行本地事务
  • MQ服务端向生产者发起二次确认(提交或回滚)。若MQ服务端收到commit状态,则将半消息标记为可投递,消费者可消费该消息;若收到rollback状态,则删除该半消息,消费者将无法消费该消息。
  • 若生产者长时间未向MQ服务端发送二次确认(有可能是断网或者服务挂了等因素),MQ服务端将向生产者发起消息状态回查
  • 生产者收到消息回查后,需要获取本地事务执行的最终结果
  • 生产者根据获取的最终结果向MQ服务端发送二次确认
2. Push Consumer 和 Pull Consumer:

   在RocketMQ中,消费者存在两种模式,一种是Push模式,另一种是Pull模式。
   但在具体实现中,Push模式和Pull模式都是采用消费者主动拉取消息的方式,即consumer轮询broker拉取消息。

  • 区别
      Push方式,consumer把轮询的过程封装了,并注册MessageListener监听器,监听到消息后,唤醒MessageListener中的consumeMessage方法来消费,感觉消息是被推送过来的。
      Pull方式,消费消息的过程需要消费者自己定义(①拿到指定Topic的消息队列的集合,②遍历消息队列的集合,③针对每个消息队列消费消息)
    疑问:既然是采用pull方式实现,RocketMQ如何保证消息的实时性呢?   答:长轮询


3. 长轮询:

rocketmq docker compose 单机 rocketmq apache_后端_03


    客户端像传统轮询一样,向服务端请求消息,服务端接到客户端请求时,会阻塞该请求不会立即返回,直到有消息或超时的时候才会返回客户端,然后客户端处理完响应信息后,再次向服务端发送请求,以此循环。

4. 消息消费模式:

疑问:当一条消息发送到一个消费者组里时,同一个消费者组里的多个消费者,怎么分配去消费消息呢?
  :RocketMQ中有两种消息消费模式,集群模式和广播模式。

  • 集群模式(clustering)
      同一个consumer group内的单个消费者只能消费指定topic的部分消息,组内所有消费者消费的内容之和才是topic内容的全部,从而达到负载均衡的效果。
  • 广播模式(broadcasting)
      同一个consumer group内的每一个消费者可以消费指定topic的 全部消息。


5. 如何处理重复消息:

疑问:当消费端收到两条一样的消息,该怎么处理?

  答:两种思路。①消费端处理消息的业务逻辑保持幂等性。②用日志表去记录消息处理结果,保证同一条消息只做一次处理。下面具体说明。

  1. 第一条很好理解,只要保持幂等性,不管来多少条相同的消息,处理的结果都保持一致。需在消费端处理。
  2. 利用一张日志表来记录消息处理的记录,同一条消息只会被处理一次。理论上可以由消息系统实现,但是会影响吞吐量和高可用等性能,所以还是放在消费端实现,这也是为什么RocketMQ官方不提供解决消息重复的方案。

RocketMQ不保证消息不重复,如果你的系统需要严格的保证消息不重复,那么需要你自己在业务端去重。