這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
1. 介紹
最近在研究一些訊息中介軟體,常用的MQ如RabbitMQ,ActiveMQ,Kafka等。NSQ是一個基於Go語言的分布式即時訊息平台,它基於MIT開源協議發布,由bitly公司開源出來的一款簡單易用的訊息中介軟體。
官方和第三方還為NSQ開發了眾多用戶端功能庫,如官方提供的基於HTTP的nsqd、Go用戶端go-nsq、Python用戶端pynsq、基於Node.js的JavaScript用戶端nsqjs、非同步C用戶端libnsq、Java用戶端nsq-java以及基於各種語言的眾多第三方用戶端功能庫。
1.1 Features
1). Distributed
NSQ提供了分布式的,去中心化,且沒有單點故障的拓撲結構,穩定的訊息傳輸發布保障,能夠具有高容錯和HA(高可用)特性。
2). Scalable易於擴充
NSQ支援水平擴充,沒有中心化的brokers。內建的探索服務簡化了在叢集中增加節點。同時支援pub-sub和load-balanced 的訊息分發。
3). Ops Friendly
NSQ非常容易配置和部署,生來就綁定了一個管理介面。二進位包沒有運行時依賴。官方有Docker image。
4.Integrated高度整合
官方的 Go 和 Python庫都有提供。而且為大多數語言提供了庫。
1.2 組件
- Topic :一個topic就是程式發布訊息的一個邏輯鍵,當程式第一次發布訊息時就會建立topic。
- Channels :channel與消費者相關,是消費者之間的負載平衡,channel在某種意義上來說是一個“隊列”。每當一個發行者發送一條訊息到一個topic,訊息會被複製到所有消費者串連的channel上,消費者通過這個特殊的channel讀取訊息,實際上,在消費者第一次訂閱時就會建立channel。Channel會將訊息進行排列,如果沒有消費者讀取訊息,訊息首先會在記憶體中排隊,當量太大時就會被儲存到磁碟中。
- Messages:訊息構成了我們資料流的中堅力量,消費者可以選擇結束訊息,表明它們正在被正常處理,或者重新將他們排隊待到後面再進行處理。每個訊息包含傳遞嘗試的次數,當訊息傳遞超過一定的閥值次數時,我們應該放棄這些訊息,或者作為額外訊息進行處理。
- nsqd:nsqd 是一個守護進程,負責接收,排隊,投遞訊息給用戶端。它可以獨立運行,不過通常它是由 nsqlookupd 執行個體所在叢集配置的(它在這能聲明 topics 和 channels,以便大家能找到)。
- nsqlookupd:nsqlookupd 是守護進程負責管理拓撲資訊。用戶端通過查詢 nsqlookupd 來發現指定話題(topic)的生產者,並且 nsqd 節點廣播話題(topic)和通道(channel)資訊。有兩個介面:TCP 介面,nsqd 用它來廣播。HTTP 介面,用戶端用它來發現和管理。
- nsqadmin:nsqadmin 是一套 WEB UI,用來彙集叢集的即時統計,並執行不同的管理工作。
常用工具類:
- nsq_to _file:消費指定的話題(topic)/通道(channel),並寫到檔案中,有選擇的滾動和/或壓縮檔。
- nsq_to _http:消費指定的話題(topic)/通道(channel)和執行 HTTP requests (GET/POST) 到指定的端點。
- nsq_to _nsq:消費者指定的話題/通道和重發布訊息到目的地 nsqd 通過 TCP。
1.3 拓撲結構
NSQ推薦通過他們相應的nsqd執行個體使用協同定位發行者,這意味著即使面對網路磁碟分割,訊息也會被儲存在本地,直到它們被一個消費者讀取。更重要的是,發行者不必去發現其他的nsqd節點,他們總是可以向本地執行個體發布訊息。
NSQ
首先,一個發行者向它的本地nsqd發送訊息,要做到這點,首先要先開啟一個串連,然後發送一個包含topic和訊息主體的發布命令,在這種情況下,我們將訊息發布到事件topic上以分散到我們不同的worker中。
事件topic會複製這些訊息並且在每一個串連topic的channel上進行排隊,在我們的案例中,有三個channel,它們其中之一作為檔案channel。消費者會擷取這些訊息並且上傳到S3。
nsqd
每個channel的訊息都會進行排隊,直到一個worker把他們消費,如果此隊列超出了記憶體限制,訊息將會被寫入到磁碟中。Nsqd節點首先會向nsqlookup廣播他們的位置資訊,一旦它們註冊成功,worker將會從nsqlookup伺服器節點上發現所有包含事件topic的nsqd節點。
nsqlookupd
然後每個worker向每個nsqd主機進行訂閱操作,用於表明worker已經準備好接受訊息了。這裡我們不需要一個完整的連通圖,但我們必須要保證每個單獨的nsqd執行個體擁有足夠的消費者去消費它們的訊息,否則channel會被隊列堆著。
2. Internals
2.1 訊息傳遞擔保
NSQ 保證訊息將交付至少一次,雖然訊息可能是重複的。消費者應該關注到這一點,重複資料刪除資料或執行idempotent等操作。
這個擔保是作為協議和工作流程的一部分,工作原理如下(假設用戶端成功串連並訂閱一個話題):
1)客戶表示已經準備好接收訊息
2)NSQ 發送一條訊息,並暫時將資料存放區在本地(在 re-queue 或 timeout)
3)用戶端回複 FIN(結束)或 REQ(重新排隊)分別指示成功或失敗。如果用戶端沒有回複, NSQ 會在設定的時間逾時,自動重新排隊訊息
這確保了訊息丟失唯一可能的情況是不正常結束 nsqd 進程。在這種情況下,這是在記憶體中的任何資訊(或任何緩衝未重新整理到磁碟)都將丟失。
如何防止訊息丟失是最重要的,即使是這個意外情況可以得到緩解。一種解決方案是構成冗餘 nsqd對(在不同的主機上)接收訊息的相同部分的副本。因為你實現的消費者是等冪的,以兩倍時間處理這些訊息不會對下遊造成影響,並使得系統能夠承受任何單一節點故障而不會丟失資訊。
2.2 簡化配置和管理
單個 nsqd 執行個體被設計成可以同時處理多個資料流。流被稱為“話題”和話題有 1 個或多個“通道”。每個通道都接收到一個話題中所有訊息的拷貝。在實踐中,一個通道映射到下行服務消費一個話題。
話題和通道都沒有預先配置。話題由第一次發布訊息到命名的話題或第一次通過訂閱一個命名話題來建立。通道被第一次訂閱到指定的通道建立。話題和通道的所有緩衝的資料相互獨立,防止緩慢消費者造成對其他通道的積壓(同樣適用於話題層級)。
一個通道一般會有多個用戶端串連。假設所有已串連的用戶端處於準備接收訊息的狀態,每個訊息將被傳遞到一個隨機的用戶端。nsqlookupd,它提供了一個目錄服務,消費者可以尋找到提供他們感興趣訂閱話題的 nsqd 地址 。在配置方面,把消費者與生產者解耦開(它們都分別只需要知道哪裡去串連 nsqlookupd 的共同執行個體,而不是對方),降低複雜性和維護。
在更底的層面,每個 nsqd 有一個與 nsqlookupd 的長期 TCP 串連,定期推動其狀態。這個資料被 nsqlookupd 用於給消費者通知 nsqd 地址。對於消費者來說,一個暴露的 HTTP /lookup 介面用於輪詢。為話題引入一個新的消費者,只需啟動一個配置了 nsqlookup 執行個體地址的 NSQ 用戶端。無需為添加任何新的消費者或生產者更改配置,大大降低了開銷和複雜性。
2.3 消除單點故障
NSQ被設計以分布的方式被使用。nsqd 用戶端(通過 TCP )串連到指定話題的所有生產者執行個體。沒有中間人,沒有訊息代理,也沒有單點故障。
這種拓撲結構消除單鏈,彙總,反饋。相反,你的消費者直接存取所有生產者。從技術上講,哪個用戶端串連到哪個 NSQ 不重要,只要有足夠的消費者串連到所有生產者,以滿足大量的訊息,保證所有東西最終將被處理。對於 nsqlookupd,高可用性是通過運行多個執行個體來實現。他們不直接相互連信和資料被認為是最終一致。消費者輪詢所有的配置的 nsqlookupd 執行個體和合并 response。失敗的,無法訪問的,或以其他方式故障的節點不會讓系統陷於停頓。
2.4 效率
對於資料的協議,通過推送資料到用戶端最大限度地提高效能和輸送量的,而不是等待用戶端拉資料。這個概念,稱之為 RDY 狀態,基本上是用戶端流量控制的一種形式。
當用戶端串連到 nsqd 和並訂閱到一個通道時,它被放置在一個 RDY 為 0 狀態。這意味著,還沒有資訊被發送到用戶端。當用戶端已準備好接收訊息發送,更新它的命令 RDY 狀態到它準備處理的數量,比如 100。無需任何額外的指令,當 100 條訊息可用時,將被傳遞到用戶端(伺服器端為那個用戶端每次遞減 RDY 計數)。用戶端庫的被設計成在 RDY 數達到配置 max-in-flight的 25% 發送一個命令來更新 RDY 計數(並適當考慮串連到多個 nsqd 情況下,適當地分配)。
efficiency
2.5 心跳和逾時
NSQ 的 TCP 協議是面向 push 的。在建立串連,握手,和訂閱後,消費者被放置在一個為 0 的 RDY 狀態。當消費者準備好接收訊息,它更新的 RDY 狀態到準備接收訊息的數量。NSQ 用戶端庫不斷在幕後管理,訊息控制流程的結果。每隔一段時間,nsqd 將發送一個心跳線串連。用戶端可以配置心跳之間的間隔,但 nsqd 會期待一個回應在它發送下一個心掉之前。
組合應用層級的心跳和 RDY 狀態,避免頭阻塞現象,也可能使心跳無用(即,如果消費者是在後面的處理訊息流程的接收緩衝區中,作業系統將被填滿,堵心跳)為了保證進度,所有的網路 IO 時間上限勢必與配置的心跳間隔相關聯。這意味著,你可以從字面上拔掉之間的網路連接 nsqd 和消費者,它會檢測並正確處理錯誤。當檢測到一個致命錯誤,用戶端串連被強制關閉。在傳輸中的訊息會逾時而重新排隊等待傳遞到另一個消費者。最後,錯誤會被記錄並累計到各種內部指標。
2.6 分布式
因為NSQ沒有在精靈之間共用資訊,所以它從一開始就是為了分布式操作而生。個別的機器可以隨便宕機隨便啟動而不會影響到系統的其餘部分,訊息發行者可以在本地發布,即使面對網路磁碟分割。
這種“分布式優先”的設計理念意味著NSQ基本上可以永遠不斷地擴充,需要更高的輸送量?那就添加更多的nsqd吧。唯一的共用狀態就是儲存在lookup節點上,甚至它們不需要全域視圖,配置某些nsqd註冊到某些lookup節點上這是很簡單的配置,唯一關鍵的地方就是消費者可以通過lookup節點擷取所有完整的節點集。清晰的故障事件——NSQ在組件內建立了一套明確關於可能導致故障的的故障權衡機制,這對訊息傳遞和恢複都有意義。雖然它們可能不像Kafka系統那樣提供嚴格的保證層級,但NSQ簡單的操作使故障情況非常明顯。
2.7 no replication
不像其他的隊列組件,NSQ並沒有提供任何形式的複製和叢集,也正是這點讓它能夠如此簡單地運行,但它確實對於一些高保證性高可靠性的訊息發布沒有足夠的保證。我們可以通過降低檔案同步的時間來部分避免,只需通過一個標誌配置,通過EBS支援我們的隊列。但是這樣仍然存在一個訊息被發布後馬上死亡,丟失了有效寫入的情況。
2.8 沒有嚴格的順序
雖然Kafka由一個有序的日誌構成,但NSQ不是。訊息可以在任何時間以任何順序進入隊列。在我們使用的案例中,這通常沒有關係,因為所有的資料都被加上了時間戳記,但它並不適合需要嚴格順序的情況。
2.9 無資料重複刪除功能
NSQ對於逾時系統,它使用了心跳檢測機制去測試消費者是否存活還是死亡。很多原因會導致我們的consumer無法完成心跳檢測,所以在consumer中必須有一個單獨的步驟確保等冪性。
3. 實踐安裝過程
本文將nsq叢集具體的安裝過程略去,大家可以自行參考官網,比較簡單。這部分介紹下筆者實驗的拓撲,以及nsqadmin的相關資訊。
3.1 拓撲結構
topology
實驗採用3台NSQD服務,2台LOOKUPD服務。
採用官方推薦的拓撲,訊息發布的服務和NSQD在一台主機。一共5台機器。
NSQ基本沒有設定檔,配置通過命令列指定參數。
主要命令如下:
LOOKUPD命令
bin/nsqlookupd
NSQD命令
bin/nsqd --lookupd-tcp-address=172.16.30.254:4160 -broadcast-address=172.16.30.254
bin/nsqadmin --lookupd-http-address=172.16.30.254:4161
工具類,消費後儲存到本地檔案。
bin/nsq_to_file --topic=newtest --channel=test --output-dir=/tmp --lookupd-http-address=172.16.30.254:4161
發布一條訊息
curl -d 'hello world 5' 'http://172.16.30.254:4151/put?topic=test'
3.2 nsqadmin
對Streams的詳細資料進行查看,包括NSQD節點,具體的channel,隊列中的訊息數,串連數等資訊。
nsqadmin
channel
列出所有的NSQD節點:
nodes
訊息的統計:
msgs
lookup主機的列表:
hosts
4. 總結
NSQ基本核心就是簡單性,是一個簡單的隊列,這意味著它很容易進行故障推理和很容易發現bug。消費者可以自行處理故障事件而不會影響系統剩下的其餘部分。
事實上,簡單性是我們決定使用NSQ的首要因素,這方便與我們的許多其他軟體一起維護,通過引入隊列使我們得到了堪稱完美的表現,通過隊列甚至讓我們增加了幾個數量級的輸送量。越來越多的consumer需要一套嚴格可靠性和順序性保障,這已經超過了NSQ提供的簡易功能。
結合我們的業務系統來看,對於我們所需要傳輸的發票訊息,相對比較敏感,無法容忍某個nsqd宕機,或者磁碟無法使用的情況,該節點堆積的訊息無法找回。這是我們沒有選擇該訊息中介軟體的主要原因。簡單性和可靠性似乎並不能完全滿足。相比Kafka,ops肩負起更多負責的運營。另一方面,它擁有一個可複製的、有序的日誌可以提供給我們更好的服務。但對於其他適合NSQ的consumer,它為我們服務的相當好,我們期待著繼續鞏固它的堅實的基礎。
ps: 本文首發於筆者的csdn部落格,此處將其加入個人的部落格。
參考
- NSQ:分布式的即時訊息平台
- NSQ - NYC Golang Meetup
- NSQ Docs