Erlang+C+Lisp的大數(shù)據(jù)方案:BugSense
譯文【51CTO精選譯文】編輯注:本文作者是BugSense公司的創(chuàng)始人兼首席技術(shù)官Jon Vlachogiannis。BugSense是一家專門報(bào)告錯(cuò)誤、度量質(zhì)量的服務(wù)商,每天跟蹤成千上萬個(gè)應(yīng)用程序。移動(dòng)應(yīng)用程序崩潰時(shí),BugSense可以幫助開發(fā)人員準(zhǔn)確查明問題,并解決問題。這家新興公司的客戶包括VMWare、三星、Skype以及數(shù)千家獨(dú)立應(yīng)用軟件開發(fā)商。跟蹤2億多種設(shè)備需要有一套運(yùn)行速度快、容錯(cuò)性強(qiáng)、成本又低廉的基礎(chǔ)設(shè)施。
在最近六個(gè)月,我們決定使用自己的BigData基礎(chǔ)設(shè)施,為用戶們提供評(píng)估應(yīng)用程序性能和穩(wěn)定性的度量指標(biāo),好讓他們知道出現(xiàn)的錯(cuò)誤給其用戶群和經(jīng)營(yíng)收入帶來了怎樣的影響。
我們清楚,自己的解決方案自一開始就應(yīng)該具有良好的擴(kuò)展性,因?yàn)橥饷嫠械闹悄苁謾C(jī)中超過4%會(huì)開始向我們發(fā)送大量數(shù)據(jù),實(shí)際上無異于分布式拒絕服務(wù)攻擊(DDoS)。
我們希望能夠做到以下這幾點(diǎn):
■抽取應(yīng)用程序邏輯,并以JSON格式向?yàn)g覽器傳送數(shù)據(jù)
■實(shí)時(shí)運(yùn)行復(fù)雜算法
■嘗試處理數(shù)據(jù),不需要專門的Hadoop集群
■預(yù)處理數(shù)據(jù),然后存儲(chǔ)數(shù)據(jù)(減少存儲(chǔ)空間)
■能夠在每一個(gè)節(jié)點(diǎn)上處理1000多個(gè)并發(fā)請(qǐng)求
■每個(gè)應(yīng)用程序?qū)Τ^1.25億行進(jìn)行“聯(lián)結(jié)”操作
■不需要把大筆錢投入到服務(wù)器上,即可做到這一點(diǎn)
解決方案使用了:
■在Azure平臺(tái)上運(yùn)行的不到20個(gè)大實(shí)例
■內(nèi)存中數(shù)據(jù)庫(kù)
■使用C語言編寫的完全成熟的自定義LISP語言,以實(shí)現(xiàn)查詢,其速度要比讓虛擬機(jī)(附帶廢料收集器)一直在線運(yùn)行快好多倍。
■用于節(jié)點(diǎn)之間聯(lián)系的Erlang語言
■經(jīng)過改動(dòng)的TCP_TIMEWAIT_LEN,足足少了4萬條連接,節(jié)省了處理器、內(nèi)存和TCP緩沖器等方面的資源。
內(nèi)存中數(shù)據(jù)庫(kù)萬歲!
我們知道,我們要處理這一切流量,唯一的辦法就是使用內(nèi)存中數(shù)據(jù)庫(kù)。
為了迅速解答針對(duì)龐大數(shù)據(jù)集的臨時(shí)特定問題(比如“一個(gè)多星期以來,多少不重復(fù)的三星設(shè)備用戶遇到了這個(gè)特定的錯(cuò)誤?”),你不僅要應(yīng)對(duì)內(nèi)存方面的局限性,還要應(yīng)對(duì)在數(shù)據(jù)處理前后的數(shù)據(jù)串行化和并行化工作。這就是我們啟動(dòng)LDB項(xiàng)目的原因。
LDB項(xiàng)目介紹
你是否相信能做到這點(diǎn):可以將來自不同數(shù)據(jù)源(甚至上千個(gè)不同數(shù)據(jù)源,如移動(dòng)設(shè)備)的數(shù)據(jù)傳送到某個(gè)系統(tǒng)中,描述你用僅僅幾行代碼想提取什么樣的信息,然后讓所有這些信息觸手可及?而且以實(shí)時(shí)方式實(shí)現(xiàn),同時(shí)不影響系統(tǒng)的正常運(yùn)行?
LDB與其說是個(gè)數(shù)據(jù)庫(kù),還不如說是款應(yīng)用服務(wù)器。盡管它是內(nèi)存中數(shù)據(jù)庫(kù),但是數(shù)據(jù)實(shí)際上存儲(chǔ)在硬驅(qū)中,并且復(fù)制到其他節(jié)點(diǎn)上。
有了LDB,我們并不運(yùn)行查詢,而是運(yùn)行算法,因?yàn)槲覀冇幸环N使用C語言編寫的完全成熟的自定義LISP語言,它可以訪問與數(shù)據(jù)庫(kù)同樣的地址空間。這意味著,你能夠以極快的速度搜索數(shù)據(jù)、增加計(jì)數(shù)器以及進(jìn)行g(shù)et/put操作等。
擁有LISP的優(yōu)點(diǎn)在于,你可以輕松創(chuàng)建類似SQL的語言,比如Hive,并且實(shí)時(shí)查詢數(shù)據(jù),就像這樣:
LDB工作起來像這樣:
每一個(gè)應(yīng)用程序都有各自的LDB。這意味著它有自己的內(nèi)存空間。這樣一來,我們就可以把就流量而言更龐大的應(yīng)用程序輕松轉(zhuǎn)移到不同的機(jī)器上。
當(dāng)請(qǐng)求來自移動(dòng)設(shè)備時(shí),主LDB節(jié)點(diǎn)接受連接(使用erlang線程池),并將該數(shù)據(jù)轉(zhuǎn)發(fā)到某個(gè)特定的數(shù)據(jù)庫(kù)(DB)。這種請(qǐng)求處理機(jī)制是用不到20行Erlang代碼實(shí)現(xiàn)的。這也是我們選擇Erlang用于節(jié)點(diǎn)之間聯(lián)系的另一個(gè)原因。
當(dāng)請(qǐng)求被“流式傳送”到LDB后,一個(gè)名為“process.lql”的文件負(fù)責(zé)分析和標(biāo)記數(shù)據(jù),并創(chuàng)建各種計(jì)數(shù)器。這一切都是實(shí)時(shí)進(jìn)行的,對(duì)每個(gè)請(qǐng)求而言都是如此。
我們之所以能夠這么做,是因?yàn)獒槍?duì)每個(gè)請(qǐng)求,開啟我們的LISP虛擬機(jī)、處理所有這些過程仍比讓虛擬機(jī)(附帶廢料收集器)一直在線運(yùn)行快好多倍。
借助LDB,我們只要用短短的3行代碼,就能創(chuàng)建時(shí)間序列和聚合數(shù)據(jù)。
比如說,下列代碼為不重復(fù)用戶創(chuàng)建了7天的時(shí)間序列:
替代辦法
我們?cè)跍y(cè)試過程中發(fā)現(xiàn),SQL數(shù)據(jù)庫(kù)之所以不是很適合,正是由于這一點(diǎn):我們的數(shù)據(jù)以非結(jié)構(gòu)化數(shù)據(jù)為主,我們需要許多復(fù)雜的“聯(lián)結(jié)”(和許多索引)。另一方面,就NoSQL數(shù)據(jù)庫(kù)而言,我們無法在系統(tǒng)運(yùn)行的同時(shí)針對(duì)數(shù)據(jù)運(yùn)行我們的算法,而擁有mappers/reducers使得整個(gè)事情處理起來既復(fù)雜又緩慢。我們需要一種沒有大鎖或數(shù)據(jù)庫(kù)鎖的高并發(fā)系統(tǒng),可以用僅僅幾KB就能跟蹤數(shù)百萬個(gè)不重復(fù)事件,而且、擴(kuò)展起來很容易。
一個(gè)很好的替代辦法就是使用Stream數(shù)據(jù)庫(kù)(比如Storm)。我們的主要問題在于,單個(gè)節(jié)點(diǎn)有許多變化因素,而且存在性能問題。借助LDB,我們有了這個(gè)優(yōu)點(diǎn):能夠以極快的速度處理數(shù)據(jù)(數(shù)據(jù)駐留在同一個(gè)內(nèi)存空間),把它們作為聚合計(jì)數(shù)器或符號(hào)來存儲(chǔ)(因而能夠?qū)?shù)GB數(shù)據(jù)塞入到數(shù)KB空間),然后讓特定領(lǐng)域語言(DSL)來實(shí)時(shí)處理我們想要進(jìn)行的任何關(guān)聯(lián)操作。沒有串行化/并行化,沒有網(wǎng)絡(luò)調(diào)用,也沒有廢料收集器。這就好比是將匯編代碼映射到你的數(shù)據(jù)上。
除此之外,借助LDB,我們有了可以擴(kuò)展并處理入站數(shù)據(jù)的接收器、一切僅用幾行代碼就可以定義的流式組件以及存儲(chǔ)引擎(Storage Engine)和復(fù)制引擎(Replication engine)。
優(yōu)化內(nèi)核
較之于每秒處理大量請(qǐng)求的其他服務(wù),我們進(jìn)行分析時(shí)的獨(dú)特地方在于,移動(dòng)設(shè)備與服務(wù)器之間的對(duì)話極少(3個(gè)TCP握手?jǐn)?shù)據(jù)包、1個(gè)有效載荷數(shù)據(jù)包和3個(gè)TCP終結(jié)數(shù)據(jù)包)。
不過,TCP在設(shè)計(jì)時(shí)并沒有考慮到這種情況(也就是說設(shè)備之間的對(duì)話很少),而是實(shí)現(xiàn)了一種名為TIME_WAIT的狀態(tài)(在2.6 Linux內(nèi)核中持續(xù)時(shí)間約1分鐘):上一個(gè)FIN(完成)數(shù)據(jù)包被發(fā)送后,該特定連接tuple的TCP狀態(tài)仍保持一段時(shí)間的開啟狀態(tài),目的是為了接收可能延誤的任何雜散數(shù)據(jù)包(也就是說,在連接關(guān)閉之前)。不過就我們的情況而言,這用處不大(我們希望酷似UDP行為,但又有TCP保障),因?yàn)橛行лd荷只是1個(gè)數(shù)據(jù)包(瀏覽請(qǐng)求多達(dá)4個(gè)或5個(gè)數(shù)據(jù)包),于是我們決定改動(dòng)內(nèi)核源代碼,把這個(gè)參數(shù)的常量減小到20秒。結(jié)果就是足足少了4萬條連接,數(shù)量驚人,因而節(jié)省了處理器、內(nèi)存和TCP緩沖器等方面的資源。
我們所打的補(bǔ)丁在這個(gè)文件中:
linux-kernel-source/include/net/tcp.h #define TCP_TIMEWAIT_LEN (60*HZ)
換成
#define TCP_TIMEWAIT_LEN (20*HZ)
使用這種架構(gòu),我們就能為我們的所有付費(fèi)客戶提供實(shí)時(shí)分析和了解移動(dòng)應(yīng)用程序的功能,不到20個(gè)大實(shí)例在Azure平臺(tái)上運(yùn)行,包括退守服務(wù)器和備用服務(wù)器。
原文:BigData Using Erlang, C And Lisp To Fight The Tsunami Of Mobile Data