在一般狀況下你在使用消息中間件的時候,都是未經設計的使用,你沒有把應用架構和系統架構邊界搞清楚。消息中間件只是一個純粹的技術工具,當你引入的時候是站在應用架構的角度引入的。這是架構的角度,也是架構的上帝視角,這樣你就不會用到最後發現愈來愈混亂,並且也沒法結合軟件模式、方法論、最佳實踐來綜合提高系統的架構能力。html
EDA(Event Driven Architecture,EDA) 事件驅動架構,它是一種用來在SOA或者Micro service中進行的架構模式。它的好處有幾個,柔性具備很高的伸縮性。node
(具體參考本人的SOA架構文章:SOA架構設計經驗分享—架構、職責、數據一致性)git
既然要EDA就要規劃好你當前的系統邊界以內有多少業務實體,這些實體是圍繞着領域模型而得來。因此這裏不要很主觀的就定義一些你認爲的事件,這些事件要根據業務實體中的對象來設計。業務實體起碼是有惟一Identity的。好比,訂單、商品,圍繞着這些實體展開,訂單可能有幾個狀態是比較經常使用的,建立、支付、配送、取消。商品可能有價格、關鍵屬性修改等等。這些實體的抽象和提煉取決於你當前的業務。github
(有關這方面內容能夠參考:《領域驅動設計》、《探索CQRS和事件源》)web
這些是相對理論的指導思想,有了這些以後你能夠落地你的Rabbitmq,這樣你就不會跑偏了。好比,你的消息名稱不會是看起來沒結構和層次的,deliveryMssage(配送消息)。而是應該,order.delivery.ondeliveryEvented(訂單.配送.配送完成事件)這樣的結構。spring
當你的層次結構不知足業務需求的時候,你可能還須要進一步明確事件範圍,order.viporder.delivery.ondeliveryEvented(訂單.VIP訂單.配送.配送完成事件)。數據庫
上圖是一個事件驅動的基本場景,它最矚目的幾個特性就是這幾個,首先是異步化的,能夠大大提升系統的抗峯值能力。而後就是解耦,這不用說了,設計模式裏的觀察者模式沒有人不知道它的好處。伸縮性,能夠按需scaleout,好比rabbitmq的node能夠很方便的加入。最終一致性解決了分佈式系統的CAP定理的問題。vim
AMQP協議中約定了routing key的設計和交互。爲了實現訂閱發佈功能,咱們須要某種方式可以訂閱本身所感興趣的事件。因此在AMQP中的Binding中,能夠根據routing key來進行模式匹配。因此,這裏能夠結合amqp routingkey與領域事件,發出來的事件就至關於amqp中的routingkey,這樣能夠完美的結合起來。設計模式
你的事件確定是隨着業務發展逐漸增長的,而這個事件集合也沒辦法在一開始就定義清楚,因此這裏有一個須要注意的就是,綁定的時候千萬不要寫死具體的routing key。好比,order.delivery.OnDeliveryEvented,這是訂單配送,此時你Binding的時候routingkey就寫成了」order.delivery.OnDeliveryEvented」。將來訂單事件一擴展,就會很麻煩,不相關的事件都被訂閱到,沒法細化或者事件你沒法獲取到,由於routingkey改變了。因此在綁定的時候記住具體點綁定,也就是藉助字符串的模式匹配綁定,好比,*.delivery.*,*.onDeliveryEvented」這樣。未來愈來愈多的routingkey和event出來都不會影響你的綁定。你只須要根據本身的關心程度,綁定在事件的不一樣層級上便可。api
上圖中,orderBinding綁定了order事件,它訂閱了頂級事件,也就是說將來任何類型的訂單均可以被訂閱到,好比,order.normalorder.delivery.onDeliveryEvent也能夠被訂閱到。而viporderBinding訂閱了viporder事件,若是發送了一個order.normalorder.delivery.onDeliveryEvent就跟它不要緊了。
搞清楚了應用架構的事情,咱們開始着手搭建RabbitMQ cluster。rabbitmq這款AMQP產品是用erlang開發的,那麼咱們稍微介紹下erlang。
我第一次正式接觸erlang就是從rabbitmq開始的,一開始並無太多感受到特別的地方,後來才明白越明白愈加現挺喜歡這門語言的。喜歡的理由就是,它是自然的分佈式語言。這句話提及來好像挺日常的,可是當你明白了.erlang.cookie機制以後才恍然大悟。瞬間頓悟了,爲何要用erlang來搞rabbitmq,而是它真的很適合信息交換之類的軟件。erlang是愛立信公司開發的專門用來開發高性能信息交換機的,想一想也會以爲那些軟件的性能和穩定性要求是極高的。RabbitMQ的節點發現和互連真的很方便,這在erlang的虛擬機中就集成了,並且具備高度容錯能力。反正我對它頗有好感。
還有一點值得驕傲的是RabbitMQ是偉大的pivotal公司的,你應該知道pivotal公司是幹什麼的,若是你還不清楚建議你馬上google下。
一開始我並無太關注他們的copyright,後來對pivotal公司愈來愈佩服以後忽然看到原來RabbitMQ也是他們家的,忽然信心倍增。這就是影響力和口碑,看看人家公司的spring、springboot、spring cloud,佩服的五體投地。(RabbtiMQ 官網:http://www.rabbitmq.com/)
要想安裝RabbitMQ,首先須要安裝和配置好它的宿主環境erlang。去erlang官網下載好erlang otp_src源碼包,而後在本地執行源碼安裝。(erlang官網:http://www.erlang.org/)
因爲我本機已經下載好了otp_src源碼包,我是使用的otp_src_19.1版本。下載好以後解壓縮,而後進入目錄,執行./configure --prefix=/usr/erlang/,進行環境的檢查和安裝路徑的選擇。若是你提示「No curses library functions found」錯誤,是由於缺乏curses庫,yum install –y ncurses-devel。安裝後在進行configure。
若是沒有報錯的話,就說明安裝成功了。你還須要配置下環境變量:
export PATH=$PATH:/usr/erlang/bin
source /etc/profile
此時使用erl命令檢查下erlang是否能正常工做了。
接下來安裝RabbitMQ,去官網下載運行的包就好了。
一樣要配置下環境變量,這樣你的命令才能被系統查找到。而後運行rabbitmq實例。
這裏有一個須要注意,記得配置下hosts,在127.0.0.1里加上本機的名稱。erlang進程須要host來進行鏈接,因此它會檢查你的hosts配置。還須要設置下防火牆,三個端口要打開。15672是管理界面用的,25672是集羣之間使用的端口,4369是erlang進程epmd用來作node鏈接的。
我配置了兩個節點,192.168.0.10五、192.168.0.107,如今已經所有就緒。咱們添加原始帳號進入rabbitmq管理界面。
先保證你的各個rabbitmq節點都是能夠訪問的,且打開rabbitmq_management plugin,這樣能夠當出現某個節點掛掉以後能夠切換到其餘管理界面查看狀況或者管理。
打開管理界面插件:
rabbitmq-plugins enable rabbitmq_management
添加帳號:
rabbitmqctl add_user admin admin
添加 權限tag
rabbitmqctl set_user_tags admin administrator
保證兩個節點都是能夠正常工做的。下面咱們就將這兩個節點鏈接起來造成高可用的cluster,這樣咱們就可讓咱們的exchange、queue在這兩個節點之間複製,造成高可用的queue。
cd 到你的home目錄下,我是在root下,裏面有一個隱藏的.erlang.cookie文件,這就是我在前面介紹erlang時候提到的,這個文件是erlang用來發現和互連的基礎。咱們須要作的很簡單,將兩個節點中的.erlang.cookie設置成同樣的。這是erlang的約定,同樣的cookie hash key他認爲是合法和正確的鏈接。
.erlang.cookie默認是隻讀的,你須要修改下寫入權限,而後複製粘貼下cookie 字符串便可。
chmod u+w .erlang.cookie
配置好了以後接下來配置hosts文件,erlang會使用hosts文件裏的配置去發現節點。
vim /etc/hosts
192.168.0.107 rabbitmq_node2
192.168.0.105 rabbitmq_node1
保證一樣的配置在全部的節點上都是相同的。驗證你配置的正確不正確你只須要在你的機器上ping rabbitmq_node1,試下請求的ip是否是你配置的便可。按照DNS的請求原理,hosts是最高優先權,除非瀏覽器有緩存,你直接用ping就不會有問題的。
選擇一個節點stop,而後鏈接到另外節點。
rabbitmqctl stop_app
rabbitmqctl join_cluster rabbit@rabbitmq_node2
Clustering node rabbit@rabbitmq_node1 with rabbit@rabbitmq_node2 ...
rabbitmqctl start_app
節點已經鏈接成功。
默認狀況下節點佔用的memory是總內存的40%,能夠根據本身的用途仔細研究rabbitmq的配置項。爲了提升性能,不須要兩個節點都是disc的節點,因此咱們須要啓動一個節點爲RAM模式。
rabbitmqctl change_cluster_node_type ram
改變rabbitmq_node1爲內存節點模式。
節點是準備好了,接下來咱們須要設置exchange、queue 高可用策略,這樣才能真的作到高可用。如今是物理上的機器或者說虛擬機節點是高可用的,可是裏面的對象須要咱們進行配置策略。
RabbitMQ支持很好的策略模式,須要管理員才能操做。
首先咱們須要建立一個屬於本身業務範圍內的vhost,標示一個邏輯上的獨立空間,全部的帳號、策略、隊列都是強制在某個虛擬機裏的。我建立了一個common vhost。
開始添加policie。
最主要是Apply to ,能夠做用在exchange或者queues上,固然也能夠包含這兩個。策略選擇仍是比較豐富的,最經常使用的是HAmode,還有MessageTTL(消息的過時時間)。這些策略按照幾個維度分組了,有跟高可用相關的,有Federation(集羣之間同步消息)相關的 ,有Queue相關的,還有Exchange相關的。能夠根據的業務場景進行調整。
咱們定義了策略的匹配模式.order.,這樣能夠避免將全部的exchange、queue都鏡像了。
咱們新建了一個ex.order.topic exchange,它的features中應用了exchange_queue_ha策略。(相同的策略是沒法疊加使用的。)其餘的exchange並無應用這個策略,是由於咱們的pattern限定了只匹配.order.的名稱。
建立一個qu.order.crm queue,注意看它的node屬性裏有一個」Synchronised mirrors:rabbit@rabbitmq_node2「鏡像複製。features裏也應用了exchange_queue_ha策略。這個時候,隊列其實在兩個節點裏都是有的,雖然咱們建立的時候是在rabbit@rabbitmq_node1裏的,可是它會複製到集羣裏的其餘節點。在建立HAmode的時候能夠提供HA params參數,來限定複製節點的個數,這一般用來提升性能和HA之間的平衡。
在rabbitmq-plugins中有兩個plugin仍是能夠試着研究研究的。rabbitmq-plugins list。
rabbitmq-plugins list
Configured: E = explicitly enabled; e = implicitly enabled
| Status: * = running on rabbit@rabbitmq_node1
|/
[e*] amqp_client 3.6.5
[ ] cowboy 1.0.3
[ ] cowlib 1.0.1
[e*] mochiweb 2.13.1
[ ] rabbitmq_amqp1_0 3.6.5
[ ] rabbitmq_auth_backend_ldap 3.6.5
[ ] rabbitmq_auth_mechanism_ssl 3.6.5
[ ] rabbitmq_consistent_hash_exchange 3.6.5
[ ] rabbitmq_event_exchange 3.6.5
[ ] rabbitmq_federation 3.6.5
[ ] rabbitmq_federation_management 3.6.5
[ ] rabbitmq_jms_topic_exchange 3.6.5
[E*] rabbitmq_management 3.6.5
[e*] rabbitmq_management_agent 3.6.5
[ ] rabbitmq_management_visualiser 3.6.5
[ ] rabbitmq_mqtt 3.6.5
[ ] rabbitmq_recent_history_exchange 1.2.1
[ ] rabbitmq_sharding 0.1.0
[ ] rabbitmq_shovel 3.6.5
[ ] rabbitmq_shovel_management 3.6.5
[ ] rabbitmq_stomp 3.6.5
[ ] rabbitmq_top 3.6.5
[ ] rabbitmq_tracing 3.6.5
[ ] rabbitmq_trust_store 3.6.5
[e*] rabbitmq_web_dispatch 3.6.5
[ ] rabbitmq_web_stomp 3.6.5
[ ] rabbitmq_web_stomp_examples 3.6.5
[ ] sockjs 0.3.4
[e*] webmachine 1.10.3
rabbitmq_sharding、rabbitmq_federation,rabbitmq_sharding的版本有點低了,github地址:https://github.com/rabbitmq/rabbitmq-sharding
Rederation 能夠用來進行跨cluster或者node之間同步消息。http://www.rabbitmq.com/federated-exchanges.html
這個用來在不一樣的domain之間傳遞消息仍是個不錯的解決方案,跨機房或者跨網絡區域,訂閱別人的rabbitmq消息始終不太穩定,能夠用這種方式來傳遞消息。
有時候可能因爲各類緣由致使queue mirror失敗,這個時候能夠手動進行同步,而不是像其餘分佈式系統來重啓節點或者重建數據。
這個仍是比較方便的,有時候總有那麼幾個小問題須要你手動處理的。
各個環境的集羣配置同步也是個平常運維的問題,還好RabbitMQ也提供了相關工具。
因爲RabbitMQ是AMQP協議的實現,因此在進行遠程鏈接的時候儘可能採用amqp協議的方式鏈接。
var amqpList = new List<AmqpTcpEndpoint>
{
new AmqpTcpEndpoint(new Uri("amqp://192.168.0.105:5672")),
new AmqpTcpEndpoint(new Uri("amqp://192.168.0.107:5672"))
};
關於集羣的vip方案其實也是須要綜合考慮的,若是是統一的地址會面臨三個問題,DNS、LoadBalance、VIP,這三個點都有可能致使集羣鏈接不上。如今愈來愈多的方案傾向於在客戶端作負載和故障轉移,這有不少好處,消除了中間節點帶來的故障機率。若是這三個點加在一塊兒出現的可用性指標確定是比直接在客戶端鏈接的低的多。
咱們碰到最多就是VIP的問題,這類系統的VIP不一樣於數據庫,數據庫的master\slave大多都是要人工check後才切換,不會隨便自動的切換主從庫。而非數據庫的VIP大多都是Keepalived自動檢測切換,這帶來一些列問題,包括鏈接重試、心跳保持。這只是VIP的出錯場景之一。還有LoadBalance帶來的問題,DNS出錯的可能性也是很大。因此我傾向於使用客戶端來作這些。
有幾個地方很重要,第一個就是消息的Persistent持久化狀態要帶上,第二個就是ContentType,這個屬性很實用,方便你查看消息的正文。
若是沒設置,默認是null。
第三個就是AutomaticRecoveryEnabled,自動鏈接重試,這致命重要。當上面的VIP切換以後這個能夠保命。第四個就是TopologyRecoveryEnabled,從新恢復Exchange、queue、binding。在出現網絡斷開以後,一旦恢復鏈接就會恢復這些設置以保證是最新的設置。
無論rabbitmq保證的多麼強壯,多麼高可用,記住必定要有備用方案。
在以前我寫了一篇文章,WebAPi的可視化輸出模式(RabbitMQ、消息補償相關)——全部webapi彷佛都缺失的一個功能
說了就是消息的持久化和補償。
一旦將發送和接受的消息持久化以後咱們能作到事情就比較多了。消息補償是能夠作的,異常也不用擔憂。可是在發送消息的時候必定要注意,是先持久化消息在業務邏輯處理。爲了應對特殊活動的監控,還能夠開發必定的業務來監控消息的接受和處理的數量,而後自動補償。
在開發補償程序的時候有一個邏輯挺饒人的,當你對某一個消息進行補償的時候會多出發送消息,而接受的消息確定是比你發送的少。因此你在統計的時候記得DISTINCT下。
相關文章:
封裝RabbitMQ.NET Library 的一點經驗總結
WebAPi的可視化輸出模式(RabbitMQ、消息補償相關)——全部webapi彷佛都缺失的一個功能