本文根据第16期美团技术线上沙龙OnLine演讲内容整理而成。
应用性能是产品用户体验的基石,性能优化的终极目标是优化用户体验。当我们谈及性能,最直观能想到的一个词是“快”,Strangeloop在对众多的网站做性能分析之后得出了一个著名的3s定律“页面加载速度超过3s,57%的访客会离开”,可见页面加载速度对于互联网产品的重要性。速度在Google、百度等搜索引擎的PR评分中也占有一定的比例,会影响到网站的SEO排名。“天下武功,唯快不破”,套在性能上面也非常适用。
性能优化是个系统性工程,涉及到后端、前端、移动端、系统网络及各种基础设施,每一块都需要做各自的性能优化。当我们系统的分析性能问题时,可以通过以下指标来衡量:
互联网产品是创意、设计、研发、系统、网络、硬件、运维等众多资源相互交织的集合体,性能受多方面因素影响,犹如一只木桶,木桶能盛多少水,取决于最短的那块木板,也可称之为短板效应。影响产品性能的因素有:
1.产品逻辑与用户行为
产品逻辑过于复杂、功能交互过于丰富、产品设计过于绚丽、页面元素素材过多等都会影响产品性能。
2.基础网络
中国的基础网络是世界上最复杂的基础网络,国内的网络运营商众多且各自为政,互联互通成本很高。对于境外业务来说更是要面对国内国际网络交互的情况,再加上GFW的存在,网络延迟、丢包现象非常严重。
3.代码及应用
开发语言瓶颈、代码质量及系统架构等都会影响系统性能,常见的代码及应用问题有:
4.移动端环境
移动互联网时代,移动端环境的复杂性对产品的性能影响也很大,比如用户的设备类型、设备性能、操作系统类型、系统版本及网络类型(电信/移动/联通,Wi-Fi/4G/3G/2G)等。
5.硬件及云环境
硬件的发展遵循着摩尔定律,生命周期一般都很短,服务器老化或其他硬件问题经常会导致应用故障。IDC、机架、服务器、内存、磁盘、网卡等不同硬件和操作系统上运行的应用性能差距可以达到数十倍之多。
境外业务与其他境内业务相比,区别主要表现在以下及方面:
用户在境外访问
境外业务很大一部分流量来自境外访问,国外网络情况十分复杂,一些国家的网络基础设施很差,4G覆盖率很低,从国外访问国内机房,不仅网络链路长,还涉及到跨网跨运营商跨GFW的访问情况,访问延迟、网络丢包等情况非常严重。
大量使用Hybrid实现
由于业务发展很快,业务新增及变更也相对频繁,为适应业务的快速发展,我们大量采用的是H5方式实现,大量使用Hybrid模式。H5相比Native页面需要加载的内容更多,对网络环境的要求更高。
与境外商家对接
除了用户在境外访问,境外业务还会和很多境外的商家、供应商或代理商有合作对接,同样面临着跨国网络访问的问题。
基于以上背景,如何提升产品性能,做到像国内业务一样,其中面临了很多的技术挑战。本文将从网络优化、前端优化、后端优化几个方面来介绍境外业务在性能优化方面的做过的一些事情。
影响网络性能的问题有很多,常见的网络问题有以下几类:
问题一:DNS问题
DNS问题最容易被大家所忽视,而实际上DNS出问题的概率非常大,DNS问题主要有2类:
一类是DNS解析慢或解析失败,我们统计过一些数据,我们的域名在国内DNS解析耗时大概在30-120ms之间,而国外网络下耗时达到200-300ms左右。在2G/3G等弱网环境下,DNS解析失败非常常见。DNS对于首次网络访问的耗时及网络成功率会有很大的影响。
下图是我们利用页面测速工具(GTmatrix)在加拿大温哥华节点测试的一个页面首次访问时的网络请求情况,可以看出当用户在加拿大第一次访问且运营商LocalDNS无NS缓存记录时,DNS解析耗时2.36s。
另一类常见问题是DNS劫持或失效,乌云(WooYun)上曾报过多起因域名服务商安全漏洞被黑客利用导致网站NS记录被篡改的case。而更多的DNS劫持问题则是来自于网络运营商的作恶,主要有以下几种:
问题二:网络链路问题
链路过长、请求经过的路由转发跳数过多、跨网访问等都是影响网络传输性能的关键因素。另外网络攻击(主要是DDoS、CC攻击等洪水攻击)流量也影响着网络链路的稳定性。据统计,骨干网上每天有数百G的流量为攻击流量。
问题三:传输Payload大小
移动设备的网络在非Wi-Fi环境下时通常不太稳定,再加上有TCP拥塞策略的限制,数据传输量越大,传的就越慢。我们需要尽量的减少数据传输量。通常的做法有:数据压缩、图片压缩、选择更高效的序列化算法(比如ProtocolBuffers)等。
我们在网络优化方面主要做了以下几件事情:
接下来,我们分别详细阐述一下。
CDN也是一种缓存,是缓存就不得不谈命中率的问题。如果用户在境外访问时CDN未命中,静态资源从境外回源到国内源站获取,成本非常高。为了提升缓存命中率,我们的做法是在香港搭了一个CDN中间源,在前端资源发布时会调用CDN的push接口把资源预热到中间源,保证当境外边缘节点缓存未命中时无需再回源到国内IDC,只需从中间源获取。
由于DNS的种种问题,腾讯推出了HttpDNS服务,使用HTTP协议向DNS服务器的80端口进行请求,代替传统的DNS协议向DNS服务器的53端口进行请求,绕开LocalDNS,避免网络劫持和跨网访问等问题。但HttpDNS需要能够获取CDN边缘节点的IP,这就限制了只有像腾讯、阿里这种有自建CDN的大厂才能实现。
我们采取的是一种轻量级的方案,如下:
HTTP请求重度依赖DNS,DNS劫持、移动端网络不稳定使建连失败,以及公网链路质量差等因素,导致移动端的网络成功率一直不高。HTTP2.0可以通过SSE、WebSocket等方式与服务端保持长连接,并且可以做到请求多路复用,但HTTP2.0对运维、前端、后端的改造成本非常高。基于此背景美团自研了Shark服务。一种“代理长连接”的模式,主要用于解决移动设备网络通信质量差的问题。
这种“代理长连接”的模式,对后端业务是无感知的,业务无需做任何改造。另外也巧妙的绕开了DNS、公网质量差等问题,极大的提升了NativeAPI请求的网络成功率。
目前美团大部分的App都接入了SharkSDK基础网络库,NativeAPI(我们内部叫MobileAPI,MAPI)的网路请求由SharkSDK统一解决,使用的是自定义的序列化方式(内部称DPObject,比JSON效率高)。但对于H5页面中的Ajax请求,是没法直接享受到Shark带来的“福利”的。先看一下Hybrid模式下一次Ajax请求的过程:
上图可以看出,一次普通的Ajax请求会由WebView的内置浏览器内核来发送接受请求,一般是JSON格式的数据,和PC浏览器的一次HTTP请求过程差别不大,都是要经过DNS、TCP建连以及要面对公网链路差等问题,另外Ajax请求没法复用TCP连接,意味着每次请求都要重新建连。有了MAPI的经验,我们很容易想到,能否像MAPI一样利用长连通道来提升性能呢?
一种方式是在WebView中拦截页面的HTTP请求,在容器层做请求代理并处理序列化反序列化等事情。这种方式对业务比较友好,业务方几乎不需要做什么事情。但WKWebView的限制比较多,所以该方案目前很难推行。另一种方式是通过JsBridge来实现,缺点是对业务侵入性较高,业务方需要手动控制桥API的调用,一期我们选择的是“较笨拙”的方案二。
另一个场景是,我们和很多的境外供应商有直连对接,通过HttpClient的方式后端发起调用对方的OpenAPI接口,这种场景优化前接口延迟及网络成功率都非常不理想(很多和国外对接的业务应该都遇到过类似的问题),我们的优化方案是:
在香港部署一个正向代理Squid,请求先由内网专线转发到香港的Squid服务器,再从香港机房的网络出口出去。以与香港迪士尼的对接为例,优化前的API接口RT95:9s+(迪士尼接口传输的数据非常多),优化后降到2.3s,效果非常明显。
除了专线方案,我们还测试CDN动态加速。
CDN不仅可以用来对静态资源做缓存加速,也可以对动态数据接口起加速作用,原理如下:
CDN动态加速主要在以下几方面起到优化效果:
我们实测下来CDN动态加速在部分国家和地区有明显的加速效果,但整体的效果不够明显,所以最终未投入规模使用。
前端优化我们主要做了下面几件事情:
在之前的项目中,页面是“Java直出”的方式,由Java后端项目中通过FTL模板引擎拼装,前端团队会维护另外一个前端的项目,存放相应的CSS和JS文件,最后通过公司内部的Cortex系统打包发布。
前后端分离的关键点在于前端拥有完整独立的开发、测试、部署的流程,与后端完全分离。我们把页面的组装完全放置到了前端项目,后端只提供Ajax的接口用于获取和提交数据。前端页面完全静态化,构建完毕之后连同相应的静态资源通过CI直接发布到CDN。这样的好处有:
前后端分离架构有诸多的优点,但有一个坑需要注意:SEO的问题,无法提供给搜索引擎可收录的页面,因为主文档HTML基本为空页面,需要搜索引擎蜘蛛拥有执行JavaScript的能力才行,现实是大部分的搜索引擎都不支持。所以对于一些需要搜索引擎引流的页面不推荐用前后端分离。
在一些重体验的网页上,图片资源的占比通常较大,一些高清大图动则几十上百K大小。针对图片这块我们主要做了以下几点优化:
图片尺寸按屏幕大小自适应。原先我们图片的尺寸都是由后端控制,由服务在代码中写死下发给前端,这样带来问题是:
而新手工程师可能使用的是800*800的图片,导致页面加载慢、用户流量白白被浪费且客户端还需要做图片压缩剪裁。
美团云的图片服务提供了实时剪裁功能,后端在下发图片URL时不需要指定尺寸,由客户端根据屏幕尺寸做自适应计算,这样可保证每台设备上的图片都“刚好合适”。
CDN加速:前面CDN优化章节已介绍,通过接入境外CDN服务商及CDN预热的方式做CDN加速。
图片压缩:境外业务内部已在全面使用WebP,经测试WebP格式能够优化图片大小25%-50%,清晰度基本没有影响。
域名和请求数过多会带来以下问题:
我们做了以下几件事:
离线化可以减少网络请求,加速页面加载速度。另外当用户网络不稳定或断网时也可以访问已被缓存的页面和资源,我们先后使用了2种离线化方案:
AppCache(HTMLApplicationCacheAPI),在前端项目构建流程中,通过分析页面资源依赖关系,自动生成资源manifest文件,这样就能够确保页面及资源发生变更时,manifest文件内容同步更新。当浏览器监测到manifest文件有更新时,会自动重新下载manifest里面的文件。AppCache的一个缺点是缓存文件会越来越多,缓存不容易清理。AppCache未来会逐步被ServiceWorker所取代,无论从灵活性还是可扩展性而言,SW都更胜一筹。
目前在使用的是公司平台自研的离线包框架,相比于AppCache,离线包框架在资源更新,离线配置,内存管理等方面都做了很大的改善。另外AppCache对于用户第一次加载页面是没有加速效果的,因为只有第一次访问之后才会把资源缓存下来。而离线包框架则可以做到真正的预加载,它会监听APP正常启动事件,当APP启动后即可开始加载更新离线资源。
前面介绍了前后端分离的架构,HTML主文档可以利用CDN加速,另外前后端同学很好的解耦开了,前端可以更方便的做组件化沉淀。但这种架构除了SEO时的问题还有另外一个问题,先看一下前后端分离下的一个页面加载过程:
当遇到页面引入的外部依赖很多时,这种架构性能可能还不如”Java直出”:
同构渲染,结合了Java直出和前后端分离的优势:
Node同构和一开始的Javafreemark后端渲染Java直出的方式对比,最大的区别在于:Node项目可由前端同学来维护,用的是前端工程师熟悉的JS语言。另外前端生态较好,React、Vue等框架都提供了丰富的渲染模板供前端工程师选择。
后端优化的思路相对比较比较通用,和境外业务的特点关系性并不大,文中的前言部分“影响性能的因素”章节有简单描述,本文将不对各种后端优化手段做详细介绍,只挑几件我们做过的事情做下简单介绍:
1.硬件升级
硬件问题对性能的影响不容忽视,早期的时候,我们的一个DB集群经常有慢SQL报警,业务排查下来发现SQL都很简单,该做的索引优化也都做了。后来DBA同学帮忙定位到问题是硬件过旧导致,将机械硬盘升级成固态硬盘之后报警立马消失了,效果立竿见影!
2.缓存化
缓存可以称的上是性能优化的利器,使用缓存时需要考虑缓存命中率、缓存更新、数据一致性、缓存穿透及雪崩、Value过大等问题,可以通过mutiGet将多次请求合并一次、异步访问等方式来提升缓存读取的性能。
3.产品逻辑优化
业务逻辑优化经常会容易被忽略,但效果却往往比数据库调优、JVM调优之类的来的更明显。
再比如,12306春运抢火车票的场景,由于访问的人多,用户点击“查票”之后系统会非常卡,进度条非常慢,作为用户,我们会习惯性的再去点“查票”,可能会连续点个好几次。假设平均一个用户点5次,则后端系统负载就增加了5倍!而其中80%的请求是重复请求。这个时候我们可以通过产品逻辑的方式来优化,比如,在用户点击查询之后将“按钮置灰”,或者通过JS控制xx秒只能只能提交一次请求等,有效的拦截了80%的无效流量。
4.服务化
我们做服务化最基础的是按业务做服务拆分,避免跨业务间的互相影响,数据和服务同时拆分。同一个业务内部我们还按计算密集型/IO密集型的服务拆分、C端/B端服务拆分、核心/非核心服务拆分、高频服务单独部署等原则做拆分。
5.异步化
异步化可以利用线程池、消息队列等方式实现。使用线程池的时候一定要注意核心参数的设置,可以通过监控工具去观测实际创建、活跃、空闲的线程数,结合CPU、内存的使用率情况来做线程池调优。
另一种是通过NIO实现异步化,一切网络IO皆可异步:RPC框架、Servlet3.0提供的异步技术、ApacheHttpAsyncClient、缓存异步接口等等。
6.搜索引擎
复杂查询以及一些聚合计算不适合在数据库中做,可以利用搜索引擎来实现,另外搜索引擎还可以帮我们很好的解决跨库、跨数据源检索的场景。
美团在上海、北京两地都有机房,以我们的商品服务为例,商品服务会同时被上海和北京的两侧的应用依赖调用。一开始我们的服务只部署在上海机房,由于上海、北京两地机房间地理间隔较远(北京-上海专线ping延迟30ms左右),当北京的应用调服务时就会有较高的延迟,如果一个API内多次跨地域调用则性能会非常差。
为此我们做了如下的缓存双集群加异地模式部署:
通过这种双缓存加异地部署的“异地多活”模式(实际是异地只读),提升了我们服务在跨地域场景下调用时的性能。
结合境外业务特点,本文从网络优化、前端优化、后端优化几个角度介绍了境外业务在性能优化上的一些实践,重点篇幅放在了网络优化部分。性能优化是一个系统性工程,需要前端、后端和SRE一起协作才能做好。得益于公司强大的高性能前端框架、BGP网络、高性能应用组件、云平台等基础设施,以及在靠谱的运维保障SRE团队、基础架构团队以及平台团队支持下,我们境外业务的性能优化取得了阶段性的成果,后续还要继续努力。