本博客強烈推薦:html
Java電子書高清PDF集合免費下載前端
RabbitMQ是一個由erlang開發的AMQP(Advanved Message Queue)的開源實現。算法
場景說明:用戶註冊後,須要發註冊郵件和註冊短信,傳統的作法有兩種1.串行的方式;2.並行的方式數據庫
(1)串行方式:將註冊信息寫入數據庫後,發送註冊郵件,再發送註冊短信,以上三個任務所有完成後才返回給客戶端。 這有一個問題是,郵件,短信並非必須的,它只是一個通知,而這種作法讓客戶端等待沒有必要等待的東西.編程
(2)並行方式:將註冊信息寫入數據庫後,發送郵件的同時,發送短信,以上三個任務完成後,返回給客戶端,並行的方式能提升處理的時間。安全
假設三個業務節點分別使用50ms,串行方式使用時間150ms,並行使用時間100ms。雖然並性已經提升的處理時間,可是,前面說過,郵件和短信對我正常的使用網站沒有任何影響,客戶端沒有必要等着其發送完成才顯示註冊成功,英愛是寫入數據庫後就返回.服務器
引入消息隊列後,把發送郵件,短信不是必須的業務邏輯異步處理架構
由此能夠看出,引入消息隊列後,用戶的響應時間就等於寫入數據庫的時間+寫入消息隊列的時間(能夠忽略不計),引入消息隊列後處理後,響應時間是串行的3倍,是並行的2倍。併發
場景:雙11是購物狂節,用戶下單後,訂單系統須要通知庫存系統,傳統的作法就是訂單系統調用庫存系統的接口.
當庫存系統出現故障時,訂單就會失敗。(這樣馬雲將少賺好多好多錢^ ^)
訂單系統和庫存系統高耦合.
引入消息隊列
訂單系統:用戶下單後,訂單系統完成持久化處理,將消息寫入消息隊列,返回用戶訂單下單成功。
庫存系統:訂閱下單的消息,獲取下單消息,進行庫操做。
就算庫存系統出現故障,消息隊列也能保證消息的可靠投遞,不會致使消息丟失(馬雲這下高興了).
流量削峯通常在秒殺活動中應用普遍
場景:秒殺活動,通常會由於流量過大,致使應用掛掉,爲了解決這個問題,通常在應用前端加入消息隊列。
1.能夠控制活動人數,超過此必定閥值的訂單直接丟棄(我爲何秒殺一次都沒有成功過呢^^)
2.能夠緩解短期的高流量壓垮應用(應用程序按本身的最大處理能力獲取訂單)
1.用戶的請求,服務器收到以後,首先寫入消息隊列,加入消息隊列長度超過最大值,則直接拋棄用戶請求或跳轉到錯誤頁面.
2.秒殺業務根據消息隊列中的請求信息,再作後續處理.
Broker:它提供一種傳輸服務,它的角色就是維護一條從生產者到消費者的路線,保證數據能按照指定的方式進行傳輸,
Exchange:消息交換機,它指定消息按什麼規則,路由到哪一個隊列。
Queue:消息的載體,每一個消息都會被投到一個或多個隊列。
Binding:綁定,它的做用就是把exchange和queue按照路由規則綁定起來.
Routing Key:路由關鍵字,exchange根據這個關鍵字進行消息投遞。
vhost:虛擬主機,一個broker裏能夠有多個vhost,用做不一樣用戶的權限分離。
Producer:消息生產者,就是投遞消息的程序.
Consumer:消息消費者,就是接受消息的程序.
Channel:消息通道,在客戶端的每一個鏈接裏,可創建多個channel.
4.1Round-robin dispathching循環分發
RabbbitMQ的分發機制很是適合擴展,並且它是專門爲併發程序設計的,若是如今load加劇,那麼只須要建立更多的Consumer來進行任務處理。
爲了保證數據不被丟失,RabbitMQ支持消息確認機制,爲了保證數據能被正確處理而不只僅是被Consumer收到,那麼咱們不能採用no-ack,而應該是在處理完數據以後發送ack.
在處理完數據以後發送ack,就是告訴RabbitMQ數據已經被接收,處理完成,RabbitMQ能夠安全的刪除它了.
若是Consumer退出了可是沒有發送ack,那麼RabbitMQ就會把這個Message發送到下一個Consumer,這樣就保證在Consumer異常退出狀況下數據也不會丟失.
RabbitMQ它沒有用到超時機制.RabbitMQ僅僅經過Consumer的鏈接中斷來確認該Message並無正確處理,也就是說RabbitMQ給了Consumer足夠長的時間作數據處理。
若是忘記ack,那麼當Consumer退出時,Mesage會從新分發,而後RabbitMQ會佔用愈來愈多的內存.
要持久化隊列queue的持久化須要在聲明時指定durable=True;
這裏要注意,隊列的名字必定要是Broker中不存在的,否則不能改變此隊列的任何屬性.
隊列和交換機有一個建立時候指定的標誌durable,durable的惟一含義就是具備這個標誌的隊列和交換機會在重啓以後從新創建,它不表示說在隊列中的消息會在重啓後恢復
消息持久化包括3部分
1.exchange持久化,在聲明時指定durable => true
hannel.ExchangeDeclare(ExchangeName, "direct", durable: true, autoDelete: false, arguments: null);//聲明消息隊列,且爲可持久化的
2.queue持久化,在聲明時指定durable => true
channel.QueueDeclare(QueueName, durable: true, exclusive: false, autoDelete: false, arguments: null);//聲明消息隊列,且爲可持久化的
3.消息持久化,在投遞時指定delivery_mode => 2(1是非持久化).
channel.basicPublish("", queueName, MessageProperties.PERSISTENT_TEXT_PLAIN, msg.getBytes());
若是exchange和queue都是持久化的,那麼它們之間的binding也是持久化的,若是exchange和queue二者之間有一個持久化,一個非持久化,則不容許創建綁定.
注意:一旦建立了隊列和交換機,就不能修改其標誌了,例如,建立了一個non-durable的隊列,而後想把它改變成durable的,惟一的辦法就是刪除這個隊列而後重現建立。
你可能也注意到了,分發機制不是那麼優雅,默認狀態下,RabbitMQ將第n個Message分發給第n個Consumer。n是取餘後的,它無論Consumer是否還有unacked Message,只是按照這個默認的機制進行分發.
那麼若是有個Consumer工做比較重,那麼就會致使有的Consumer基本沒事可作,有的Consumer卻毫無休息的機會,那麼,Rabbit是如何處理這種問題呢?
經過basic.qos方法設置prefetch_count=1,這樣RabbitMQ就會使得每一個Consumer在同一個時間點最多處理一個Message,換句話說,在接收到該Consumer的ack前,它不會將新的Message分發給它
channel.basic_qos(prefetch_count=1)
注意,這種方法可能會致使queue滿。固然,這種狀況下你可能須要添加更多的Consumer,或者建立更多的virtualHost來細化你的設計。
先來溫習如下交換機路由的幾種類型:
Direct Exchange:直接匹配,經過Exchange名稱+RountingKey來發送與接收消息.
Fanout Exchange:廣播訂閱,向全部的消費者發佈消息,可是隻有消費者將隊列綁定到該路由器才能收到消息,忽略Routing Key.
Topic Exchange:主題匹配訂閱,這裏的主題指的是RoutingKey,RoutingKey能夠採用通配符,如:*或#,RoutingKey命名採用.來分隔多個詞,只有消息這將隊列綁定到該路由器且指定RoutingKey符合匹配規則時才能收到消息;
Headers Exchange:消息頭訂閱,消息發佈前,爲消息定義一個或多個鍵值對的消息頭,而後消費者接收消息同時須要定義相似的鍵值對請求頭:(如:x-mactch=all或者x_match=any),只有請求頭與消息頭匹配,才能接收消息,忽略RoutingKey.
默認的exchange:若是用空字符串去聲明一個exchange,那麼系統就會使用」amq.direct」這個exchange,咱們建立一個queue時,默認的都會有一個和新建queue同名的routingKey綁定到這個默認的exchange上去
channel.BasicPublish("", "TaskQueue", properties, bytes);
由於在第一個參數選擇了默認的exchange,而咱們申明的隊列叫TaskQueue,因此默認的,它在新建一個也叫TaskQueue的routingKey,並綁定在默認的exchange上,致使了咱們能夠在第二個參數routingKey中寫TaskQueue,這樣它就會找到定義的同名的queue,並把消息放進去。
若是有兩個接收程序都是用了同一個的queue和相同的routingKey去綁定direct exchange的話,分發的行爲是負載均衡的,也就是說第一個是程序1收到,第二個是程序2收到,以此類推。
若是有兩個接收程序用了各自的queue,但使用相同的routingKey去綁定direct exchange的話,分發的行爲是複製的,也就是說每一個程序都會收到這個消息的副本。行爲至關於fanout類型的exchange。
下面詳細來講:
綁定其實就是關聯了exchange和queue,或者這麼說:queue對exchange的內容感興趣,exchange要把它的Message deliver到queue。
Driect exchange的路由算法很是簡單:經過bindingkey的徹底匹配,能夠用下圖來講明.
Exchange和兩個隊列綁定在一塊兒,Q1的bindingkey是orange,Q2的binding key是black和green.
當Producer publish key是orange時,exchange會把它放到Q1上,若是是black或green就會到Q2上,其他的Message被丟棄.
多個queue綁定同一個key也是能夠的,對於下圖的例子,Q1和Q2都綁定了black,對於routing key是black的Message,會被deliver到Q1和Q2,其他的Message都會被丟棄.
對於Message的routing_key是有限制的,不能使任意的。格式是以點號「.」分割的字符表。好比:」stock.usd.nyse」, 「nyse.vmw」, 「quick.orange.rabbit」。你能夠聽任意的key在routing_key中,固然最長不能超過255 bytes。
對於routing_key,有兩個特殊字符
*(星號)表明任意一個單詞
#(hash)0個或多個單詞
Producer發送消息時須要設置routing_key,routing_key包含三個單詞和連個點號o,第一個key描述了celerity(靈巧),第二個是color(色彩),第三個是物種:
在這裏咱們建立了兩個綁定: Q1 的binding key 是」.orange.「; Q2 是 「..rabbit」 和 「lazy.#」:
Q1感興趣全部orange顏色的動物
Q2感興趣全部rabbits和全部的lazy的.
例子:rounting_key 爲 「quick.orange.rabbit」將會發送到Q1和Q2中
rounting_key 爲」lazy.orange.rabbit.hujj.ddd」會被投遞到Q2中,#匹配0個或多個單詞。
RabbitMQ使用ProtoBuf序列化消息,它可做爲RabbitMQ的Message的數據格式進行傳輸,因爲是結構化的數據,這樣就極大的方便了Consumer的數據高效處理,固然也可使用XML,與XML相比,ProtoBuf有如下優點:
1.簡單
2.size小了3-10倍
3.速度快了20-100倍
4.易於編程
6.減小了語義的歧義.
,ProtoBuf具備速度和空間的優點,使得它如今應用很是普遍