作者: H.E. | 您可以轉(zhuǎn)載, 但必須以超鏈接形式標(biāo)明文章原始出處和作者信息及版權(quán)聲明
網(wǎng)址:
http://www.javabloger.com/article/mq-kestrel-redis-for-java.html 豆瓣讀書 向你推薦有關(guān)
JMS、
NoSQL、
分布式、
存儲(chǔ)、
性能、 類別的圖書。
Kestrel是twitter的開發(fā)團(tuán)隊(duì)用scala語(yǔ)言寫的開源消息中間件,可以將消息持久存儲(chǔ)到磁盤上,也可以將消息存儲(chǔ)于內(nèi)存中,但是不論保存磁盤還是內(nèi)存中都可以設(shè)置消息存儲(chǔ)的超期時(shí)間長(zhǎng)短。
原先Kestrel是由Ruby寫的Starling項(xiàng)目,但是后來(lái) twitter的開發(fā)人員嘗試用scala重新實(shí)現(xiàn),并且可以支持Memcached的部分協(xié)議,例如:GET、SET、FLUSH_ALL、 STATS。對(duì)于Kestrel 服務(wù)器端而言如果有N個(gè)接收端連接在Kestrel服務(wù)器上,那么每個(gè)接收端會(huì)平均或者隨機(jī)的收到不同的消息,并且發(fā)送端發(fā)過了消息接收端就算不接收,等 到接收端再上去接收的時(shí)候還能收到消息,因?yàn)镵estrel支持消息持久化。
Kestrel 不存在主從 和 集群的概念,只存在分布式的說(shuō)法,這有點(diǎn)類似memcached 通過客戶端組成一個(gè)環(huán)狀,對(duì)于Kestrel 服務(wù)器端而言,如果服務(wù)器端收到消息了,但是里面有消息沒有收下來(lái)就掛了,再重啟的時(shí)候接收端還能收到之前的消息。
Redis是一個(gè)key/value存儲(chǔ)系統(tǒng),大多數(shù)開發(fā)者把他當(dāng)做類似Memcached的緩存系統(tǒng)使用,
Redis和MemCached區(qū)別的是:
1.會(huì)周期性的把更新的數(shù)據(jù)寫入磁盤或者把修改操作寫入追加的記錄文件
2.對(duì)于我來(lái)說(shuō)更重要的是Redis還實(shí)現(xiàn)了master-slave(主從)數(shù)據(jù)同步的功能。
3.Redis不僅支持傳統(tǒng)的存儲(chǔ)的value類型,還可以支持list(鏈表)、set(集合)和zset(有序集合),
4.Redis還支持多種方式的排序,并且可以按照數(shù)據(jù)存儲(chǔ)的范圍來(lái)訪問,有點(diǎn)像查詢(select),
5.支持消息發(fā)布和訂閱功能。
Redis也有我們對(duì)他不滿意之處,例如:
1.對(duì)于同一個(gè)隊(duì)列或者通道來(lái)說(shuō),有N個(gè)接收端連接在Redis服務(wù)器上,每個(gè)接收端會(huì)收到相同的消息。
2.發(fā)過了 你不接收 等接收端再上去消息就沒有了,默認(rèn)的配置是寫在內(nèi)存中,重啟后不保存。
這2點(diǎn)上跟 Kestrel 是相反的,但通過一些奇巧淫技將Redis消息視為緩存進(jìn)行持久化,保證redis服務(wù)器重啟后數(shù)據(jù)還在,并且通過Redis客戶端動(dòng)態(tài)設(shè)置機(jī)器的主從備 份,只要有兩臺(tái)或以上的Redis做集群可以對(duì)數(shù)據(jù)永遠(yuǎn)有保證,除非服務(wù)器集群環(huán)境全掛,這樣的話Redis還算是對(duì)數(shù)據(jù)提供了可靠性。
但動(dòng)態(tài)指定主從關(guān)系的話此時(shí)又會(huì)面臨一致性的話題,在客戶端指定Master的時(shí)候,為避免相互競(jìng)爭(zhēng)或者重復(fù)指定,我們通過Jgroups工具作為Redis for Java客戶端之間的通訊工具,在通訊過程中采用了Paxos算法,由客戶端先選出一個(gè)Leader,再由這個(gè)選出來(lái)的Leader進(jìn)行動(dòng)態(tài)指定Redis集群中的master機(jī)器,這樣可以提高一致性,避免競(jìng)爭(zhēng)和沖突。
回到一開始說(shuō)的Kestrel, 經(jīng)過一番測(cè)試比較使用 xmemcached、spymemcached 等客戶端對(duì)Kestrel進(jìn)行收發(fā)操作,發(fā)現(xiàn)效果都比較爛,很遺憾沒有找到讓人滿意的Kestrel for Java的客戶端,所以我們一致認(rèn)為使用Kestrel 這貨將來(lái)的杯具估計(jì)產(chǎn)生在客戶端(java),也許Twitter使用的是ruby,是不是對(duì)ruby反而支持的很好?對(duì)Java支持的不夠給 力?Kestrel 接收端 需要不斷的循環(huán)服務(wù)器 才能及時(shí)的收到消息, 這樣節(jié)點(diǎn)數(shù)一多的話網(wǎng)絡(luò)開銷會(huì)不會(huì)很大?
在測(cè)試的過程中,我們使用xmemcached作為客戶端,貌似不太靠譜,開10個(gè)線程,循環(huán)1000次,表示鴨梨很大,出現(xiàn)以下異常信息,producer和consumer都有,而且同時(shí)冒出來(lái)。
發(fā)送端:
Exception in thread "main" java.util.concurrent.TimeoutException: Timed out(1000) waiting for operation
接收端:
Exception in thread "main" java.util.concurrent.TimeoutException: Timed out waiting for operation
隨后google之,作者提供了解答:http://code.google.com/p/xmemcached/issues/detail?id=108
作者給出的解釋是:“這主要是因?yàn)閤mc的操作都是異步的,同步等待有個(gè)超時(shí)時(shí)間,默認(rèn)的1秒在高并發(fā)或者存取大數(shù)據(jù)的時(shí)候通常是不夠的。”暈啊,表示不能接受這樣的解釋。
隨后目光開始關(guān)注Redis,Redis采用長(zhǎng)連接,事件監(jiān)聽,不需要輪詢,接收端等待消息,只接收字符串類型的消息,對(duì)消息的數(shù)據(jù)沒有保證,不接收就沒 有了服務(wù)器端不保存,也不支持Queue的消息模式,就像JMS中的Topic消息那樣一個(gè)發(fā)送端對(duì)應(yīng)N個(gè)接收端每個(gè)接收端收到同樣的消息。
但我們將publish/subscribe和rpush/rpop 4個(gè)協(xié)議混合使用就可以完全滿足我們的要求,客戶端不輪訓(xùn)可以保證及時(shí)性和消息持久化的問題,而且還是Queue的模式。思路上通了后,做了壓力測(cè)試,收 發(fā)100w個(gè)消息開1000個(gè)線程,8秒以內(nèi)搞定收發(fā),表示毫無(wú)壓力,體積小,部署簡(jiǎn)單比JMS方便,而且無(wú)單點(diǎn),支持可散列。
在壓力測(cè)試的過程中發(fā)現(xiàn)使用Redis Java客戶端需要注意的兩個(gè)問題:1.接收端莫名其妙的異常退出,這是由于沒有設(shè)置連接空閑的超時(shí)導(dǎo)致的,就和MySQL的8小時(shí)問題一樣,在 Redis客戶端設(shè)置一下config.setMaxIdle(num) 就好了。2.發(fā)送端由于發(fā)送大量請(qǐng)求會(huì)崩潰,出現(xiàn)超時(shí)錯(cuò)誤,例如:JedisConnectionException: java.net.SocketTimeoutException: Read timed out,后來(lái)使用JedisPool和JedisPoolConfig實(shí)例,進(jìn)行池化,一切都o(jì)k了,否則難以支持大數(shù)據(jù)量的高并發(fā)。
在系統(tǒng)中采用MQ是為了進(jìn)行解耦合,用在2個(gè)環(huán)節(jié)上:
1.系統(tǒng)中有大量數(shù)據(jù)并發(fā)寫入,并且可以容忍一定的延時(shí),那么就可以先存儲(chǔ)緩存中,然后再將數(shù)據(jù)批量寫入,降低DAL和數(shù)據(jù)庫(kù)層每次通訊的開銷。
2.我們自己開發(fā)的分布式并行計(jì)算的定時(shí)器稱為“任務(wù)工廠”(支持失敗轉(zhuǎn)發(fā)),任務(wù)工廠從數(shù)據(jù)庫(kù)或者緩存中拿用戶的信息,將用戶信息和業(yè)務(wù)邏輯框架作為消 息,發(fā)送給各個(gè)不同的處理業(yè)務(wù)邏輯的服務(wù)器進(jìn)行處理,那樣1000w個(gè)用戶的需要定時(shí)批量操作的話,任務(wù)工廠集群越大處理的速度就越快,而且如果有一個(gè)任 務(wù)工廠的節(jié)點(diǎn)壞掉了或者又添加了一臺(tái)新節(jié)點(diǎn),輪詢完畢后 重新計(jì)算一次后,每臺(tái)機(jī)器又將平攤被分載的計(jì)算。
這次在實(shí)施的項(xiàng)目中,我們還專門設(shè)計(jì)了一個(gè)“失敗隊(duì)列”,他的作用是 處理具體業(yè)務(wù)邏輯的服務(wù)器 收到消息后將處理失敗的請(qǐng)求放入“失敗隊(duì)列”中,往往消息在發(fā)送過來(lái)后進(jìn)行處理,但是由于種種原因處理失敗,但是下次還會(huì)再進(jìn)來(lái)處理,這樣的情況如果量大 而且每次都這樣的話對(duì)系統(tǒng)顯然很不利,所以從原有的隊(duì)列中移除這個(gè)用戶請(qǐng)求,轉(zhuǎn)移到“失敗隊(duì)列”中由 專門處理“失敗隊(duì)列”的任務(wù)工廠會(huì)去定時(shí)處理,如果這個(gè)賬號(hào)輪訓(xùn)處理5次再失敗,移除失敗隊(duì)列,存入數(shù)據(jù)庫(kù),進(jìn)行管理員人工干預(yù)。
如果你真正的理解了MQ的工作原理,可以采用一些網(wǎng)絡(luò)通信工具例如:netty、mina、grizzly ,加上一些基于Key/Value的內(nèi)存存儲(chǔ)產(chǎn)品,例如memcached、redis ,自己可以是建造一個(gè)符合自己要求高效的輕量級(jí)MQ系統(tǒng),如果說(shuō)你可以失去一部分性能而換取可靠性的系統(tǒng)而言JMS依舊是首選產(chǎn)品。
感謝作者 莊曉丹 的回復(fù),內(nèi)容如下:
———————————————————————————————————————-
1、需要輪詢,kestrel就是一個(gè)簡(jiǎn)單的push/pull模型,消費(fèi)者只能通過pull輪詢來(lái)獲取消息。這個(gè)輪詢的代價(jià)我認(rèn)為很低的,取決于隊(duì)列名稱的大小,一般也就十幾個(gè)字節(jié)一次請(qǐng)求。
2、超時(shí)的原因很多,通常來(lái)說(shuō)跟你傳輸?shù)南⒋笮 ⒕W(wǎng)絡(luò)狀況、kestrel的服務(wù)器狀況都有關(guān)系,數(shù)據(jù)越大,在網(wǎng) 絡(luò)傳輸上耗費(fèi)的時(shí)間越多,相應(yīng)的越容易出現(xiàn)超時(shí)的情況。kestrel是scala寫的,啟動(dòng)的時(shí)候要注意下jvm參數(shù),如堆大小、gc算法之類,減少 gc暫停帶來(lái)的影響。服務(wù)器如果gc暫停,或者磁盤做刷入,都有可能導(dǎo)致響應(yīng)超時(shí),簡(jiǎn)單的做法就是將操作的超時(shí)時(shí)間加大,請(qǐng)注意,這個(gè)超時(shí)加大,不代表每 個(gè)操作都要用這么多時(shí)間(可以統(tǒng)計(jì)平均看看),而是代表可以容許的最大超時(shí)時(shí)間。
kestrel整體來(lái)講,只能作為一個(gè)輕量級(jí)的mq方案,用在一些相對(duì)簡(jiǎn)單的場(chǎng)景。———————————————————————————————————————-
–end–