乾貨|建議初創團隊起初也要構建分散式應用 本文內容整理自W-Time技術分享沙龍-天津站現場演講《一切都是分佈的》,演講者:李傲,問啊聯合創始人,前中交車聯網總架構。 好多人都會問什麼是架構師?其實架構師的定義很寬泛,前端後端的定義都不一樣。作為後端出身的架構師,我認為後端並不是大家想的封裝組件,它 ...
乾貨|建議初創團隊起初也要構建分散式應用
本文內容整理自W-Time技術分享沙龍-天津站現場演講《一切都是分佈的》,演講者:李傲,問啊聯合創始人,前中交車聯網總架構。
好多人都會問什麼是架構師?其實架構師的定義很寬泛,前端後端的定義都不一樣。作為後端出身的架構師,我認為後端並不是大家想的封裝組件,它要定義的是規劃,規劃模塊之前的關係。在一臺機器搞不定時怎麼辦?答:集群!這詞說著很容易,但真要給你,你發現how?怎麼去加?
有人問我,架構師要做什麼?我認為,架構師就是要在軟體起初階段就能夠從情景當中預先想到這問題,通過架構分散式解決方案,預先把問題都埋好。可能有人會說這算不算重度設計?其實所謂重度設計,要看團隊的基本能力,如果團隊寫代碼還寫不利索的情況下,那麼這個設計就很重要。因為不能指望一個代碼都沒有寫過幾行的人去寫架構,這個是不可能的,做分散式都很難。如果團隊能力屬於中上層,那麼有兩種可能:1、團隊做過十幾年的代碼,但架構差一些,那麼也會把程式寫的非常漂亮,因為對代碼有把控能力,對底層的研究比較透徹;2、代碼寫的並不是很好,但是架構師對新技術有瞭解,那麼也是有可能做好分散式的。如果你的團隊做不到以上這些,那隻有一種方法--用伺服器抗。但伺服器是一回事,團隊能力也不能太差。
下麵就我們“問啊”來說給大家做一下簡單的分享,“問啊”是一款訂製化IT教育平臺,可以一鍵呼叫大牛解決IT 問題。
關於分散式存儲
“問啊”利用了一些分散式的概念,也就是分散式存儲。我們都知道,每一臺伺服器資源占用都是有限的,我們用的是雲伺服器,一般上來都會去買本地磁碟,但無論容量多大,總有一天會用完,當這一天到來的時候,有兩種可能:1、在你不知道的時候,服務死了,怎麼也起不來。可能會不運行、cpu過高,有人會問,這什麼原因導致的?計算量太大了?其實不是,因為一直在做無謂的io操作;2、無從下手,有個公司叫emc的做的磁碟可以解決,可是太貴,300萬左右的價格初創公司老闆肯定不批,而且性能也不能保證。
但是分散式存儲就把這個事解決了,將要存儲的文件預先就散出去,而不是放在一個地方。拿的時候就很快的知道在哪了。舉個例子,就像大家都有房子,每個房子都有地址,這個地址都是預先規劃好的,就跟分散式存儲一樣,在規劃的時候就解決了未來如果數據膨脹會怎麼樣的問題。
關於高速緩存
接下來再說高速緩存,這也是一個分散式緩存的概念,對存儲和緩存來講,在演算法的選擇上是不一樣的,數據分為兩種,維度數據和事實數據。維度數據可以理解為是一種屬性,人也是一種維度數據,這個數據是有盡頭的。事實數據就是我出門的時候可能會踩死一隻螞蟻,但我不知道我這一路會踩死多少只,這就是事實數據。包括廣大的女性同胞養活了淘寶,這也是最好的例子,都是事實數據。
事實數據存儲和維度數據是不一樣的,大家一般按時間存儲是最多的,這樣數據在取得時候有一些優勢--可以單獨拿一段出來。在數據結構中,拿一段怎麼拿?肯定得是連續存儲,這樣對cpu的消耗則是最小的。那維度數據呢,數據量本身就不大,我們用hash是比較靠譜的。
對於記憶體和磁碟的存儲來講,磁碟是一個碟片,我們在尋找一個道的時候,我們會連續去打,這樣是最快的。因為不會跳針,跳針速度是最慢的。但記憶體不是,記憶體是隨機存儲,我們在利用存儲的時候也會考慮到,如果對於連續存儲來講,記憶體就一定比磁碟快嗎?在我看來,不一定。
關於任務隊列
我之前做車聯網,包括現在的滴滴打車都是一樣的,大家之所以能打到車,是車的位置信息已經上傳上來了。因為在同一時間內會爆發很多小高峰,導致我存數據的時候很慢,所以我不直接存儲,我先存到另一層去。為什麼要存到另一層呢?我們存儲的目的就是為了要拿出來,簡單的說:存的目的就是拿。其實存和拿是一個悖論,好存不好拿,好拿不好存。舉一個例子,這麼說可能可以直觀一些。上大學時,放學回宿舍,書包就扔在地上。為什麼?因為懶,扔的速度特別快,但是你可能轉天再找就找不到了。問題就出在存儲快但是沒有索引。想要快速查到怎麼辦?那就在存的時候分門別類,細緻存好。這樣拿比較快,存的時候就會費一些時間。如何解決這個悖論?讀寫分離。存和查的時候一定要做分離。軟體有一個理論叫“解偶”,如何解偶?簡單來說,a層和b層之間加一個c層就是解偶。只要你發現兩層之間有一個很緊密的聯繫,你就往裡面加一個。
所以,任務隊列就是這樣,我們先把東西寫到隊列里,讓它慢慢的去消費。也就是說你把數據拿到隊列裡面來的時候,我就認為你存了。等你取得時候,看你的消費水平,消費水平慢,最多就一個感覺:這個更新好慢,僅此而已。然後下一步就該研究怎麼把這個消費做快,你是有這個機會的。但如果你這個事兒不這麼做,你就會發現,在你存的時候就已經死了。這樣用戶的感受可能會有兩種,一個是用戶並不知道,他只會覺得你們公司的數據比較少;二是我知道,你們已經死了。這樣的用戶體驗度是非常不好的。
關於高速查詢
這裡我要講一下我們查詢的數據結構。對於每種查詢來說,數據結構都不太一樣。現在有一種比較好的既能查又能存的數據結構叫LSM-TREE,就是hbase一個數據結構,大家有興趣可以看一下。
如果你是一個應用,你可能會發現其實沒有現成的東西可以用,怎麼辦?那就用最短的時間研發出來一個輕量級的分散式,像任務下發。這種東西其實比較占資源,當你的伺服器數量不夠龐大,節點數量也不夠龐大的時候,你拿到的結果可能跟你最初的預想不太一樣。
互聯網公司都有個比較難受的事兒,就是做運維不敢動機器。我們在做“問啊”的時候也沒有完全做到不停機,這個挺難的。有的時候為了嘗試試錯,我們需要做降級指標,其實有時候比升級指標難的多,會丟數據、丟節點,之後數據還能夠均攤,這個是挺難的。我們現在降級指標還沒有做到完全不停機,但升級指標已經能夠做到了。其實這就是各種負載均衡,通過負載均衡來代替ha,那麼這兩個概念是什麼概念呢?ha,高可用。負載均衡,就是就是你一個人搞不定,四五個人一起上!ha是什麼?一個活著,另一個就得死。一般用負載均衡來解決高可用有個問題,就是機器不閑著。一般來講,做機器空閑的話,總有一臺機器是不幹活的,這個對於初創公司來說成本比較高,配置選擇就是個難題。所以各種ha方案一般就是共用,那個節點可能會分攤好多個節點的共用,但是突然間發現兩個小高峰,兩個死了這個也就算死了。對於loadbalance本身有個非常大的優勢,就是本身來講,只要有1/2以上活了,這服務也就認為是活的,這個是比較好保證的。但是有一點,做lb演算法就一定要做到線上的配額一定要高於你目前的配額,否則你是沒法做的。
下麵分享一下我們用過得一些東西:
Redis
一個基於記憶體的存儲。最大優勢就是單線程處理的。肯定有人問為什麼做單線程,很慢的!但是如果你測過的話就會發現,單線程多任務有時候不一定就比多線程多任務慢,多線程多任務有個空閑的概念,交替的概念,我要去調度。這個就完全省去調度,只要存儲速度特別快,能讓一個線程別空閑了,遠比多線程多任務要快。單線程有一個不可替代的優勢:無鎖,可以做到一致性。多線程本身來講只要併發對一個數據操作的時候,你就必須得加鎖。你的鎖如果設計不合理的話,你這個數據本來可以加到5的,結果才加到2就沒了。
如果用redis完全不用考慮這些事。包括我們做訂單號的話,單號取一個數,這是個最普通業務。我們對單號+1就會考慮一個線程去做,如果可以搞定的話,那速度就快了,那我為什麼要去做多線程?那麼做多線程有一個方法,你可以先把訂單號切割,就是預計今年的訂單號能有多少,你先把他分好了。然後每一個進程怎麼樣操作,這樣不會出問題。但是訂單是跳躍,永遠不要用訂單號去做排序,這是最不合理的。就像大家都玩過搖紅包,其實都是一樣的,搖紅包為什麼能做到這麼快。其實不可能快,這個事情不是一個快的動作,有交互就不可能快。只能先把交互的紅包預先切好了,存在緩存里。甚至最狠的是用一些空池,就是沒獎。結果你的ip正好打到這一核了,hash到這一核里。不換ip拿不出來。
Redis本身來講,它的存儲速度包括查詢速度是可以上到10萬級的,是非常快的。但是我們線上測的話。可能也是因為我們用的是雲平臺的一個服務,它本身是基於codis做的分散式服務。我們沒有時間去做自己的分散式redis,所以codis已經成為我們的瓶頸了。它大概的速度也就是1w,對於剛起步的應用來說應該夠用,那如何做到更快,只有一個方法:當你對一個事兒做到不能更快了,就三個字解決:分散式。這樣就肯定能解決,下一步再去琢磨這一個事該怎麼解決,這個就更高深了,有機會我會再詳細的為大家講解。
Hbase
有人說Hbase是大數據,其實我覺得很詫異,我認為大數據更偏重於分析。這個東西本身就是一個nosql ,為什麼要放到大數據呢?也許因為存的數據多?
一開始起步的時候是三個節點,這算大數據嗎?這算是實驗。其實不是,它就是一個資料庫,大家不要對它有偏見,認為非要數據到多少的時候我把它遷過來,記住,數據量大的那天,你再想遷就遷不過來了。我的經驗告訴我,重構可以做局部模塊重構,但絕對不能做大遷移。只要做大遷移:丟數據、版本核對,動態版本核對,特別頭疼。
我最怕跳槽,因為我的位置是救火的。經驗告訴我這個最好提前設計好。因為到那天了你難受,有兩方都不滿意:第一,用戶那裡,一直在用,你就得天天看著,怎麼還有用戶用?不知道該用高興還是該不用高興;第二,產品經理、運營就會使勁催你問你好了嗎?但是提前規劃好,就可以避開這些問題。
Hbase本身來講呢,既然它能存這麼多數據,我們也不想太浪費。有兩套出報表的方案,一套是大家熟知的spark,可能用的比較多。對於一個初創團隊來講,spake本身它的worker節點配置有點高,因為它專門吃記憶體的。還有一種。Tkeyang24:06,替代hadoop,但是它本身沒有計算模塊,你還是上s,逃不過去。我們的方案呢,作為一個初創團隊來講,對於運營這一塊可能有點殘忍,但是沒有辦法,沒有這麼大的數據量,沒有必要。
那麼插入數據方案呢,就兩個:
1、通過集成phoenix序列化方法使用原生Hbase API插入更新數據,數據裝載速率並沒有降低;
2、插入數據時採用雙保證,緩存和hbase均需要完全插入成功,否則記錄日誌進入修複隊列非同步保證數據同步。
高速緩存
關於高速緩存,剛剛有說過, redis最好的方案就是hash。這裡說兩個應用,Redis本身有個pub/sub,做數據隊列的,併列時隊列比較麻煩的是,如果想用它做一對一隊列,你是做不了的,其實一對一有很多隊列。但是作為初創團隊沒必要,那如何來解決這個問題呢?很簡單。我們分散式里,分三個redis,我這一個應用先用三個redis,寫數據的時候我在三個裡面做shard,你這一個應用shard這三個,那這一個應用就可以把這三個歸過來我用,如果兩個應用怎麼辦?有兩種方法,一種方法從k上,我監控不同的shard的隊列,a監控a隊列,b監控b隊列,每個人都監控三個,沒問題,但還有一個問題,redis本身來講,提供一個key失效的概念,緩存一定會有時效時間,但是緩存失效之後,他所定義的隊列的名字是寫死的。是跟十六庫綁定的,我們可以切16個作為拓展。如果十六不夠的話,切兩組作為拓展,這樣的話就可以無限的擴大。B監聽b的零號庫。這樣可以無限的擴大這樣可以通過闊機器,來提升一個人的消費能力。另一方面通過縱深,來分散消費能力。
關於Redis2.8.22,可能用的比較多。3.0和3.2用的並不多。這有個毛病,TTL只要move一次庫,這個TTL就廢了,就是失效時間。本來是想用什麼巧妙的方法來做黑科技的,結果發現2.8.23解決了,所以大家用的時候從2.8.23以上用。
我們在任務隊列主要做的事是,我們應用有個紅包的概念,為了讓大家都有學習的機會,能夠去問問題,所以我們有個紅包。如果用分散式的話,會有很多坑就可以繞過去了。
Pub/sub,有一個比較噁心的事,當你消費的時候,如果消費函數,你沒有消費下一條的時候,pub/sub是不往後挪數據的,所以他會大量的堆積在redis里出不來,這個一定要註意。
我推薦一個工具,現在有不少公司都在用這個:ES。這個還是比較好用的,但是現在有個問題:隨機函數。它裡面可以隨機抽量數據,在隨機抽量數據的時候我們發現兩個坑,1、散不開,大部分都一樣,隨機不太好;2、隨機速度特別慢,100w就開始特別慢了。做抽量的話,其實還有很多可以試一下。
關於我們自主研發的,主要就是以下這幾點:1、針對服務節點運行數據決定任務的下發;2、任務之間沒有任何牽扯;3、任務死亡可以實時分裂新任務繼續執行;4、支持定時消息及紅包的應用。
我發現很多人有個誤區,不寫代碼就真的省事嗎?我覺得吧,不寫代碼滿足需求最省事,用現成的有時候還不如自己寫,有什麼問題也好找。
關於不停機運維
Zookeeper這個東西挺好的。我現在註冊一些服務,立刻就會建上臨時的鏈接,臨時文件。比較坑人的地方就是它的存儲是限量的。每個節點存儲都一樣。一定要註意,存儲要省著使用,別寫進數據,不要用它做分散式事務所。Zabbix挺好用的,我們運維現在就使用這個。