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张,我这里放三张,你们能够感觉一下。
至于数据来源及其图层都是经过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,有必定的安全保障,前端页面也能够作到秒开,给用户带来了绝佳的体验。所以,既然是页面静态化,那么确定就不适合那些页面频繁改动,或者有强交互的场景。
原本此次支付宝帐单页面彻底能够静态化,后来发生的「受权协议门」事件让我打消了这个念头,这个小插曲的出现就意味着须要将以前生成好的页面所有失效并整改,又会引发一大波流量,也会引发存储空间的浪费,除非是替换以前的文件。不过细想一下,支付宝帐单页面嵌入了动态受权,也就很差作页面静态化了。
本文从前端到后端两个层面,对支付宝帐单的技术实现作了一次很是浅显的剖析,对于一些没法得知的技术细节,也给了一部分本身的实现思路。若是读者看了这篇文章以后,对此也很是感兴趣,也能够针对这个话题发表本身的想法。
每日干货分享,传递互联网世界有价值的讯息,微信公众号:技术汇