Facebook之前上線了新的messages實(shí)現(xiàn),以前草草的看過(guò)相關(guān)的幾篇blog還有facebook同學(xué)在qcon上講的hbase的slide,但其實(shí)看的都很粗略,尤其是之前去facebook和相關(guān)同學(xué)交流后,發(fā)現(xiàn)和自己之前理解的太不一樣了,于是近幾天又翻出了messages的幾篇blog來(lái)仔細(xì)的看了看,看完后的感觸就是facebook在設(shè)計(jì)其messages實(shí)現(xiàn)時(shí),會(huì)盡可能做到物盡其用,對(duì)于不滿足的則更多的是在應(yīng)用層來(lái)處理,這樣可以保證很好的靈活性以及擴(kuò)展性,下面就來(lái)講講我現(xiàn)在理解的facebook messages實(shí)現(xiàn),請(qǐng)各位看官拍磚,:)

Facebook的這套新的messages實(shí)現(xiàn)用于email/sms/即時(shí)聊天等多個(gè)場(chǎng)景中,大附件或大內(nèi)容的存儲(chǔ)使用haystack(Facebook用其來(lái)存圖片),小數(shù)據(jù)則在N次對(duì)比(MySQL、Cassandra和HBase)后選擇了hbase,結(jié)合hbase的特性,facebook設(shè)計(jì)了如下的schema:


表名未知,:),maybe是usermetadata,單個(gè)用戶的數(shù)據(jù)存儲(chǔ)在同一行中(多個(gè)版本),有4個(gè)cf:
1、Action Logs
記錄用戶messages的變化日志,同時(shí)也將有利于實(shí)現(xiàn)跨表的事務(wù)問(wèn)題,版本為時(shí)間戳,在將舊的inbox的數(shù)據(jù)遷移到新的messages時(shí),facebook的做法就是根據(jù)舊的數(shù)據(jù)形成了actionlogs,然后再用mapreduce直接寫(xiě)入hbase,而它提到的另外一點(diǎn)好處就是,如果將來(lái)metadataentities/indexes要改的話,也只需要reply action logs就行了。
2、Metadata Entities
記錄metadata的實(shí)際信息,例如threads、messages信息,版本為action logs的id。
3、Metadata Indexes
記錄metadata的索引信息,以k/v方式為值,例如已讀的、未讀的等,版本為action logs的id。
4、Keyword Search Index
根據(jù)lucene parse出來(lái)的結(jié)果存儲(chǔ)的keyword –> message的索引,每列就是一個(gè)keyword,版本為messageId。
在這個(gè)設(shè)計(jì)中,facebook很巧妙的借助了hbase的{rowkey,cl,version}的有序存儲(chǔ)來(lái)實(shí)現(xiàn)業(yè)務(wù)的需求,另外為了避免hbase隨機(jī)讀的弱點(diǎn),facebook設(shè)計(jì)了一層cache來(lái)盡可能減少對(duì)hbase的查詢。

為了能夠統(tǒng)一的支撐各種message場(chǎng)景,facebook設(shè)計(jì)了一層app serverlogic來(lái)支撐,facebook將cluster劃分為了多個(gè)cell,每個(gè)cell負(fù)責(zé)一部分用戶的請(qǐng)求,cell中的多臺(tái)服務(wù)器按照一致性hash來(lái)組成,之所以這么設(shè)計(jì)一方面是為了保持比較好的擴(kuò)展性,另一方面則是圍繞hbase的特性來(lái)打造的,為了實(shí)現(xiàn)一致性hash以及幫助app端某用戶應(yīng)該到哪臺(tái)服務(wù)器上處理,提供了一個(gè)discoveryservice,這個(gè)service基于zookeeper來(lái)實(shí)現(xiàn),cell中每臺(tái)server在啟動(dòng)后會(huì)向zk注冊(cè),discoveryservice會(huì)注冊(cè)一個(gè)zk的watcher,這樣discovery service就知道server的變動(dòng)狀況了。

當(dāng)用戶發(fā)送一條message時(shí),會(huì)經(jīng)過(guò)這樣的過(guò)程:
1、訪問(wèn)discovery service,獲取處理當(dāng)前用戶的appserver node;
2、向appserver node發(fā)送請(qǐng)求,appserver node會(huì)相應(yīng)的將message的大數(shù)據(jù)存儲(chǔ)到haystack上,組裝message并存儲(chǔ),并根據(jù)message的類型調(diào)用相應(yīng)的處理器進(jìn)行后續(xù)處理。

當(dāng)用戶收到一個(gè)message時(shí),會(huì)經(jīng)過(guò)這樣的過(guò)程:
1、解析message,提取大數(shù)據(jù)部分存儲(chǔ)至haystack;
2、訪問(wèn)discovery service,獲取處理此用戶的appserver node;
3、在hbase中存儲(chǔ)對(duì)此用戶的action log以及message的small bodies;
4、以前facebook采用的是根據(jù)actionlog異步刷入hbase,原因是之前index/entities表里都是粗粒度的schema,同步刷的話比較浪費(fèi),現(xiàn)在則逐漸改為細(xì)粒度的schema了,所以就可以同步寫(xiě)了index/entities和keyword search index了;
5、dirty掉memcache上的一些信息,例如未讀的message數(shù)。
用戶可能會(huì)同時(shí)收到多條message,那么其中有幾個(gè)部分就必須有事務(wù)的支持了,由于任何一個(gè)用戶在某一時(shí)間內(nèi)都只會(huì)在固定的一臺(tái)server上處理,因此facebook的做法就是在app server上來(lái)保證事務(wù)的問(wèn)題。

當(dāng)用戶訪問(wèn)inbox時(shí),會(huì)經(jīng)過(guò)這樣的過(guò)程:
1、訪問(wèn)discovery service,獲取處理此用戶的appserver node;
2、查看本地cache中是否有user的active metadata(第一頁(yè)會(huì)直接cache),有的話就直接進(jìn)行必要的indexjoin,獲取到對(duì)應(yīng)的數(shù)據(jù),返回,沒(méi)有的話就去hbase中裝載并放入本地cache,blog上的數(shù)據(jù)表明,app cache大概能cache5%–10%的users,由于facebook的用戶不是同時(shí)使用,而使用者基本有一定的黏性,因此cache的命中率差不多能到95%。

當(dāng)用戶根據(jù)keyword搜索一條message時(shí),會(huì)經(jīng)過(guò)這樣的過(guò)程:
1、訪問(wèn)discovery service,獲取處理此用戶的appserver node;
2、訪問(wèn)keyword search index列,獲取top n的messageId,由于hbase存儲(chǔ)的有序性,這個(gè)性能還是不錯(cuò)滴,然后從cache的active metadata中獲取實(shí)際的信息。

為了防止大家被誤導(dǎo),原信息請(qǐng)大家查看以下的參考資料。
參考資料:
1、See the Messages that Matter
2、The Underlying Technology of Messages
3、Scaling the Messages Application Back End
4、Inside Facebook Messages’ Application Server
5、Hbase @ Facebook,The technology behind messages