出處:https://mp.weixin.qq.com/s/3kYarzQcXY1-4t57DGn9Eg
貝殼找房小程式至今為止已經擁有了近2億的使用者,團隊正在在朝著貝殼人的宗旨邁進,給這個行業創造更多的價值,努力成為一個能夠服務2億家庭的品質居住平臺。
經過兩年多的發展,為了更好的適應業務發展,貝殼小程式後端從最初的快速搭建到後來由php到golang的轉型與最佳化,到現在微服務的落地和業務閘道器能力的提升,貝殼小程式平臺一直走在蛻變的路上。而貝殼找房小程式和app的上線,也意味著一直在房產行業裡深耕的鏈家,從直營模式到貝殼找房平臺化模式的正式轉變。
小程式平臺同樣傳承了公司平臺化的轉變理念和模式,把平臺化思路堅決貫徹下去,在完成平臺需求的同時,沉澱平臺核心能力,賦能各個業務,甚至賦能給整個行業。
1 小程式平臺1.0
貝殼找房小程式起始於2018年,1.0時代小程式團隊主要是在追趕功能,團隊在半年時間內完成了1年應該完成的業務。半年期間小程式產研團隊快速擴增,此期間大量的進行API層開發,封裝各業務介面,以快速完成第一版的小程式為目標。
1.1 極速上線小程式後臺服務
極速上線公司級小程式平臺要面對的問題是什麼呢?因為貝殼小程式平臺的特殊屬性,各個業務例如新房、二手、租賃、裝修都是分屬在各團隊維護的,所以需要有平臺業務方來整體對小程式的效能做分析和監控。
首先是每日效能監控,團隊做了第一版的效能分析日報,把小程式內所有域名的請求量、平均響應時間、499以及5XX狀態碼的數量佔比、併發峰值、qps高峰時間、穩定性百分比等等,以穩定性日報的形式輸出給團隊所有成員,並且詳細的列出了所有介面的情況,做到小程式內所有的服務,平臺都能“心中有數”。二是小程式平臺自身介面的實時預警,接入了公司Fast監控平臺,實時監控各介面的效能指標情況,包括域名錯誤、SQL異常、第三方請求超時、業務日誌ERROR、服務請求異常等等,保證極速上線的小程式能健康的執行。
1.2 如何抗住100倍流量的九宮格?
全部小程式的統一帶來的風險就是流量的瞬間上漲,不僅僅是各小程式流量的整合,微信九宮格的流量也會瞬間湧入小程式平臺,於是專案組開始了介面的最佳化和拆分,首先對預估流量進行了一次壓測,發現效能瓶頸在redis叢集上,記憶體使用率達到了90%以上,但實際瓶頸並不在於redis本身,在於當前redis叢集的記憶體管控上,這裡我提一下為什麼DBA需要對redis叢集進行記憶體管控呢?首先是貝殼業務線較多,每個微服務都可以申請獨立的redis叢集,運維成本是比較大的,另外如果記憶體超過20G,高可用的切換後,恢復的時長就可能會超過5分鐘,一定程度增加了故障發生的機率,所以這個記憶體的管控上還是有必要的。
那該如何最佳化呢?第一反應是特定業務場景的擴容,雖然說有20G記憶體的限制,但是如果業務場景特殊性,這個擴容也是可以支援的,不過如果在應用層面就能最佳化,何樂而不為?我們透過監控指標發現redis叢集的cpu佔用和I/O使用都是在正常的範圍,唯獨記憶體佔用嚴重超標,經過分析發現是房源詳情大json和小程式首頁佔用了較大的記憶體,一方面我們選擇了合適的壓縮演算法,對redis大value進行壓縮,減少了redis的將近一半以上的記憶體佔用,另一方面,我們對小程式首頁的redis使用了pconnet長連線,避免頻繁建立短連線的效能損耗。
簡單的最佳化,也給給團隊帶來了一些思考,以後在對面redis的最佳化,在平臺側要保持一定的容量buffer可擴容,避免出現容量危機,另一方面,可以利用壓縮、切分等來提高效能。
1.3 沉澱核心服務
技術上,也沉澱了一些核心能力,例如第一版小程式的分享能力的建設,經紀人透過小程式進行分享後,可以記錄核心的分享鏈條,包括經紀人使用者關係、閱讀場景、停留時長等等。
上圖是第一版使用者分享行為上報的核心鏈路,當用戶掃碼以及授權後,會建立經紀人和使用者之間的關係,計算出相關的業務指標並落庫,經紀人在後臺就可以看到使用者的訪問資訊、閱讀時長等等,這部分能力隨著業務迭代進行過幾次比較大的升級,當前也透過大資料等手段完善了原有的分享能力。
可以看到的是核心分享能力的沉澱,對業務可以有較大幫助,業務方可以透過使用者與經紀人的行為分析,更精準、更有效的給那些真正有購房需求的使用者更大的幫助。分享能力的平臺化建設可以拓展的範圍也是可以預見的,除了之前核心B2C的場景,C2C也是未來可以深入的一個方向,其中包括使用者分享與使用者裂變的行為資料鏈能力的建設。可以沿著平臺化思路的方向,和大資料推薦團隊合作,做一些定向的營銷方案、打包推薦等等。
2 小程式平臺2.0
隨著業務量的上升,小程式使用者數量急劇增長,面對越來越高的QPS,必須要保持敬畏之心,團隊準備開啟2.0的升級,大膽的嘗試從php往golang的躍遷,率先在把之前php api層的流量往golang 閘道器切換,團隊在golang層上也做了很多最佳化。
首先說下為什麼要選擇在閘道器層引入golang。關於php和golang的選擇近些年的爭議不斷,當然了PHP是最好的語言,也得益於PHP的易用性和穩定性,才得以讓小程式能夠在這麼短的時間內完成1.0的上線。但是PHP在安全性、併發性等方面也有一些不足之處,那golang有什麼自身的優勢呢?首先是goruntine協程,它作為golang的主要代表成員之一,是golang中的併發執行單位,它也是輕量級的執行緒,由Go(runtime)管理,Go程式會智慧的將goroutine中的任務合理的分配給每個CPU。在面對高額QPS的請求,golang會更適合做閘道器。
2.1 golang改造,突破高併發瓶頸
在傳統golang專案中,團隊做了第一次的壓測,效果並不理想,當時我們進行了pprof的效能分析。
上圖是cup的效能分析,發現高QPS下,系統GetLocalIp獲取函式佔用了4.91s的大效能消耗,這個效能消耗是比較可怕的,另外Log的寫入也有一定程度的效能消耗,佔用了1.92s,同時大家也分析了一下高QPS下的記憶體分配情況。
從上圖也可以看出。對於ip的獲取,使用記憶體達到了20M,而分配的累計值達到了29G之多,所以採取了一些最佳化方案,改變了獲取本地ip的方式,從快取中獲取,一次寫入多次讀取,並且把log的寫入方式改成非同步寫入。之後,整體的效能和效果得到了一個較大的提升。
上圖可以看到單機下,golang閘道器的抗壓QPS可以達到11000QPS,而CPU的使用率還不到40%,而相同機器配置的PHP服務已在4000+QPS壓測的時候,機器CPU已經達到80%,逼近極限,閘道器的改造達到了我們的心裡預期。這也是為什麼使用golang作為小程式閘道器的原因之一。
2.2 整體架構-沉澱小程式平臺核心架構
2.3 爭當業務線“保衛官”
這裡要提一下閘道器層在小程式平臺的必要性,微信對原生小程式內的HTTP請求是有域名限制的,因此新業務已經沒有辦法再分配更多的域名,故而新的業務方想要接入小程式的話,只能接入小程式的閘道器,走統一域名。而小程式的閘道器自然而然的成為了接入小程式平臺的“必經之路”,我們接下來要開啟閘道器能力建設了,因為閘道器的能力也是真正能夠賦能給業務方的,讓大家放心的接入,小程式平臺可以成為業務線的“保衛官”。
3 小程式平臺3.0
完成2.0時代的最佳化之後,要更多關注的是清晰的系統架構,關注資料量大之後系統瓶頸的解決、以及關注系統穩定性的建設。
3.1 整體架構-邊界清晰,能力健全
伴隨著公司微服務理念的推進,小程式平臺也逐漸往微服務方向發展,把臃腫的大單體拆分為邊界明確、功能模組清晰的一個個微服務,不僅僅在程式碼層面解耦,部署環境、資料庫、redis等都進行了拆分和解耦,讓小程式平臺慢慢變成一個更為健壯的體系。
上圖是小程式平臺3.0的整體架構圖,從上至下,分了5層結構:
平臺閘道器層:主要接收來自小程式前端的請求,經過路由轉發給各業務api業務API層:負責接收閘道器轉發的請求,處理自己的業務邏輯平臺服務層:小程式平臺核心能力,包括多端使用者&Token管理、多端訊息、小程式搜尋等儲存層:包括了Mysql、Redis以及3.0後新增的TIDB和Hive,下文會對儲存的改進做一個介紹中臺支撐層:公司級中臺能力,用來支撐整個系統的基礎能力,包括房源、客源、人力中臺等3.2 微服務落地
關於微服務的理念,本文不再詳情的描述,近些年來微服務已經是炙手可熱的話題了,相比傳統單體架構,程式的元件是相互依賴的,二不是鬆散耦合。對微服務架構而言,每個模組都是獨立的,可以在不影響其他模組的情況下對單獨的模組進行修改,避免了大單體帶來的影響,如下圖所示。
3.0架構裡的平臺服務層,就是小程式平臺具體的微服務落地場景了,進入到3.0時代,貝殼小程式已經不僅僅只是微信小程式了,百度小程式、快手小程式、快應用、QB小程式也先後加入到了小程式平臺的行列裡,之前多端小程式的處理方式基本上就是在各個函數里做相容,程式碼越發的臃腫,更為可怕的是開發和測試過程要覆蓋的場景也是越來越多,不斷的在鞭策我們做出改變。
首先是把小程式生態內特有的服務劃分出來,於是多端使用者體系和多端訊息體系和小程式搜尋服務成為第一批需要下沉的微服務,從之前的purist專案中拆分出來,每個微服務都擁有新的伺服器和redis資源,相互隔離、邊界清晰。
微信搜尋服務是小程式平臺的另一個微服務,這個服務也是微信生態內特有的,所以他是屬於平臺的另一個核心服務,早期的微信搜尋做的比較簡單,以貝殼房價服務為例,貝殼房價指數資料目前儲存於HIVE底表中,大資料工具的查詢效率往往是比較低的,不適合C端使用者直接查詢,這樣就導致了使用者微信內搜尋召回不了貝殼的服務資料,無疑是貝殼的損失。
這次微服務下沉的過程中,團隊對微信搜尋也做了最佳化,我們打算對城市、城市+小區/樓盤、小區/樓盤/商圈,三個維度對微信的請求資料做了快取預熱,但是小區、樓盤的資料量其實是非常大的,全量的預熱不太現實,所以我們採取了熱點詞的快取預熱方案,首次搜尋的時候以貝殼檢索熱詞預熱,落地微信請求後獲取微信熱詞服務,後續可以直接用微信的熱點詞來做快取預熱,如下圖所示:
3.3 mysql大資料瓶頸最佳化
小程式平臺業務資料也在劇增,不少核心表的資料量已經過億,慢查詢、慢響應越來越多。資料庫的最佳化勢在必行,我們想到了分庫、分表等方案,還有一點非常重要的就是引入了新的工具TIDB。
為什麼要選用TIDB呢?
這裡首先要提一下,2020年TIDB已經被越來越多的國人所知,它是雲原生的分散式資料庫,儲存引擎是FaceBook開源的高效能儲存引擎RocksDB,不僅支援OLTP也支援OLAP,更重要的是它高度相容Mysql協議,也就是說我們可以在不分表的基礎上,實現Mysql的彈性擴容,極大的減小了我們的業務程式碼變動,此外TIDB支援靈活和彈性擴充套件,資料按Region分片,有自帶的排程管理元件,同時TIDB還提供了TISpark這樣的OLTP工具,滿足了我們實時、準確的大資料查詢服務。我們首先在SLA要求不超過99.99%的非核心業務場景率先做了遷移,平滑遷移的非常順利,後續我們又在一些新業務場景上使用了TIDB。
帶來的收益也是比較明顯的:
解決了單點問題,架構更加清晰,可維護性、可擴充套件性也增強了。底層資料庫切換,應用無感知,並且切換成本低。能夠滿足高效能OLTP的業務場景例如分享服務的實時的PV和UV計算,如果是透過傳統大資料方案,大多數只能滿足T+1的場景,而TIspark滿足了業務OLAP的需求。
3.4 系統穩定性建設
系統穩定性建設的原則包括了大系統小做、被依賴服務的穩定性、使用者體驗的容錯性以及有非常強健的穩定性處理方案等等。
系統穩定性架構設計的一大原則是大系統小做,怎麼理解呢?舉個簡單的例子,比如上述提到的使用者登入服務,如果公司要求加入風控邏輯,在沒有微服務拆分之前,這個邏輯是要涉及到整體發版的,然後就會把所有程式碼都帶著發版了,但是這樣可能會造成使用者訊息功能受影響,所以說微服務的落地和系統穩定性的建設就遙相呼應了,把子服務拆開,做獨立的服務。
二是被依賴服務的穩定性就要求我們做的服務和平臺能夠讓所以業務方用的放心、用的可靠,強健的穩定性處理方案就包括了一些及時止損和保護使用者體驗的方案。所以首先,如何成為一個可靠的小程式平臺,是我們要做的重要的事情。
在2.0時代golang專案引入平臺,但它並不是真正意義上的閘道器,嚴格說來,它只是做了業務透傳. 因為並沒有閘道器的實際能力,3.0後在閘道器層做了一些最佳化,為了保證業務系統跑在平臺閘道器上更為安全和穩定,團隊在golang閘道器層引入了go-sentinel、API管理和路由管理,完成了核心的限流、熔斷功能,變成了真正意義上的閘道器層。相比傳統的計數器、令牌桶限流演算法等,sentinel顯得更為強大,它是面向分散式服務架構的輕量級流量控制,不僅能夠解決固定視窗演算法流量激增超過臨界點的問題,也解決了令牌桶漏出速率必須是固定引數的問題,在保證平臺能夠有秒殺場景的支撐能力的同時,也有了更完備的實時監控能力。
上圖是小程式平臺閘道器的一些能力,有一些是還在建設中的,比如熔斷、降級方案,觸發到熔斷後如何做使用者體驗更好的產品呢?或者說如果做一個更為穩定的系統呢?
上圖是我們的一個api降級方案,有一個降級中介軟體和降級服務來完成api降級策略,當用戶請求的時候,即使是請求的服務異常觸發到了熔斷的策略,也能夠讓使用者無感,返回給前端上一次正確的請求結果,當然了這個使用成本也是有的,因為這裡的資料一致性就不能完全保證了,所以說熔斷降級方案還要和具體的業務場景結合起來,千萬不要為了降級而降級。
4 未來展望
微服務領域的細化不難看出,我們在微服務上只是簡單的做了一些大模組的拆分,其實大模組內的領域我們並沒有做精細化的劃分,這是未來我們要做的事情,我們更希望透過領域驅動的模型來協助我們做更深入且合理的設計系統穩定性建設-流量預警我們現在接入了很多監控,服務異常、響應時長的監控等等,不過還缺乏一些流量的監控體系,例如流量環比劇增或者劇降等等,之前發生了不少的問題都是由於沒有流量監控導致問題發生了很久之後才被人工發現,顯然代價是比較高的,流量預警能幫助我們更快的發現一些未知的問題。更多核心服務能力的整合平臺化的道路漫長而艱鉅,我們也需要不停的成長,相信未來一定能看到一個更為健康強壯的小程式平臺。作者: 平臺小程式團隊
出處:https://mp.weixin.qq.com/s/3kYarzQcXY1-4t57DGn9Eg