2017年度的支付寶帳單果真不負衆望,再一次刷屏了。javascript
回顧一下這個年關,現象級的刷屏活動就有三起:css
秀「18歲」;秀網易音樂歌單;秀支付寶帳單。html
一位網友調侃道:2018年大型「相親」節目就此拉開帷幕……前端
秀「18歲」,看顏值;java
秀網易音樂歌單,曬品味;程序員
秀支付寶帳單,炫財富。chrome
「相親」的三個重要指標不就都齊了嗎?!數據庫
玩笑歸玩笑,身爲一名程序員的我,「職業病」又犯了。就像微信出品的「跳一跳」小程序,各類外掛,刷分攻略絡繹不絕,佔領各大技術媒體頭條。小程序
因而乎,我決定探究一下支付寶帳單背後的技術實現。但這並不意味着我會再造一個支付寶帳單出來,畢竟這份帳單核心部分是背後的海量消費數據,這並非我等刁民能獲取獲得的。後端
因爲我的經驗水平有限,本文所做猜測若有不足之處,敬請指正。若是有螞蟻金服的同窗願意分享帳單背後的技術架構,想必也是很是受歡迎的。
本文是對支付寶帳單技術實現的我的猜測,我將此簡單粗暴的分紅了兩部分:前端,後端。由表及裏,從看得見的前端展現來推測看不見的後端邏輯。
整個探索過程的第一步就是找入口,我認爲這是一個很是關鍵的突破口。我將帳單經過釘釘分享出來,而後進入釘釘的PC版,右鍵那條分享記錄,便可複製整個URL。
爲了方便講解,這裏把整個URL放出來給你們看看:
https://render.alipay.com/p/s/i/?scheme=alipays://platformapi/startapp?appId=68687017&showOptionMenu=NO&allowsBounceVertical=NO&transparentTitle=auto&bizScenario=Share&url=https://render.alipay.com/p/f/fd-jbg7if4k/index.html
複製代碼
整個scheme參數的做用就是會去嘗試打開手機上的支付寶應用,這對於作移動開發的同窗來講是很容易看懂的。固然這種嘗試不必定都是成功的,這要看瀏覽器的安全策略了,如下是在Safari(iPhone)和Chrome(PC)中打開的結果:
scheme自帶了6個參數,這都將會傳入到APP中,執行相應的操做。本覺得每一個用戶分享出來的scheme參數會有所不一樣,可是通過我一番對比後,發現都是一樣的參數。通過後面對源碼的一番研究後,發現「bizScenario」參數會不一樣,由於我是從釘釘分享找到突破口的,因此bizScenario的值都是Share,但若是是從其餘渠道打開的,取值就不必定是同樣了。
至於用戶信息,應該是在頁面中經過js與native進行交互獲取的。若是咱們想看最終的帳單頁面的話,直接把scheme當中的url參數copy出來,在瀏覽器中打開便可。
至此,咱們就打開了一扇探索支付寶帳單的大門了。
首先須要明白的是支付寶帳單是一個Single Page Application,換言之,就是一個由html+js實現的單頁應用,我瞭解到的是靜態背景圖片+過場動畫+數據圖層。這樣作不只有效解決了跨平臺問題,並且更有利於傳播。
值得一提的是整個應用的css和js文件,包括資源請求url等,都作了必定的加密和混淆,要想讀懂,仍是有必定困難的,尤爲是js代碼。
靜態背景圖片總共有9張,我這裏放三張,你們能夠感覺一下。
過場動畫實際上是一系列的mp4文件組成的,放在video標籤裏播放,篇幅有限,這裏只放 一段視頻,讓你們感覺一下。至於數據來源及其圖層都是經過js來完成的,下面的截圖來自chrome控制檯,展示了頁面的主體DOM元素。
大體分爲了三個部分:加載,帳單主體內容,錯誤提示。
加載過程當中主要有兩個元素,一個是加載動畫,其實就是一個翻日曆的gif動畫;另外一個就是進度百分比,這個不用細想,確定作的是假的進度指示。經過**「AlipayJSBridge」**,委託Native APP發送帳單數據請求,在這個過程當中,進度指示按照必定的速率增加,大概是到了97%的時候會停下來,直到數據獲取完畢了再正式進入帳單頁面。
帳單主體內容由三部分組成:第一個是swiper,滑動屏幕切換場景,其下有9個子元素,9張靜態圖片,分別對應了9個場景;第二個和第三個部分則是兩個video標籤,分別播放下一個場景和上一個場景的mp4文件。
錯誤提示的部分沒什麼好解釋的,一行提示文字,一個重試按鈕,一目瞭然。
有讀者可能會問,這些圖片、視頻之類的資源怎麼獲得的?這是我接下來要講解的內容。
先來看一段數據結構的定義:
// 靜態資源。
window.resource = [
{
"scene": "https://gw.alipayobjects.com/zos/rmsportal/epRpbpBcCZIKasROmxcL.jpg",
"video": {
"forward": "",
"back": ""
},
"__key": 9
},
{
"scene": "https://gw.alipayobjects.com/zos/rmsportal/epRpbpBcCZIKasROmxcL.jpg",
"video": {
"forward": "https://gw.alipayobjects.com/os/rmsportal/KwIPQNAHVfCMQSToOqxX.mp4",
"back": "https://gw.alipayobjects.com/os/rmsportal/zRAHVrBufLRAlmOMwXgA.mp4"
},
"__key": 1
},
{
"scene": "https://gw.alipayobjects.com/zos/rmsportal/ALzmXZZYrnFDqYFFrGjY.jpg",
"video": {
"forward": "https://gw.alipayobjects.com/os/rmsportal/QuZvhHxfIyRqgNxGQRqq.mp4",
"back": "https://gw.alipayobjects.com/os/rmsportal/zRAHVrBufLRAlmOMwXgA.mp4"
},
"__key": 2
},
{
"scene": "https://gw.alipayobjects.com/zos/rmsportal/ZcmJnyzRQNuFspDzfoxX.jpg",
"video": {
"forward": "https://gw.alipayobjects.com/os/rmsportal/CLryDglMNEQLfDxmYnUW.mp4",
"back": "https://gw.alipayobjects.com/os/rmsportal/nmqWCcwURUxRdUqgdJje.mp4"
},
"__key": 3
},
{
"scene": "https://gw.alipayobjects.com/zos/rmsportal/qQLBJCGEDtXCoCiOtPzc.jpg",
"video": {
"forward": "https://gw.alipayobjects.com/os/rmsportal/TUKbyXyonamHXwQifpDQ.mp4",
"back": "https://gw.alipayobjects.com/os/rmsportal/rwcPCPShQgxvVYfobSeU.mp4"
},
"__key": 4
},
{
"scene": "https://gw.alipayobjects.com/zos/rmsportal/zQzcNkGFJJqHinrABCDa.jpg",
"video": {
"forward": "https://gw.alipayobjects.com/os/rmsportal/OBocUmcqGaHuZJJkNQvV.mp4",
"back": "https://gw.alipayobjects.com/os/rmsportal/HbyqRtKZMlKFQZcxwCJy.mp4"
},
"__key": 5
},
{
"scene": "https://gw.alipayobjects.com/zos/rmsportal/JzzNsINqFRVOCAMNJonO.jpg",
"video": {
"forward": "https://gw.alipayobjects.com/os/rmsportal/kBYTpmZElHykGKYytIIG.mp4",
"back": "https://gw.alipayobjects.com/os/rmsportal/zvFLfvTTgXdUUCFHuIlv.mp4"
},
"__key": 6
},
{
"scene": "https://gw.alipayobjects.com/zos/rmsportal/vOpPBFXzjbvlSAxZBcSB.jpg",
"video": {
"forward": "https://gw.alipayobjects.com/os/rmsportal/dIPIDFjkwlJkxwjbtmRO.mp4",
"back": "https://gw.alipayobjects.com/os/rmsportal/VIbUIjvACyUlBSVULXbB.mp4"
},
"__key": 7
},
{
"scene": "https://gw.alipayobjects.com/zos/rmsportal/HJUaMfRgdvNwrxJgibsJ.jpg",
"video": {
"forward": "https://gw.alipayobjects.com/os/rmsportal/egQUtbPUbknxskiWkGgU.mp4",
"back": "https://gw.alipayobjects.com/os/rmsportal/JlBuZvsTbtOGIiUDNTuW.mp4"
},
"__key": 8,
"poster": "https://gw.alipayobjects.com/zos/rmsportal/guBrTMglMdSRsWcRnaXB.jpg"
}
];
複製代碼
這段代碼出自帳單頁面,從Chrome控制檯裏複製出來的。
scene 爲靜態圖片,用做背景;forward 爲前進動畫,back 爲後退動畫;poster 彷佛意義不大(從實現上考慮),能夠認爲是對scene的「備胎」;至於__key字段最值得斟酌,在如下給出的代碼中,會有我對此字段的理解。
特別注意一下最後一組場景對象,forward 裏面的視頻內容在整個帳單彷佛都沒有出現過,一個不當心就讓我看到了動畫製做的外包商名字了,這是程序猿哥哥加的「雞腿」嗎?
還須要注意的是 forward 和 back 的取值含義。forward 字段的取值含義仍是比較好理解,就是當前場景滑向下一個場景所播放的過場動畫,而 back 字段的取值含義稍微有點繞,我直接舉個例子:__key=3的場景對象中,back字段記錄的是回退到__key=2的場景的過場動畫。
由於js源代碼被混淆得實在是無法看懂了,只能根據交互的效果來猜測代碼實現。如下是場景切換的代碼:
//此處也能夠直接賦值爲0,猜測__key的做用,爲了用上此字段
var index = window.resource[0].__key;
var len = window.resource.length;
function next() {
if(index == window.resource[len - 1].__key) return; //at the last page
index = (index + 1) % len;
var resource = window.resource[index];
//1. play video in resource.forward.
//2. video player listener binding.
//3. to display data with animations after forward video completed.
}
function previous() {
if(index == window.resource[0].__key) return; //at the first page
var resource = window.resource[index];
//1. play video in resource.back.
//2. video player listener binding.
//3. to display data with animations after back video completed.
//4. set value for index variable.
index = (index == window.resource[1].__key) ? window.resource[0].__key : index--;
}
複製代碼
主要是經過對len取模,對場景進行先後切換,這樣作能夠達到循環播放的效果。對於__key字段的猜測也在代碼中體現了,值得一提的是,在全部的js源代碼中搜索了一番,並無發現有任何地方用到了這個字段,不知道是否是被加密混淆了。
至此,關於前端的頁面展現部分的介紹就結束了。
這部份內容將重點介紹支付寶帳單數據的造成,純屬我的對支付寶技術架構的瞭解進行猜測的,並不表明是真實的運做狀況。
以上是我認爲比較合理的架構圖,架構的視角放在了Data層面。
一、Database,這一個層次表示的是雲數據庫集羣,整個集羣中的數據庫極有多是異構的,如MySQL,Oracle,PostgreSQL,MongoDB等等,此外,這裏所說的集羣也涵蓋了淘寶,天貓,支付寶等阿里體系中的產品所使用的數據庫,因此這一部分承載了較多的數據輸入輸出的工做,相當重要。
二、DW,Data Warehouse,即數據倉庫。其中重要的數據來源是雲數據庫集羣,也會有一些直接來自文件。在數據倉庫裏面能實現的功能就很是多了,其中當屬ETL工做,這也是BI的必經之路,配合Reporting System,能夠實現數據可視化,日誌分析,運維監控等功能。
三、MaxCompute,這個實際上是屬於阿里雲的一個大規模分佈式計算平臺,其中以Hadoop、Spark爲表明的分佈式計算框架,Hadoop擅長離線計算,Spark則能夠完成快速實時計算。
四、DRDS和REST APIs。DRDS一樣也是阿里雲出品的數據庫中間件產品,上述提到過雲數據庫集羣是異構的,必須有一箇中間件參與數據的讀寫工做。至於REST APIs,主要是提供一些列的API,以便客戶端進行數據操做。
解釋完了整張架構圖後,我再進一步將整個數據請求流程梳理一遍。
一、數據的產生。主要是用戶2017年度的消費記錄,來自天貓,淘寶,支付寶,螞蟻信用等平臺,這些數據大部分被結構化的存儲在了數據庫集羣中;
二、年度帳單數據的生成。將用戶2017年度的消費數據導入到數據倉庫中,經由分佈式計算平臺離線計算出每位用戶的帳單數據,將這份結構化的帳單數據再放入數據庫集羣中。這裏使用離線計算是比較明智的,畢竟數據都是PB級別的,實時計算也只能針對個別用戶,否則的話,會對用戶體驗形成必定的影響。這部分的工做,簡單地說,就是寫若干個MapReduce任務,在分佈式計算平臺上跑2~3天應該就差很少了;
三、數據獲取。到這個步驟,說明帳單數據已經準備就緒了,客戶端只須要調用API便可獲取,也就能作出咱們如今所看到的帳單頁面了。
基於對前端展現的研究,我才作出了上述架構的猜測,但這並非我第一直覺的產物。我一直認爲像支付寶帳單,網易歌單這類年度盤點的營銷活動,可使用「頁面靜態化」技術。固然,這樣的架構也是有利有弊,先看一張改進後的架構圖。
能夠看到,前端和後端能夠說已經處於一個高度解耦的狀態了,後端只負責帳單數據的生成,並填充好用戶的帳單頁面,而前端訪問指定的靜態HTML頁面便可。針對這個架構,咱們來討論以下三個問題:
一、html文件命名方式。這個想象空間仍是很大的,規則也各式各樣,好比簡單粗暴地將用戶id,生成時間等元素進行Hash。固然,對於文件目錄也是有要求的,這裏就再也不深究了。
二、頁面靜態化技術選型。理論上,最佳的選擇就是CDN技術,這方面的技術在市面上已經比較成熟了,能夠放心使用。若是不用CDN的話,那能夠考慮利用squid,作一個緩存代理緩存服務,能夠認爲是精簡版的CDN,若是隻須要內容分發而不考慮其餘更高級的功能,squid不失爲一個好的解決方案。
三、適用場景分析。頁面靜態化最吸引個人地方就是減輕了大量後端數據訪問的壓力,將壓力轉移給了CDN,可是大可沒必要擔憂,由於這是CDN的長項,實現成本低,不易觸及瓶頸,此外,沒有額外的網絡數據訪問,不只不會暴露API,有必定的安全保障,前端頁面也能夠作到秒開,給用戶帶來了絕佳的體驗。所以,既然是頁面靜態化,那麼確定就不適合那些頁面頻繁改動,或者有強交互的場景。
原本此次支付寶帳單頁面徹底能夠靜態化,後來發生的「受權協議門」事件讓我打消了這個念頭,這個小插曲的出現就意味着須要將以前生成好的頁面所有失效並整改,又會引發一大波流量,也會引發存儲空間的浪費,除非是替換以前的文件。不過細想一下,支付寶帳單頁面嵌入了動態受權,也就很差作頁面靜態化了。
本文從前端到後端兩個層面,對支付寶帳單的技術實現作了一次很是淺顯的剖析,對於一些沒法得知的技術細節,也給了一部分本身的實現思路。若是讀者看了這篇文章以後,對此也很是感興趣,也能夠針對這個話題發表本身的想法。
每日干貨分享,傳遞互聯網世界有價值的訊息,微信公衆號:技術匯