原文地址:http://mp.weixin.qq.com/s?__biz=MzA3MzYwNjQ3NA==&mid=401628413&idx=1&sn=91abfbad4c7dc882e94939042a8785a4&scene=23&srcid=121664TU0fDpPeR324qis7yL#rd
作者簡介:
衛向軍,畢業后在微軟工作五年,接着去了金山雲,做的金山快盤,然后去了新浪微博做平台架構師,現在在三好網,做在線教育的創業公司。
主題:INTO100沙龍
時間:2015年11月21日下午
地點:夢想加聯合辦公空間
正文:
大家下午好。感謝大家支持今天的沙龍,也感謝夢想加給我們提供場地。麥思博是一家軟件研發培訓公司,2007年成立,我們一直專注於軟件研發快速成長,今天Into100沙龍是第14期了,我們每次主題都偏向技術,每次分享是由三個嘉賓分享。這個模式實際上跟全球軟件案例峰會是一致的,用50分鍾時間給你解讀長尾價值。今天第14期主題是千萬級規模高性能,高並發的網絡架構。這次請了了三位技術大咖,第一位分享嘉賓來自前新浪微博架構師,現任三好網CTO,他為我們分享億級用戶下的高並發的網絡架構。
主題演講:
大家下午好,我中午從公司過來的時候,外面一直在下雪,在這兒看到是一個又溫馨又特別的會場。大家離我的距離非常近,所以很感謝麥思博給我這個機會與大家分享探討。
我是衛向軍,畢業后在微軟工作五年,接着去了金山雲,做的金山快盤,然后去了新浪微博做平台架構師,現在在三好網,做在線教育的創業公司。
第一:我會介紹一下架構以及我理解中架構的本質
第二:介紹一下新浪微博整體架構是什么樣的
第三:在 大型網站的系統架構是如何演變的
第四:微博的技術挑 戰和正交分解法解析架構
第五:微博多級雙機房緩存架 構
第六:分布式服務追蹤系統
第七:今天分享的內容 做一下總結
在開始談我對架構本質的理解之前,先談談對今天技術沙龍主題的個人見解,千萬級規模的網站感覺數量級是非常大的,對這個數量級我們戰略上要重視它,戰術上又要藐視它。
先舉個例子感受一下千萬級到底是什么數量級?現在很流行的優步(Uber),從媒體公布的信息看,它每天接單量平均在百萬左右,假如每天有10個小時的服務時間,平均QPS只有30左右。對於一個后台服務器,單機的平均QPS可以到達800-1000,單獨看寫的業務量很簡單。為什么我們又不能說輕視它?第一,我們看它的數據存儲,每天一百萬的話,一年數據量的規模是多少?其次,剛才說的訂單量,每一個訂單要推送給附近的司機、司機要並發搶單,后面業務場景的訪問量往往是前者的上百倍,輕松就超過上億級別了。
今天我想從架構的本質談起之后,希望大家理解在做一些建構設計的時候,它的出發點以及它解決的問題是什么。
架構,剛開始的解釋是我從知乎上看到的。什么是架構?有人講,說架構並不是一個很懸乎的東西,實際上就是一個架子,放一些業務和算法,跟我們的生活中的晾衣架很像。更抽象一點,說架構其實是對我們重復性業務的抽象和我們未來業務拓展的前瞻,強調過去的經驗和你對整個行業的預見。
我們要想做一個架構的話需要哪些能力?我覺得最重要的是架構師一個最重要的能力就是你要有戰略分解能力。 這個怎么來看呢,第一,你必須要有抽象的能力,抽象的能力最基本就是去重,去重在整個架構中體現在方方面面,從定義一個函數,到定義一個類,到提供的一個服務,以及模板,背后都是要去重提高可復用率。第二,分類能力。 做軟件需要做對象的解耦,要定義對象的屬性和方法,做 分布式系統的時候要做服務的拆分和模塊化,要定義服務 的接口和規范。 第三,算法(性能),它的價值體現在提升系統的性能,所有性能的提升,最終都會落到CPU、內 存、IO和網絡這4大塊上。
這一頁PPT舉了一些例子來更深入的理解常見技術背后的架構理念。第一個例子,在分布式系統我們會做MySQL分庫分表,我們要從不同的庫和表中讀取數據,這樣的抽象最直觀就是使用模板,因為絕大多數SQL語義是相同的,除了路由到哪個庫哪個表,如果不使用Proxy中間件,模板就是性價比最高的方法。第二看一下加速網絡的CDN,它是做速度方面的性能提升,剛才我們也提到從CPU、內存、IO、 網絡四個方面來考慮,CDN本質上一個是做網絡智能調度優 化,另一個是多級緩存優化。第三個看一下服務化,剛才已經提到了,各個大網站轉型過程中一定會做服務化,其 實它就是做抽象和做服務的拆分。第四個看一下消息隊列,本質上還是做分類,只不過不是兩個邊際清晰的類,而是把兩個邊際不清晰的子系統通過隊列解構並且異步化。
接下我們看一下微博整體架構,到一定量級的系統整個架構都會變成三層,客戶端包括WEB、安卓和IOS,這里就不說了。接着還都會有一個接口層,有三個主要作用:
第一個作用,要做安全隔離,因為前端節點都是直接和用戶交互,需要防范各種惡意攻擊;
第二個還充當着一個流量控制的作用,大家知道在2014年春節的時候,微信紅包每分鍾8億多次的請求,其實真正到它后台的請求量, 只有十萬左右的數量級(這里的數據可能不准),剩余的流量在接口層就被擋住了;
第三,我們對PC端和移動端的需求不一樣的,所以我們可以進行拆分。接口層之后是后台,可以看到微博后台有三大塊:一個是平台服務,第二是搜索,第三是大數據。到了后台的各種服務其實都是處理的數據。 像平台的業務部門,做的就是數據存儲和讀取,對搜索來說做的是數據的檢索,對大數據來說是做的數據的挖掘。
微博其實和淘寶是很類似的。一般來說,第一代架構, 基本上能支撐到用戶到百萬級別,到第二代架構基本能支 撐到千萬級別都沒什么問題,當業務規模到億級別時,需 要第三代的架構。
從LAMP的架構到面向服務的架構,有幾個地方是非 常難的,首先不可能在第一代基礎上通過簡單的修修補補 滿足用戶量快速增長的,同時線上業務又不能停, 這是我們常說的在飛機上換引擎的問題。前兩天我有一個朋友問 我,說他在內部推行服務化的時候,把一個模塊服務化做完了,其他部門就是不接。我建議在做服務化的時候,首先更多是偏向業務的梳理,同時要找准一個很好的切入點, 既有架構和服務化上的提升,業務方也要有收益,比如提 升性能或者降低維護成本同時升級過程要平滑,建議開始 從原子化服務切入,比如基礎的用戶服務, 基礎的短消息服務,基礎的推送服務。 第二,就是可以做無狀態服務, 后面會詳細講,還有數據量大了后需要做數據Sharding, 后面會將。第三代架構要解決的問題,就是用戶量和業務 趨於穩步增加(相對爆發期的指數級增長),更多考慮技術框架的穩定性, 提升系統整體的性能,降低成本,還有對整個系統監控的完善和升級。
我們通過通過數據看一下它的挑戰,PV是在10億級別, QPS在百萬,數據量在千億級別。我們可用性,就是SLA要求4個9,接口響應最多不能超過150毫秒,線上所有的故障必須得在5分鍾內解決完。如果說5分鍾沒處理呢?那會影 響你年終的績效考核。2015年微博DAU已經過億。我們系統有上百個微服務,每周會有兩次的常規上線和不限次數的緊急上線。我們的挑戰都一樣,就是數據量,bigger and bigger,用戶體驗是faster and faster,業務是more and more。互聯網業務更多是產品體驗驅動,技術在產品體驗 上最有效的貢獻,就是你的性能越來越好。每次降低加載一個頁面的時間,都可以間接的降低這個頁面上用戶的流失率。
下面看一下第三代的架構圖以及我們怎么用正交分 解法闡述。 我們可以看到我們從兩個維度,橫軸和縱軸可 以看到。一個維度是水平的分層拆分,第二從垂直的維 度會做拆分。水平的維度從接口層、到服務層到數據存儲 層。垂直怎么拆分,會用業務架構、技術架構、監控平台、 服務治理等等來處理。我相信到第二代的時候很多架構已 經有了業務架構和技術架構的拆分。我們看一下,接口層 有feed、用戶關系、通訊接口;服務層,SOA里有基層服務、 原子服務和組合服務,在微博我們只有原子服務和組合服 務。原子服務不依賴於任何其他服務,組合服務由幾個原 子服務和自己的業務邏輯構建而成 ,資源層負責海量數據 的存儲(后面例子會詳細講)。
技術框架解決獨立於業務的海量高並發場景下的技 術難題,由眾多的技術組件共同構建而成。在接口層, 微博使用JERSY框架,幫助你做參數的解析,參數的驗證, 序列化和反序列化;資源層,主要是緩存、DB相關的各類 組件,比如Cache組件和對象庫組件。
監控平台和服務治理,完成系統服務的像素級監控, 對分布式系統做提前診斷、預警以及治理。包含了SLA 規則的制定、服務監控、服務調用鏈監控、流量監控、錯 誤異常監控、線上灰度發布上線系統、線上擴容縮容調度
系統等。
下面我們講一下常見的設計原則。
首先是系統架構三個利器:
一個,我們RPC服務組件(這里不講了),第二個,我們消息中間件 。消息中間件起的作用:可以把兩個 模塊之間的交互異步化,其次可以把不均勻請求流量輸出 為勻速的輸出流量,所以說消息中間件異步化解耦和流量 削峰的利器。第三個是配置管理,它是代碼級灰度發布 以及保障系統降級的利器。
第二個,無狀態,接口層最重要的就是無狀態。我們在電商網站購物,在這個過程中很多情況下是有狀態的, 比如我瀏覽了哪些商品,為什么大家又常說接口層是無狀態的,其實我們把狀態從接口層剝離到了數據層。像用戶在電商網站購物,選了幾件商品,到了哪一步,接口無狀態后,狀態要么放在緩存中,要么放在數據庫中,其實它並不是沒有狀態,只是在這個過程中我們要把一些有狀態的東西抽離出來到了數據層。
第三個,數據層比服務層更需要設計,這是一條非常重要的經驗。對於服務層來說,可以拿PHP寫,明天你可以拿JAVA來寫,但是如果你的數據結構開始設計不合理, 將來數據結構的改變會花費你數倍的代價,老的數據格式向新的數據格式遷移會讓你痛不欲生,既有工作量上的,又有數據遷移跨越的時間周期,有一些甚至需要半年以上。
第四,物理結構與邏輯結構的映射,上一張圖看到兩個維度切成十二個區間,每個區間代表一個技術領域, 這個可以看做我們的邏輯結構。另外,不論后台還是應用 層的開發團隊,一般都會分幾個垂直的業務組加上一個基 礎技術架構組,這就是從物理組織架構到邏輯的技術架構 的完美的映射,精細化團隊分工,有利於提高溝通協作的 效率。
第五,www.sanhao.com的訪問過程,我們這個架構圖里沒有涉及到的,舉個例子,比如當你在瀏覽器輸入 www.sanhao網址的時候,這個請求在接口層之前發生了什么?首先會查看你本機DNS以及DNS服務,查找域名對應的IP地址,然后發送HTTP請求過去。這個請求首先會到前端 的VIP地址(公網服務IP地址),VIP之后還要經過負載均 衡器(Nginx服務器),之后才到你的應用接口層。在接口 層之前發生了這么多事,可能有用戶報一個問題的時候, 你通過在接口層查日志根本發現不了問題,原因就是問題 可能發生在到達接口層之前了。
第六,我們說分布式系統,它最終的瓶頸會落在哪里 呢?前端時間有一個網友跟我討論的時候,說他們的系統遇到了一個瓶頸, 查遍了CPU,內存,網絡,存儲,都沒有問題。我說你再查一遍,因為最終你不論用上千台服務器還是上萬台服務器,最終系統出瓶頸的一定會落在某一 台機(可能是葉子節點也可能是核心的節點),一定落在 CPU、內存、存儲和網絡上,最后查出來問題出在一台服務 器的網卡帶寬上。
接下來我們看一下微博的Feed多級緩存。我們做業務的時候,經常很少做業務分析,技術大會上的分享又都偏向技術架構。其實大家更多的日常工作是需要花費更多時間在業務優化上。這張圖是統計微博的信息流前幾頁的訪 問比例,像前三頁占了97%,在做緩存設計的時候,我們最 多只存最近的M條數據。 這里強調的就是做系統設計要基 於用戶的場景,越細致越好。舉了一個例子,大家都會 用電商,電商在雙十一會做全國范圍內的活動,他們做設 計的時候也會考慮場景的,一個就是購物車,我曾經跟相 關開發討論過,購物車是在雙十一之前用戶的訪問量非常 大,就是不停地往里加商品。在真正到雙十一那天他不會 往購物車加東西了,但是他會頻繁的瀏覽購物車。針對這 個場景,活動之前重點設計優化購物車的寫場景, 活動開 始后優化購物車的讀場景。
你看到的微博是由哪些部分聚合而成的呢?最右邊的是Feed,就是微博所有關注的人,他們的微博所組成的。 微博我們會按照時間順序把所有關注人的順序做一個排序。 隨着業務的發展,除了跟時間序相關的微博還有非時間序的微博,就是會有廣告的要求,增加一些廣告,還有粉絲 頭條,就是拿錢買的,熱門微博,都會插在其中。分發控制,就是說和一些推薦相關的,我推薦一些相關的好友的微博,我推薦一些你可能沒有讀過的微博,我推薦一些其他類型的微博。 當然對非時序的微博和分發控制微博,實 際會起多個並行的程序來讀取,最后同步做統一的聚合。
這里稍微分享一下, 從SNS社交領域來看,國內現在做的比較好的三個信息流:
-
微博是基於弱關系的媒體信息流;
-
朋友圈是基於強關系的信息流;
-
另外一個 做的比較好的就是今日頭條,它並不是基於關系來構 建信息流,而是基於興趣和相關性的個性化推薦信息流。
信息流的聚合,體現在很多很多的產品之中,除了SNS, 電商里也有信息流的聚合的影子。比如搜索一個商品后出來的列表頁,它的信息流基本由幾部分組成:第一,打廣 告的;第二個,做一些推薦,熱門的商品,其次,才是關鍵字相關的搜索結果。信息流開始的時候很簡單,但是到后期會發現,你的這個流如何做控制分發,非常復雜, 微博在最近一兩年一直在做這樣的工作。
剛才我們是從業務上分析,那么技術上怎么解決高並發,高性能的問題?微博訪問量很大的時候,底層存儲是 用MySQL數據庫,當然也會有其他的。對於查詢請求量大的時候,大家知道一定有緩存,可以復用可重用的計算結果。 可以看到,發一條微博,我有很多粉絲,他們都會來看我
發的內容,所以微博是最適合使用緩存的系統,微博的讀寫比例基本在幾十比一。
微博使用了雙層緩存,上面是L1,每個L1上都是一組 (包含4-6台機器),左邊的框相當於一個機房,右邊又是一個機房。在這個系統中L1緩存所起的作用是什么?首先, L1緩存增加整個系統的QPS,其次以低成本靈活擴容的方式增加系統的帶寬。想象一個極端場景,只有一篇博文, 但是它的訪問量無限增長,其實我們不需要影響L2緩存, 因為它的內容存儲的量小,但它就是訪問量大。這種場景下,你就需要使用L1來擴容提升QPS和帶寬瓶頸。另外一個場景,就是L2級緩存發生作用,比如我有一千萬個用戶, 去訪問的是一百萬個用戶的微博 ,這個時候,他不只是說 你的吞吐量和訪問帶寬,就是你要緩存的博文的內容也很 多了,這個時候你要考慮緩存的容量,第二級緩存更多的是從容量上來規划,保證請求以較小的比例穿透到后端的數據庫中,根據你的用戶模型你可以估出來,到底有百分之多少的請求不能穿透到DB, 評估這個容量之后,才能更好的評估DB需要多少庫,需要承擔多大的訪問的壓力。
另外,我們看雙機房的話,左邊一個,右邊一個。兩個機房是互為主備,或者互為熱備 。如果兩個用戶在不 同地域,他們訪問兩個不同機房的時候,假設用戶從IDC1過來,因為就近原理,他會訪問L1,沒有的話才會跑到 Master,當在IDC1沒找到的時候才會跑到IDC2來找。同時 有用戶從IDC2訪問,也會有請求從L1和Master返回或者到 IDC1去查找。 IDC1和IDC2,兩個機房都有全量的用戶 數據,同時在線提供服務,但是緩存查詢又遵循最近 訪問原理。
還有哪些多級緩存的例子呢?CDN是典型的多級緩存。 CDN在國內各個地區做了很多節點,比如在杭州市部署一個節點時,在機房里肯定不止一台機器,那么對於一個地區 來說,只有幾台服務器到源站回源,其他節點都到這幾台服務器回源即可,這么看CDN至少也有兩級。
Local Cache+分布式緩存,這也是常見的一種策略。 有一種場景,分布式緩存並不適用,比如單點資源的爆發 性峰值流量,這個時候使用Local Cache + 分布式緩存, Local Cache在應用服務器上用很小的內存資源擋住少量的極端峰值流量,長尾的流量仍然訪問分布式緩存,這樣的Hybrid緩存架構通過復用眾多的應用服務器節點,降低 了系統的整體成本。
我們來看一下Feed的存儲架構,微博的博文主要存在 MySQL中。首先來看內容表,這個比較簡單,每條內容一個索引,每天建一張表,其次看索引表,一共建了兩級索引。 首先想象一下用戶場景,大部分用戶刷微博的時候,看的 是他關注所有人的微博,然后按時間來排序。仔細分析發 現在這個場景下, 跟一個用戶的自己的相關性很小了。 所以在一級索引的時候會先根據關注的用戶,取他們的前條微博ID,然后聚合排序。我們在做哈希(分庫分表)的 時候,同時考慮了按照UID哈希和按照時間維度。很業務和時間相關性很高的,今天的熱點新聞,明天就沒熱度了, 數據的冷熱非常明顯,這種場景就需要按照時間維度做分表,首先冷熱數據做了分離(可以對冷熱數據采用不同的 存儲方案來降低成本),其次, 很容止控制我數據庫表的爆炸。像微博如果只按照用戶維度區分,那么這個用戶所有數據都在一張表里,這張表就是無限增長的,時間長了 查詢會越來越慢。二級索引,是我們里面一個比較特殊的場景,就是我要快速找到這個人所要發布的某一時段的微博時,通過二級索引快速定位。
分布式追蹤服務系統,當系統到千萬級以后的時候, 越來越龐雜,所解決的問題更偏向穩定性,性能和監控。 剛才說用戶只要有一個請求過來,你可以依賴你的服務 RPC1、RPC2,你會發現RPC2又依賴RPC3、RPC4。分布式服 務的時候一個痛點,就是說一個請求從用戶過來之后,在 后台不同的機器之間不停的調用並返回。
當你發現一個問題的時候,這些日志落在不同的機器 上,你也不知道問題到底出在哪兒,各個服務之間互相隔 離,互相之間沒有建立關聯。所以導致排查問題基本沒有 任何手段,就是出了問題沒法兒解決。
我們要解決的問題,我們剛才說日志互相隔離,我們 就要把它建立聯系。建立聯系我們就有一個請求ID,然后結合RPC框架, 服務治理功能。假設請求從客戶端過來, 其中包含一個ID 101,到服務A時仍然帶有ID 101,然后調 用RPC1的時候也會標識這是101 ,所以需要一個唯一的請求ID標識遞歸迭代的傳遞到每一個相關節點。第二個, 你做的時候,你不能說每個地方都加,對業務系統來說需 要一個框架來完成這個工作,這個框架要對業務系統是最低侵入原則,用JAVA的話就可以用AOP,要做到零侵入的原則,就是對所有相關的中間件打點,從接口層組件 (HTTP Client、HTTP Server)至到服務層組件(RPC Client、RPC Server),還有數據訪問中間件的,這樣業 務系統只需要少量的配置信息就可以實現全鏈路監控 。為什么要用日志?服務化以后,每個服務可以用不同的開發 語言,考慮多種開發語言的兼容性,內部定義標准化的日志是唯一且有效的辦法。
最后,如何構建基於GPS導航的路況監控?我們剛才講分布式服務追蹤。分布式服務追蹤能解決的問題,如果單一用戶發現問題后,可以通過請求ID快速找到發生問 題的節點在什么,但是並沒有解決如何發現問題。我們看 現實中比較容易理解的道路監控,每輛車有GPS定位,我想 看北京哪兒擁堵的時候,怎么做? 第一個,你肯定要知 道每個車在什么位置,它走到哪兒了。其實可以說每個車 上只要有一個標識,加上每一次流動的信息,就可以看到 每個車流的位置和方向。其次如何做監控和報警,我們怎 么能了解道路的流量狀況和負載,並及時報警。我們要定 義這條街道多寬多高,單位時間可以通行多少輛車,這就 是道路的容量。有了道路容量,再有道路的實時流量,我 們就可以基於實習路況做預警?
對應於分布式系統的話如何構建? 第一,你要定 義每個服務節點它的SLA是多少?SLA可以從系統的CPU占 用率、內存占用率、磁盤占用率、QPS請求數等來定義,相 當於定義系統的容量。 第二個,統計線上動態的流量, 你要知道服務的平均QPS、最低QPS和最大QPS,有了流量和 容量,就可以對系統做全面的監控和報警。
剛才講的是理論,實際情況肯定比這個復雜。微博在 春節的時候做許多活動,必須保障系統穩定,理論上你只要定義容量和流量就可以。但實際遠遠不行,為什么?有 技術的因素,有人為的因素,因為不同的開發定義的流量 和容量指標有主觀性,很難全局量化標准,所以真正流量 來了以后,你預先評估的系統瓶頸往往不正確。實際中我 們在春節前主要采取了三個措施:
第一,最簡單的就是有 降級的預案,流量超過系統容量后,先把哪些功能砍掉, 需要有明確的優先級 。
第二個,線上全鏈路壓測,就是把 現在的流量放大到我們平常流量的五倍甚至十倍(比如下 線一半的服務器,縮容而不是擴容),看看系統瓶頸最先 發生在哪里。我們之前有一些例子,推測系統數據庫會先 出現瓶頸,但是實測發現是前端的程序先遇到瓶頸。
第三, 搭建在線Docker集群, 所有業務共享備用的Docker 集群資源,這樣可以極大的避免每個業務都預留資源,但 是實際上流量沒有增長造成的浪費。
接下來說的是如何不停的學習和提升,這里以Java語 言為例,首先,一定要理解JAVA;第二步,JAVA完了以后, 一定要理解JVM;其次,還要理解操作系統;再次還是要 了解一下Design Pattern,這將告訴你怎么把過去的經 驗抽象沉淀供將來借鑒;還要學習TCP/IP、分布式系統、 數據結構和算法。
最后就是我想說的就是今天我所說的可能一切都是錯的! 大家通過不停的學習、練習和總結, 形成自己的一套架構 設計原則和方法,謝謝大家。