網易云音樂這3年經歷了用戶量的爆炸式增長,作為一款口碑爆棚的音樂社區App,在業務快速發展的背景下,后端的架構又經歷了怎樣的發展歷程呢?
本文回顧了一個大型互聯網應用的后端發展歷程,其中涉及到的一些技術內部基本有自研的替代品,出于通用性考慮替換成了相應的開源產品。
本文的讀者對象假設已經具備一定計算機基礎,如果你希望了解一個億級用戶互聯網應用的服務端會涉及到哪些技術,那么請繼續讀下去吧~
從聽一首歌說起
故事從你打開網易云音樂的APP,點開一個歌單聽歌開始。那么為了滿足你聽歌的簡單愿望,我們的服務端攻城獅們都做了些啥呢?
你點開歌單,發起了一個HTTP請求,為了響應你的請求,我們搭建了一臺服務器運行服務端程序,我們選擇免費開源的linux+Tomcat。
>> 企鵝和貓啥時候成搭檔了
為了編寫服務端程序,需要一種編程語言和服務端編程框架,我們選擇了業界流行的JAVA+Spring/SpringBoot。
>> 喝著咖啡就把代碼寫了,碼農的春天來了
我撐不住了
服務器有了,代碼也寫好了,現在我們可以開始服務不同用戶的各種請求了。有一天你發現突然歌單打不開了,歌也聽不了了,看上去服務器掛了。我們的工程師緊急聯系PE發現服務器的load很高,CPU、內存、網卡都吃緊,看上去是我們的熱心用戶太多,服務器容量已經撐不住了。
經過這件事之后,大家一致覺得只有一臺機器太不靠譜了,晚上都沒法踏實睡覺。單臺服務器無法支撐日益增長的業務請求,CPU、內存、網卡都到了瓶頸,而且一旦掛了就形成單點故障,影響服務的可用率,這時我們又添了幾臺機器搭建成集群,通過分布式集群來實現業務的平滑擴展。
>> 腦補的分布式集群
太費資源了吧
服務器越來越多,如果一個物理機只運行一個服務端進程,資源無法得到充分利用,這時我們引入虛擬化技術(KVM,Docker)在一臺物理機上運行多臺虛擬機,每個虛擬機共享底層的物理機資源(CPU、內存、網卡、硬盤),將物理資源進行虛擬化按需分配,這就是云計算。
業務穩步增長,機器越來越多,我們發現每天中午和晚上是用戶活躍的高峰期,機器的load會隨著升高,其他時間段則是低谷,機器則比較空閑。不同的請求量對服務器的資源配置提出了不同的要求,如果高峰期可以自動擴容,低峰期自動縮容,就可以實現資源的最大化利用,避免浪費,這時我們需要生產級別的容器編排系統(K8S)。
負載均衡
用戶操作客戶端時實際上是向指定的域名(music.163.com)發起HTTP調用,這個時候需要去DNS服務器查詢域名對應的服務器IP,為了對外暴露統一的IP,實現后端服務器的負載均衡,我們在客戶端和后端服務器間加入了一層反向代理(Nginx),如此一來所有的服務端API都通過Nginx來轉發,服務的擴容可以只用修改Nginx增加內網機器IP就行。
>>Nginx相當于一個統一的出口代理
業務快速發展,接口爆炸式增長,每次上線都配置Nginx不僅繁瑣,而且容易帶來穩定性隱患,此時迫切需要一個API網關來統一收攏所有的API注冊和露出。
數據存哪兒?
用戶開始使用我們的服務之后,存儲用戶的各種數據需要數據庫,除了關系型數據庫(MySQL),還有各種NoSQL數據庫(Hbase,MongoDB)。隨著數據容量的不斷增長,單機數據庫已經無法滿足需求,這時自帶分庫分表的分布式數據庫就開始派上用場了。
寫數據庫的增刪改查代碼比較繁瑣,需要一個ORM框架(Mybatis)來實現對象和關系型數據庫之間的映射,這樣讀取數據的時候,每一行數據都可以映射成內存中的一個對象,存儲數據的時候每個對象又可以映射成數據庫的一行數據。
用戶量增加后,大量日常的文件存儲(如用戶頭像、朋友圈圖片)需要一個統一的文件存儲中心,這時我們用上了分布式對象存儲平臺。
性能不行?緩存來湊
開放數字專輯售賣功能之后,我們發現用戶的購買熱情非常高,為了應對高并發大流量,減輕數據庫的壓力,我們引入分布式緩存(Memcache,redis)來抗一部分流量,避免數據庫宕機。
解耦神器,非你莫屬
為了避免業務的上游和下游強耦合,實現調用異步化,或者應對突發流量,實現業務的削峰填谷,我們用到了消息隊列(Kafka,RabitMQ,RocketMQ),將上游業務消息緩存到隊列中,由下游業務異步消費。
分解巨無霸
業務快速發展,團隊也在擴張,原有的巨無霸單點應用部署已經越來越制約業務的快速迭代和多人協同開發。為了避免單點,實現業務模塊的去耦合和故障隔離,我們開始了服務化拆分進程。
我們將原本部署在一個進程中的代碼拆分到多個進程放到不同的集群中運行。服務化拆分之后相互之間調用不再是一個進程內調用,而是需要跨進程網絡調用,這時需要注冊中心和RPC框架來實現服務的注冊、發現及跨進程調用。引入RPC框架之后,為了方便監控和在線調整線程池參數,又開發了RPC管控平臺實現動態管控。
排查問題
隨著服務拆分的深入,用戶的一個請求可能會經過幾個甚至幾十個服務,如何定位問題出現在哪個服務,以及查看各個服務的鏈路耗時,這時需要一個鏈路跟蹤系統。服務拆分之后,根據重要程度對服務進行定級(P級),為了保障核心應用的穩定性和可用性,引入了限流熔斷降級框架,可以實現應用網關層的全局限流和單機限流和降級。
線上服務越來越多,機器越來越多,日志分散,可檢索性差,為了統一的日志查詢,方便及時定位問題,需要一套日志平臺(ELK)。
團隊開發
業務越來越復雜,團隊開始爆炸增長,需求迭代相互交織,開發、回歸、預發、線上環境管理越來越復雜,此時迫切需要一個持續集成平臺管理環境和機器的自動分配、任務排期和發布計劃。
為了實現前后端同步開發,接口的定義要優先于接口的開發,這時我們開發了一個接口管理平臺來達到事前約束的目的。前后端開發可以愉快地協同開發了。測試同學也可以利用平臺管理測試用例,接口覆蓋率得到了極大的提升。
看好你的報警
線上接口越來越多,各種異常滿天飛,有些需要關注,有些可以忽略,這時我們需要一個異常報警平臺,根據服務將報警通過各種渠道通知到負責人。
隨著業務模型越來越復雜,老舊的架構開始腐化,鏈路也變得越來越脆弱,線上事故頻發,9999的可用率無法得到保證,這時我們需要進行架構梳理和全鏈路壓測,針對性地進行故障注入,全鏈路壓測平臺和故障注入平臺應運而生。
終極之路
隨著用戶量的數量級升級,單個機房的集群部署穩定性風險越來越高,可能光纖被挖斷或者機房斷電之后,服務就全部掛了,帶來的損失不可估量。這時,同城雙機房和異地多活單元化就提上了議事日程。
終于,服務的穩定性得到了極大提升,你可以放心地聽歌了。好了,不多說了,我又來了一沓新需求。
下面是一張時間軸,展示了后端基礎設施發展的整個歷程,囊括了企業基礎設施、基礎運維設施、研發提效設施、中間件基礎設施、測試基礎設施、大數據基礎設施等六大基礎設施。
后記:
本文中涉及到的絕大部分技術方案都已經在內部落地,有一些還在進展中,不過相信很快就可以應用到生產環境。學無止境,勉勵自己在后端技術之路能越走越遠,成為一個懂業務的技術專家。
來源:網易工程師-王勝
整理總結不易,如果覺得這篇文章有意思的話,歡迎轉發、收藏,給我一些鼓勵~
有想看的內容或者建議,敬請留言!
最近利用空余時間整理了一些精選Java架構學習視頻和大廠項目底層知識點,需要的同學歡迎私信我發給你~一起學習進步!有任何問題也歡迎交流~
Java日記本,每日存檔超實用的技術干貨學習筆記,每天陪你前進一點點~