Kestrel是一個
scala寫的twitter開源的消息中間件,特點是高性能、小巧(2K行代碼)、持久存儲(記錄日志到journal)并且可靠(支持可靠獲?。?。Kestrel的前身是Ruby寫的
Starling項目,后來twitter的開發人員嘗試用scala重新實現。它的代碼非常簡潔并且優雅,推薦一讀。
Kestrel采用的協議是
memcached的文本協議,但是并不完全支持所有
memcached協議,也不是完全兼容現有協議。標準的協議它僅支持GET、SET、FLUSH_ALL、STATS,擴展的協議有:
SHUTDOWN 關閉kestrel server
RELOAD 動態重新加載配置文件
DUMP_CONFIG dump配置文件
FLUSH queueName flush某個隊列
每個key對應都是一個隊列。標準memcached文本協議的支持上也沒有完全兼容,
SET不支持flag,因此現有大多數基于flag做序列化的memcached client都無法存儲任意java類型到kestrel;FLUSH_ALL返回"Flushed all queues.\r\n"而不是"OK\r\n"。
GET協議支持
阻塞獲取和可靠獲取,都是在key上作文章,例如你要獲取queue1的消息,并且在沒有消息的時候等待一秒鐘,如果有消息馬上返回,超時時間后還沒有就返回空,kestrel允許你通過發送
"GET queue1/t=1000\r\n"
來阻塞獲取。本來的key應該queue1,這里變成了"queue1/t=1000",因此如果你使用的client有對返回的key和發送的key做校驗,那么可能就認為kestrel返回錯誤。
什么是可靠獲取呢?默認的GET是從隊列中獲取消息后,server端就將該消息從隊列中移除,客戶端需要自己保證不把這個消息丟失掉,也就是說這里是類似JMS規范中的自動應答(auto-acknowledge),如果客戶端在處理這個消息的時候異常崩潰或者在接收消息數據的時候連接斷開,那么可能導致這個消息永久丟失。Kestrel的可靠獲取就是類似JMS規范中的CLIENT_ACK mode,客戶端獲取消息后,server將這個消息從隊列移除并正常發送給客戶端,如果這時候客戶端崩潰或者連接斷開,那么server將不會確認該消息被消費并且"un-get"這個消息,重新放到隊列頭部,那么當client重新連接上來的時候還可以獲取這個消息;只有當server收到客戶端的明確確認消息成功的時候,才將消息移除。這個功能也是通過key做手腳,
"GET queue1/open\r\n" 開始一次可靠獲取
"GET queue1/close\r\n" 確認消費成功
你要關閉前一次可靠獲取開啟新的一次,還可以這樣調用
"GET queue1/close/open\r\n"
要注意的是每個連接的client
只能有一個正在執行的可靠獲取,關閉一個沒有開啟的reliable fetch或者在執行一次reliable fetch再次open一個新的獲取都將直接返回空。
從kestrel的協議方面,我們可以學習到的一點就是
在做一份協議的時候,如果有多種不同語言的client的話,應該盡量用通用協議,通用協議通常都已經有很多成熟的client可以使用,避免了為私有協議開發不同語言的client;并且我們可以在通用協議上作擴展,例如kestrel在key上面做的花樣,通過給key附加不同的屬性即可實現一些特殊功能。
XMemcachedClient默認是無法支持kestrel對memcached的協議的擴展,也就是說無法支持阻塞獲取、可靠獲取和flush_all,這是因為
xmemcached會對返回的key和發送的key做校驗,如果不相等就認為解碼錯誤;并且由于kestrel不支持flag,因此無法存儲java序列化類型;另外一個問題是,xmemcached(spymemcached)都會將連續的GET協議合并成一個bulk get協議,而kestrel也并不支持bulk get,所以需要關閉這個優化,這個可以通過下列代碼關閉:
memcachedClient.setOptimizeGet(false);
Spymemcached似乎不提供這個選項。為了解決序列化問題,我添加了一個新的
KestrelCommandFactory,使用這個CommandFactory后,將默認關閉get優化,并且不對GET返回的key做校驗從而支持阻塞獲取和可靠獲取,并且將在存儲的數據之前加上4個字節的flag(整型),因此可以支持存儲任意可序列化類型。但是有一些應用只需要存儲字符串類型和原生類型,這是為了在不同語言的client之間保持可移植(如存儲json數據),那么就不希望在數據之前加上這個flag,關閉這個功能可以通過
memcachedClient.setPrimitiveAsString(true);
方法來設置,所有的原生類型都將調用toString轉成字符串來存儲,字符串前不再自動附加flag。
KestrelCommandFactory已經提交到
svn trunk,預計在xmemcached 1.2.0-RC2的時候發布。
使用KestrelCommandFactory對kestrel做的性能測試,server和client都跑在linux上,jdk6,單線程單client連續push消息
消息個數 消息長度 是否啟用journal 時間 TPS(/s)
500000 256 否 123.0s 4065
500000 1024 否 126.3s 3959
500000 4096 否 120.6s 4145
500000 4096 是 122.1s 4095
500000 8192 是 121.2s 4125
從數據上來看比官方數據好很多,可能機器配置不同。是否啟用journal帶來的影響似乎很小,寫文件都是append,還是比較高效的。
kestrel的項目主頁
http://github.com/robey/kestrel
kestrel的wiki頁
http://wiki.github.com/robey/kestrel
xmemcached項目主頁
http://code.google.com/p/xmemcached/