前言
今天很高興有機會給大家分享支付寶的開發經驗,具體的內容將分成三個部分展開:
支付寶架構設計與發展;
支付寶的敏捷釋出與穩定性保障;
支付寶架構的優勢與賦能。
1. 支付寶架構設計與發展
首先看一下支付寶的發展歷史,最開始支付寶只是作為支付功能支援淘寶業務,後來逐步發展成為獨立的 App,並從簡單的支付功能衍生出轉賬、水電煤支付等生活服務,現在的支付寶已經成為一個多應用生態的超級 App。生活中你想做任何事情,幾乎都可以在支付寶上實現。
截止目前,支付寶實名註冊使用者已經超過了 8 億,日活數億。在研發上面,僅 Android、iOS 客戶端開發人員近千人,客戶端程式碼行數超過了數百萬行,Android 版本支付寶的工程數業已近千個,每個工程都有獨立的開發 owner 負責某一個具體的模組。雖然工程師團隊及工程量越發龐大,支付寶依舊能夠做到日釋出的頻率以確保業務快速迭代。即使業務功能日益複雜,但 App 閃退率僅 0.01%。
那麼,為了達到這樣的業務指標,我們做了什麼呢?
隨著網際網路的應用場景進一步豐富,使用者對互動、體驗的要求也越來越高。由此,我們需要在 App 的各方面下足功夫,比如客戶端的執行流暢性、最大努力降低電量、流量的能耗。
我們最近在推的一個專案便是支付寶的網路統一:在客戶端建立一條與服務端的長連線,通過這個長連線通道進行網路通訊協議的封裝,進行業務網路請求的通訊。一般情況下我們們每次髮網路請求都會建立一個 HTTP 三次握手,這樣會有一定的網路延遲和沒必要的流量浪費,支付寶為了解決這樣的問題做了網路統一庫,提升了網路通訊效率。
此外,支付寶針對掃一掃功能的效能優化,不僅體現在掃碼的及時反饋,也包括對於二維碼的識別範圍相對競品會更廣。同時,國內有著名手機廠商直接在自身的手機系統中整合了支付寶的掃一掃元件,作為系統預設的掃碼能力,這從側面反映了支付寶在細分領域的優勢。
再來談談支付寶如何應對複雜的使用環境:我認為最典型的複雜使用環境的即手機沒網的情況下支付寶如何做到順利開啟付款碼?
一般情況下,出於安全考慮,付款碼僅在幾個小時內有效。但支付寶內建了阿里大安全部門提供的黑匣子,通過黑匣子能夠有效保障移動端安全,因此即使你在弱網環境或長時間未開啟付款頁面,支付寶依然能夠順利完成支付。
接下來,我們來了解看看這一系列效能優化的背後,技術架構是如何設計和實現的。
這是支付寶客戶端的總體架構圖,從下往上看:
- 最底層是支付寶框架的容器層,包括類載入資源載入和安全模組;
- 第二層是我們抽離出來的元件層,包括網路庫,日誌庫,快取庫,多媒體庫,日誌等等,簡單說這些是一些通用的能力層;
- 第三層是我們定製的框架層,這是關鍵部分,是我們得以實現上千人,上千多個工程共同開發一個 App 的基礎;
- 第四層是基於框架封裝出來的業務服務層;
- 第五層便是具體的業務模組,其中每一個模組都是一個或多個具體的工程。為了搭建超級 App 的並行開發模式,我們必須保證模組與模組之間的低耦合,實現外掛式靈活的開發,因此整體業務分為了上千多個工程。
上千個工程低耦合邏輯上是沒有問題,比如你開發網路庫工程,他開發掃一掃工程,我開發動態釋出工程,我們們程式碼可以完全沒有耦合性,但又能如何做到相互依賴?
支付寶移動端的技術架構設計靈感便來源於 Java 的 OSGi 模組化思想:內部對每個工程都叫做 bundle 工程,每個 bundle 有相應程式碼和開發資源。實際上,每個 bundle 工程都是一個 apk 工程,只是比 apk 多了一個 bundle 描述檔案。這三個東西非常關鍵,回到剛才說的工程與工程之間存在依賴,對方要如何引用?
工程之間的依賴關係只有兩種:
- 第一種:程式碼層面的依賴(即我需要呼叫對方寫的程式碼);
- 第二種:資源層面的依賴(即我需要引用對方定義的資源,比如佈局或者樣式等)。
從程式碼轉成可執行的程式可以簡單分成兩個時期:
-
- 編譯期;
-
- 執行期。
在支付寶的架構裡,編譯參與的部分是和執行期參與的部分是分離的:編譯期使用 bundle 的介面包,執行期使用 bundle 包本身。bundle 的介面包是 bundle 包的一部分,即剛才說的 bundle 的程式碼部分。bundle 的資源包同時打進介面包,在編譯期提供給另一個 bundle 引用。
傳統模式中,依賴的 SDK 既參與編譯,也參與執行。我們定製了編譯流程,使得編譯與執行的依賴內容分離。
問題來了,如何保證 App 在執行期程式碼和資源不缺失呢?支付寶有一個容器工程(即 portal 工程),該工程中配置了所有 bundle 包的引用,在 portal 中把所有 bundle 包本身合併,最終形成 apk 包本身。這樣 bundle 引用 bundle 的介面包,僅參與編譯,最終所有 bundle 在 portal 中彙集,這樣編譯期沒問題,執行期也沒問題,從而實現了工程與工程之間依賴的解耦。
完成工程間的解耦,那麼如何解決程式碼之間存在的強依賴關係?
舉個簡單例子,你引用了某個 Bundle 介面包中類 A 裡的方法 m,這個方法 m 因為一些業務原因需要變動,那麼你就被迫需要同時改動程式碼。針對這樣的問題,我們通過框架層面提供的微服務來解決,簡單來說,即面向介面程式設計:具體實現與呼叫的介面分離,因此只要保證介面包不變,具體實現可以由開發者任意調整而不影響使用方。
以上是程式碼與程式碼之間的解耦,接下來再看看業務與業務之間的解耦:
假設某個業務首頁一開始叫 ActivityA,後來因為業務優化導致這個業務首頁的入口被改,那麼跳轉到業務首頁的使用方同樣跟進改動。同理,對於 H5 前端來說,在不同平臺想要跳轉到同一個業務,但這兩個平臺跳轉的入口是不一樣的,前端怎麼跳轉?支付寶是通過框架層面的微服務和微應用來實現解耦的:
微服務是通過面向介面開發、引用,然後在框架中動態註冊的方式來實現的介面隔離解耦。 微應用是通過預先定義好一個數字來唯一標識一個業務模組,然後動態註冊到框架中,具體這個業務模組中有什麼,入口叫什麼,完全由開發負責人自己決定。 所有框架註冊的微應用微服務最終會在框架啟動中動態載入。
那麼大家是否會有疑問,支付寶這麼多工程師團隊和工程量,微服務微應用之間仍需要協同配合,那麼支付寶釋出一個版本是不是很麻煩?
2. 支付寶的敏捷釋出與穩定性保障
傳統的開發模式下大家勢必互相影響,假如你提交的程式碼閃退了或者業務異常了,那麼你業務關聯的開發測試一定會受到影響,一般我們稱這樣的開發模式是「序列開發,互相等待」。而支付寶的工程解耦,程式碼解耦能給工程效能帶來多大的效益?接下來我們來聊一聊支付寶的「並行開發模式」。
支付寶每次釋出一個版本,都是由若干個 bundle 工程組成。若干個 bundle 工程合併在一起叫基線,每次釋出版本之後,參與下一個版本迭代的同學都基於這個穩定的基線做獨立的開發。而之所以能夠支援獨立開發,歸功於我們介紹的工程解耦和程式碼解耦。
比如這張框架圖,餘額寶業務、ofo 小程式、螞蟻森林、網路庫等可以同時開發測試完成之後進入某一個大版本釋出即可,如果存在依賴關係,只需要找和自己相關同學一起進發布,正因為如此支付寶做到了每天都有很多業務進基線,每天都在同時迭代業務。
既然釋出確保了效率,那麼支付寶又是如何保證釋出穩定性的? 傳統的開發模式是:開發測試、全量發版,然後進入下個版本的迭代,繼續開發測試修 bug、全量發版。這樣做有幾個缺點:
- 如果測試階段未發現缺陷,很可能導致線上使用者出現大規模異常;
- 出現大規模異常沒有修復能力,造成使用者損失;
- 隨著業務的迭代,業務越來越多,功能越來越複雜,卡頓現象頻出,使用者體驗變差;
- 關鍵業務監控不到位,不知道使用者怎麼使用 App,或者出現業務異常無感知,傳統的開發模式裡開發測試的比重基本上佔到了 90% 甚至 100%。
而對於支付寶來說,開發測試僅佔整體工程量 25%,以下即相應的工作拆解:
3. 支付寶架構的優勢與賦能
最後,支付寶所有在移動端開發方面的技術積累和架構實踐,已經作為螞蟻金服金融科技的一部分對外開放,即我們今天見到的螞蟻金服 mPaaS。mPaaS(Mobile Platform As A Service),源於支付寶 App 的移動開發平臺,為移動開發、測試、運營及運維提供雲到端的一站式解決方案,能有效降低技術門檻、減少研發成本、提升開發效率,協助企業快速搭建穩定高質量的移動 App。
秉承著「給世界帶來小而美的變化」的理念,我們通過 mPaaS 幫助 12306 這樣的國民級 App 重構了客戶端,使得大家可以用上一個好的體驗的 App 進行出行購票,用 mPaaS 這樣成熟的底層框架搭建一個 12306 僅需要 2-3 個月的時間。除了 12306 還有支付寶香港版,廣發銀行發現精彩客戶端,同樣在短短几個月的時間內便完成了業務重構。
那麼,對你來說,模組化與解耦式開發是否還有更好的方式?歡迎留言與我們一起探討。