[TOC]
## 1. 什么是RocketMQ
2011年阿里巴巴中間件團隊自主研發了RocketMQ消息中間件,具有單機億級消息堆積能力,且能支持嚴格的消息順序。憑借其高性能、低延時和高可靠的特性承載了近年來雙十一交易峰值(2016年為17萬筆/秒),在整個生產鏈路上都有著穩定和出色的表現。阿里在2016年將RocketMQ貢獻給Apache,并成為了Apache的頂級開源項目。現在RocketMQ有開源和商用兩個版本。
> 1. 隊列模型的消息中間件,具有高性能、高可靠、高實時、分布式特點。
> 2. 支持持久化
> 3. 需要良好的硬件支持
> 4. 億級消息堆積能力
**消息隊列主要的應用場景**:
> 異步處理,應用解耦,流量削峰,消息通訊。
天然支持負載均衡,消費者組里的消費者實例平均分攤消費消息
## 2. 概念解讀
一個RocketMQ集群架構如下:

運行原理:
1,啟動Namesrv,Namesrv起來后監聽端口,等待Broker、Produer、Consumer連上來,相當于一個路由控制中心。
2,Broker啟動,跟所有的Namesrv保持長連接,定時發送心跳包。心跳包中包含當前Broker信息(IP+端口等)以及存儲所有topic信息。注冊成功后,namesrv集群中就有Topic跟Broker的映射關系。
3,收發消息前,先創建topic,創建topic時需要指定該topic要存儲在哪些Broker上。也可以在發送消息時自動創建Topic。
4,Producer發送消息,啟動時先跟Namesrv集群中的其中一臺建立長連接,并從Namesrv中獲取當前發送的Topic存在哪些Broker上,然后跟對應的Broker建長連接,直接向Broker發消息。
5,Consumer跟Producer類似。跟其中一臺Namesrv建立長連接,獲取當前訂閱Topic存在哪些Broker,然后直接跟Broker建立連接通道,開始消費消息。
### 2.1 nameserver
> 1. 幾乎無狀態節點、可部署多個,節點之間無信息同步。
> 2. nameserver接收broker的請求,注冊broker路由信息;
> 3. 保存topic路由信息(producer和consumer都需要從它這里獲取路由信息)
### 2.2 brokerserver
#### 2.2.1 功能描述
> 1. broker負責消息的接收、暫存和推送
> 2. broker節點分為兩類:Master和slave,BrokerId為0表示Master,非0表示Slave
> 3. 一個Master可以對應多個Slave,但是一個Slave只可以對應一個Master。
> 4. Master和Slave的綁定是依靠相同的BrokerName和不同的BrokerId
> 5. Broker上存Topic信息,Topic由多個隊列組成,隊列會平均分散在多個Broker上,而Producer的發送機制保證消息盡量平均分布到 所有隊列中,最終效果就是所有消息都平均落在每個Broker上。
> Broker是把消息持久化到磁盤文件的,同步刷盤就是寫入后才告知producer成功;異步刷盤是收到消息后就告知producer成功了,之后異步地將消息從內存(PageCache)寫入到磁盤上。
>6. 每個Broker與Name Server集群中的所有節點建立長連接,定時注冊Topic信息到所有Name Server,并且定時發送心跳包
### 2.3 producer
> 1. 生產者:發送消息,將消息推送給brokerserver;
> 2. Producer與Name Server集群中的其中一個節點(隨機選擇)建立長連接,定期從Name Server取Topic路由信息
> 3. 向提供Topic服務的Master建立長連接,且定時向Master發送心跳。
> 4. Producer完全無狀態,可集群部署。
**producer消息發送隊列選擇方式**
> RocketMQ采用**輪詢**所有隊列的方式確定消息發送到哪一個隊列,RocketMQ提供了三種消息隊列輪詢(MessageQueueSelector)方式:
> 1. 根據Hash值進行輪詢:
> 即SelectMessageQueueByHash implements MessageQueueSelector)
> 2. 隨機方式:
> 即SelectMessageQueueByRandoom implements MessageQueueSelector)
> 3. 自定義方式:
> 繼承MessageQueueSelector接口,重寫select方法,返回選擇的隊列
### 2.4 consumer
> 1. Consumer與Name Server集群中的其中一個節點(隨機選擇)建立長連接,定期從Name Server取Topic路由信息
> 2. 向提供Topic服務的Master、Slave建立長連接,且定時向Master、Slave發送心跳。
> 3. Consumer既可以從Master訂閱消息,也可以從Slave訂閱消息,訂閱規則由Broker配置決定。
#### 2.4.1 consumer概念
> 1. 消費者:接收消息,從brokerserver上獲取消息。
> 2. 在rocketmq里,consumer被分為2類:
> MQPullConsumer和MQPushConsumer,其實本質都是拉模式(pull),即consumer輪詢從broker拉取消息。
> 3. Consumer 如果做廣播消費,則一個 consumer實例消費這個 Topic 對應的所有隊列,如果做集群消費,則多個 Consumer 實例平均消費這個 topic 對應的隊列集合。
> 4. 消費者必須有返回狀態,否則rocketmq會重發這條消息
> 5. consumer與所有關聯的broker保持長連接(包括主從),每隔30s發送心跳,可配置,可以通過heartbeatBrokerInterval配置。broker每隔10s掃描連接,發現2分鐘內沒有心跳,則關閉連接,并通知該consumer組內其他實例,過來繼續消費該topic。
#### 2.4.1 consumer(pull、push)
> pull 與 push對比:
> 慢消費無疑是push模型最大的致命傷,如果消費者的速度比發送者的速度慢很多,勢必造成消息在broker的堆積。假設這些消息都是有用的無法丟棄的,消息就要一直在broker端保存。當然這還不是最致命的,最致命的是broker給consumer推送一堆consumer無法處理的消息,consumer不是reject就是error,然后來回踢皮球。
> 反觀pull模式,consumer可以按需消費,不用擔心自己處理不了的消息來騷擾自己,而broker堆積消息也會相對簡單,無需記錄每一個要發送消息的狀態,只需要維護所有消息的隊列和偏移量就可以了。在阿里的RocketMq里,有一種優化的做法-長輪詢,來平衡推拉模型各自的缺點。但海量的長連接block對系統的開銷還是不容小覷的,還是要合理的評估時間間隔,給wait加一個時間上限比較好~
> 二者區別是:
> push方式里,consumer把輪詢過程封裝了,并注冊MessageListener監聽器,取到消息后,喚醒MessageListener的consumeMessage()來消費,對用戶而言,感覺消息是被推送過來的。
> pull方式里,取消息的過程需要用戶自己寫,首先通過打算消費的Topic拿到MessageQueue的集合,遍歷MessageQueue集合,然后針對每個MessageQueue批量取消息,一次取完后,記錄該隊列下一次要取的開始offset,直到取完了,再換另一個MessageQueue。
3.Push Consumer
應用通常向Consumer對象注冊一個Listener接口,一旦收到消息,Consumer對象立刻回調Listener接口方法。所以,所謂Push指的是客戶端內部的回調機制,并不是與服務端之間的機制。
4.Pull Consumer
應用通常主動調用Consumer從服務端拉消息,然后處理。這用的就是短輪詢方式了,在不同情況下,與長輪詢各有優點。
### 2.5 topic
> 1. 用來表示一類應用(所以一類應用用一個topic最佳)
> 2. 一個topic默認有四個隊列
> 3. RocketMQ在發送消息時,會首先獲取路由信息。如果是新的消息,由于MQServer上面還沒有創建對應的Topic,這個時候,如果上面的配置打開的話,會返回默認TOPIC的(RocketMQ會在每臺broker上面創建名為TBW102的TOPIC)路由信息,然后Producer會選擇一臺Broker發送消息,選中的broker在存儲消息時,發現消息的topic還沒有創建,就會自動創建topic。后果就是:以后所有該TOPIC的消息,都將發送到這臺broker上,達不到負載均衡的目的。
> 線上應該關閉autoCreateTopicEnable,即在配置文件中將其設置為false。
所以基于目前RocketMQ的設計,建議關閉自動創建TOPIC的功能,然后根據消息量的大小,手動創建TOPIC。
### 2.6 tag
> topic的子型,tags 可以由應用自由設置。只有收送消息設置了tags,消費方在訂閱消息時,才可以利用 tags 在 broker 做消息過濾。
### 2.7 key
消息中的key有哪些作用:
> 服務器會為每個消息創建索引(哈希索引),應用可以通過 topic,key 來查詢返回消息內容,以及消息被誰消費。
> 如果一個消息包含key值的話,會使用IndexFile存儲消息索引,查詢
> 自定義Key,可以用于去重
索引文件主要用于根據key來查詢消息的,流程主要是:
> 根據查詢的 key 的 hashcode%slotNum 得到具體的槽的位置(slotNum 是一個索引文件里面包含的最大槽的數目,例如圖中所示 slotNum=5000000)
> 根據 slotValue(slot 位置對應的值)查找到索引項列表的最后一項(倒序排列,slotValue 總是指向最新的一個索引項)
> 遍歷索引項列表返回查詢時間范圍內的結果集(默認一次最大返回的 32 條記錄)
## 3. 集群模式
### 3.1 主從關系
#### 3.1.1 確立主從關系
broker有三個描述信息:
> 1. brokerClusterName:指定broker所屬集群(同一集群下,名稱自然要相同)
> 2. brokerName: broker名稱,用來表示broker
> 3. brokerId:broker編號
主從關系確立有兩點:
> 1. 處于同一集群下(保證brokerClusterName相同即可)
> 2. 相同的brokerName
> 3. 不同的brokerId,0表示該節點是master,大于0表示該節點是slave
#### 3.1.1 主從關系說明
1. master與slave的數據同步:同步和異步
> 同步指:
> 消息到達master,master將數據同步給slave,完成后才確認消息被安全的保存(安全,效率較異步差)
> 異步指:
> 消息到達master后,直接返回一個成功應答,同時異步同步消息給slave。(安全性差一些,性能較高)
2. 一旦master宕機,開源的rockmet不支持自動主從切換,從節點支持讀不支持寫
rocket結構模式
### 3.2 單點master
> 不安全,不實用
### 3.3 雙master
> 全是Master,沒有Slave。
> 一個broker宕機了,應用是無影響的,缺點在于宕機的Master上未被消費的消息在Master沒有恢復之前不可以訂閱。
### 3.4 多Master多Slave模式(異步復制):
> 多對Master-Slave,高可用!采用異步復制的方式,主備之間短暫延遲,MS級別。
> Master宕機,消費者可以從Slave上進行消費,不受影響,但是Master的宕機,會導致丟失掉極少量的消息。但是該從broker不支持寫。
### 3.5 多Master多Slave模式(同步雙寫):
> 數據同步方式的是同步方式,也就是在Master/Slave都寫成功的前提下,向應用返回成功,可見不論是數據,還是服務都沒有單點,都非常可靠!缺點在于同步的性能比異步稍低。但是該從broker不支持寫。
### 3.6 RocketMQ與ActiveMQ對比
RocketMQ在功能和性能上都超過了ActiveMQ!
| | RocketMQ | ActiveMQ |說明|
| --- | --- | --- |--- |
| 消息過濾 | 僅支持客戶端過濾 | 支持客戶端和broker端 |RocketMQ在broker端進行過濾可以減少大量的網絡傳輸是否會有消息重發造成的重復消費:RocketMQ可以保證,ActiveMQ無法保證|
| 消息有序性 | 支持 | 支持|
| 回溯消費 | 支持 | 不支持 |RocketMQ隊列可以將數據持久化到硬盤的,但是需要定期進行清除|
| 定時消費 | 支持 | 不支持|
| 分布式 | 原生支持 | 原生不支持 |ActiveMQ需要做額外的工作來達到分布式|
| 事務 | 支持 | 支持|
| 持久化 | 支持 | 支持|
## 4. 回溯消費
回溯消費是指Consumer已經消費成功的消息,由于業務上需求需要重新消費,要支持此功能,Broker在向Consumer投遞成功消息后,消息仍然需要保留。并且重新消費一般是按照時間維度,例如由于Consumer系統故障,恢復后需要重新消費1小時前的數據,那么Broker要提供一種機制,可以按照時間維度來回退消費進度。
RocketMQ支持按照時間回溯消費,時間維度精確到毫秒,可以向前回溯,也可以向后回溯。
## 5. 消息存儲
1. 消息的存儲是一直存在于CommitLog中的,由于CommitLog是以文件為單位(而非消息)存在的,而且CommitLog的設計是只允許順序寫,且每個消息大小不定長,所以這決定了消息文件幾乎不可能按照消息為單位刪除(否則性能會極具下降,邏輯也非常復雜)。
- Docker
- 什么是docker
- Docker安裝、組件啟動
- docker網絡
- docker命令
- docker swarm
- dockerfile
- mesos
- 運維
- Linux
- Linux基礎
- Linux常用命令_1
- Linux常用命令_2
- ip命令
- 什么是Linux
- SELinux
- Linux GCC編譯警告:Clock skew detected. 錯誤解決辦法
- 文件描述符
- find
- 資源統計
- LVM
- Linux相關配置
- 服務自啟動
- 服務器安全
- 字符集
- shell腳本
- shell命令
- 實用腳本
- shell 數組
- 循環與判斷
- 系統級別進程開啟和停止
- 函數
- java調用shell腳本
- 發送郵件
- Linux網絡配置
- Ubuntu
- Ubuntu發送郵件
- 更換apt-get源
- centos
- 防火墻
- 虛擬機下配置網絡
- yum重新安裝
- 安裝mysql5.7
- 配置本地yum源
- 安裝telnet
- 忘記root密碼
- rsync+ crontab
- Zabbix
- Zabbix監控
- Zabbix安裝
- 自動報警
- 自動發現主機
- 監控MySQL
- 安裝PHP常見錯誤
- 基于nginx安裝zabbix
- 監控Tomcat
- 監控redis
- web監控
- 監控進程和端口號
- zabbix自定義監控
- 觸發器函數
- zabbix監控mysql主從同步狀態
- Jenkins
- 安裝Jenkins
- jenkins+svn+maven
- jenkins執行shell腳本
- 參數化構建
- maven區分環境打包
- jenkins使用注意事項
- nginx
- nginx認證功能
- ubuntu下編譯安裝Nginx
- 編譯安裝
- Nginx搭建本地yum源
- 文件共享
- Haproxy
- 初識Haproxy
- haproxy安裝
- haproxy配置
- virtualbox
- virtualbox 復制新的虛擬機
- ubuntu下vitrualbox安裝redhat
- centos配置雙網卡
- 配置存儲
- Windows
- Windows安裝curl
- VMware vSphere
- 磁盤管理
- 增加磁盤
- gitlab
- 安裝
- tomcat
- Squid
- bigdata
- FastDFS
- FastFDS基礎
- FastFDS安裝及簡單實用
- api介紹
- 數據存儲
- FastDFS防盜鏈
- python腳本
- ELK
- logstash
- 安裝使用
- kibana
- 安準配置
- elasticsearch
- elasticsearch基礎_1
- elasticsearch基礎_2
- 安裝
- 操作
- java api
- 中文分詞器
- term vector
- 并發控制
- 對text字段排序
- 倒排和正排索引
- 自定義分詞器
- 自定義dynamic策略
- 進階練習
- 共享鎖和排它鎖
- nested object
- 父子關系模型
- 高亮
- 搜索提示
- Redis
- redis部署
- redis基礎
- redis運維
- redis-cluster的使用
- redis哨兵
- redis腳本備份還原
- rabbitMQ
- rabbitMQ安裝使用
- rpc
- RocketMQ
- 架構概念
- 安裝
- 實例
- 好文引用
- 知乎
- ACK
- postgresql
- 存儲過程
- 編程語言
- 計算機網絡
- 基礎_01
- tcp/ip
- http轉https
- Let's Encrypt免費ssl證書(基于haproxy負載)
- what's the http?
- 網關
- 網絡IO
- http
- 無狀態網絡協議
- Python
- python基礎
- 基礎數據類型
- String
- List
- 遍歷
- Python基礎_01
- python基礎_02
- python基礎03
- python基礎_04
- python基礎_05
- 函數
- 網絡編程
- 系統編程
- 類
- Python正則表達式
- pymysql
- java調用python腳本
- python操作fastdfs
- 模塊導入和sys.path
- 編碼
- 安裝pip
- python進階
- python之setup.py構建工具
- 模塊動態導入
- 內置函數
- 內置變量
- path
- python模塊
- 內置模塊_01
- 內置模塊_02
- log模塊
- collections
- Twisted
- Twisted基礎
- 異步編程初探與reactor模式
- yield-inlineCallbacks
- 系統編程
- 爬蟲
- urllib
- xpath
- scrapy
- 爬蟲基礎
- 爬蟲種類
- 入門基礎
- Rules
- 反反爬蟲策略
- 模擬登陸
- problem
- 分布式爬蟲
- 快代理整站爬取
- 與es整合
- 爬取APP數據
- 爬蟲部署
- collection for ban of web
- crawlstyle
- API
- 多次請求
- 向調度器發送請求
- 源碼學習
- LinkExtractor源碼分析
- 構建工具-setup.py
- selenium
- 基礎01
- 與scrapy整合
- Django
- Django開發入門
- Django與MySQL
- java
- 設計模式
- 單例模式
- 工廠模式
- java基礎
- java位移
- java反射
- base64
- java內部類
- java高級
- 多線程
- springmvc-restful
- pfx數字證書
- 生成二維碼
- 項目中使用log4j
- 自定義注解
- java發送post請求
- Date時間操作
- spring
- 基礎
- spring事務控制
- springMVC
- 注解
- 參數綁定
- springmvc+spring+mybatis+dubbo
- MVC模型
- SpringBoot
- java配置入門
- SpringBoot基礎入門
- SpringBoot web
- 整合
- SpringBoot注解
- shiro權限控制
- CommandLineRunner
- mybatis
- 靜態資源
- SSM整合
- Aware
- Spring API使用
- Aware接口
- mybatis
- 入門
- mybatis屬性自動映射、掃描
- 問題
- @Param 注解在Mybatis中的使用 以及傳遞參數的三種方式
- mybatis-SQL
- 逆向生成dao、model層代碼
- 反向工程中Example的使用
- 自增id回顯
- SqlSessionDaoSupport
- invalid bound statement(not found)
- 脈絡
- beetl
- beetl是什么
- 與SpringBoot整合
- shiro
- 什么是shiro
- springboot+shrio+mybatis
- 攔截url
- 枚舉
- 圖片操作
- restful
- java項目中日志處理
- JSON
- 文件工具類
- KeyTool生成證書
- 兼容性問題
- 開發規范
- 工具類開發規范
- 壓縮圖片
- 異常處理
- web
- JavaScript
- 基礎語法
- 創建對象
- BOM
- window對象
- DOM
- 閉包
- form提交-文件上傳
- td中內容過長
- 問題1
- js高級
- js文件操作
- 函數_01
- session
- jQuery
- 函數01
- data()
- siblings
- index()與eq()
- select2
- 動態樣式
- bootstrap
- 表單驗證
- 表格
- MUI
- HTML
- iframe
- label標簽
- 規范編程
- layer
- sss
- 微信小程序
- 基礎知識
- 實踐
- 自定義組件
- 修改自定義組件的樣式
- 基礎概念
- appid
- 跳轉
- 小程序發送ajax
- 微信小程序上下拉刷新
- if
- 工具
- idea
- Git
- maven
- svn
- Netty
- 基礎概念
- Handler
- SimpleChannelInboundHandler 與 ChannelInboundHandler
- 網絡編程
- 網絡I/O
- database
- oracle
- 游標
- PLSQL Developer
- mysql
- MySQL基準測試
- mysql備份
- mysql主從不同步
- mysql安裝
- mysql函數大全
- SQL語句
- 修改配置
- 關鍵字
- 主從搭建
- centos下用rpm包安裝mysql
- 常用sql
- information_scheme數據庫
- 值得學的博客
- mysql學習
- 運維
- mysql權限
- 配置信息
- 好文mark
- jsp
- jsp EL表達式
- C
- test