淘宝网前台应用性能优化实践

标签: 性能调优 性能优化 高性能 | 发表时间:2013-06-14 11:39 | 作者:叔同
出处:http://jm.taobao.org

本文曾发表于2013年4月的《程序员》杂志

近年来,随着用户数和PV的增加,淘宝网的后端服务器数量增长很快;并且我们知道,Web页面延迟时间和转化率之间有着直接的关联。出于提升系统吞吐量、降低成本、减少页面延迟、提升用户浏览体验、提高交易转化率的考虑,淘宝网在性能优化领域做了很多尝试。本文将从应用性能分析、基础设施优化、应用自身优化、前端性能优化这四个方面,对淘宝网的优化尝试做一个总结。

  • 应用性能分析
    1. 1. 前台应用介绍

    淘宝网前台应用是指商品详情、店铺、购物车等买家直接可以看到和使用的应用,这类应用PV较高,服务器数量较多。从技术实现来说,淘宝前台应用都使用Velocity模板引擎渲染HTML,页面平均大小大于100KB,Web Server不保存数据,数据来自于后端的DB、RPC服务、消息中间件、Tair、Search Engine、TFS等外部系统,除了写日志、读取配置和共通模板,磁盘读写很少,而相对于后端系统来说可处理的最大吞吐量较低,单台虚拟机平均TPS不到200。根据分析,这些应用都属于CPU密集型应用。

      2. 度量关键指标

    优化工作开始前,要先给系统做次体检,拿到关键指标,然后针对关键指标进行优化,这样在优化工作完成后,更容易度量成果。关键指标有吞吐量、页面大小、响应时间和每请求内存数。
    A. 吞吐量
    通过线上环境单机压测,可以得到服务器预设最大负载情况下,应用单机的最高真实吞吐量。线上压测的方法有两种:对于所有HTTP请求都具备幂等性的系统,可以使用开源的AB、http_load等工具,回放前一天的流量给服务器,逐渐增加压力,当系统负载达到预设值时就得到了应用当前最高吞吐量;对于所有HTTP请求不完全具备幂等性的系统,可以采用Nginx引流的方式,将其他服务器的流量引到同一台服务器,以达到增加负载、得到最高吞吐量的目的。
    B. 页面大小、响应时间
    页面大小和HTTP请求响应时间可以通过分析服务器访问日志得到。
    C. 每请求内存数
    淘宝前台应用都是Java应用,内存使用较多,垃圾回收很容易成为瓶颈,所以设定这一指标来衡量应用的内存使用情况。每请求内存数的计算方法是:JVM单位时间内申请的内存/服务器单位时间内处理的请求数。

      3. 查找应用瓶颈

    瓶颈是系统中比较慢的部分,在瓶颈完成前其他部分需要等待,所以优化工作可以从分析瓶颈开始;应用代码的执行也符合2/8原则,即20%的代码执行会消耗80%的资源,找到这20%的代码去做优化才会有效果。自底向上,查找应用瓶颈可以分为下面几个部分:
    A. 系统瓶颈
    使用top、sar、vmstat、mpstat、iostat等系统工具、JDK源生工具去分析CPU、IO、Memory在压测时的表现,关注当前进程的Thread、锁、打开File数、Socket数、GC表现等情况,看哪一块存在问题或者会先成为瓶颈;对于关键代码,使用Perf等工具查看热点和CPU缓存命中率。经过分析,CPU计算的通用瓶颈在字符串的查找、拼接、替换,字符字节的编码、解码转换,压缩、解压缩操作,外部调用的瓶颈在IO开销、序列化和反序列化操作。
    B. 代码瓶颈
    对于运行态的Java代码,业界有很多工具可以用来查找瓶颈,比如收费的JProfiler、YourKit等,免费的TPTP、NetBeans Profiler、VisualVM等,我们使用淘宝开发的适合线上运行的TProfiler工具(已开源),同时支持剖析和采样两种方式,可以得到对象创建排行和Java代码执行次数、执行时间排行。排在前边的热点代码,极有可能就是代码瓶颈所在,如下表所示:

    方法信息 执行时间 执行次数 总时间
    com/xxx/web/core/NewList:execute() 61 3102 190067
    com/xxx/web/core/PerformScreen:performScreen() 18 4822 87822
    com/xxx/core/DefaultSearchAuction:doMultiSearch() 43 708 30357
    com/xxx/core/DefaultSearchCatManager:doSearch() 4 1248 4552

    C. 模块瓶颈
    基于前边的热点代码排行数据按模块做归类统计,可以得出每一个模块的CPU资源消耗比重,如下图所示:
    10Sa7U
    这样就可以得出:Velocity模板引擎是此应用的瓶颈模块,需要着重优化。

  • 基础设施优化
    1. 1. 软件升级

    淘宝之前的Web应用构建在Apache + mod_jk + JBoss 4之上,软件栈相对陈旧,新版本的特性和优化也无法利用。做了一次大的升级后,变成现在的Tengine + Tomcat 7,在一些应用上实测吞吐量有近10%的提高,也验证了Nginx使用epoll IO模型带来的优越性能。操作系统由原来的32位升级为64位,可识别的内存变大,增加内存后调大新生代堆大小4倍,某应用吞吐量提升达到70%,可见新生代大小对应用吞吐量非常的重要;淘宝有专门的JVM团队和Linux内核团队,使用taobao-jdk(补丁开源)、淘宝内核、开启JVM大内存页后实测,某应用吞吐量提升40%;TCP初始拥塞窗口调优,对用户平均下载时间也有不错的提升。在当前开源软件百花齐放的形势下,升级基础软件投入不大,却能给系统性能带来较大提升,非常划算。唯一要面对的问题是升级周期会比较长,因为线上环境需要长时间的beta以保证新软件的稳定。

      2. JVM调优

    根据前面的分析和实践,吞吐量与GC有直接的关系,在页面大小不变的情况下,调大新生代有益于提升吞吐量,减小页面大小(每请求内存数)也能提升吞吐量。目前JDK7已经发布,但G1垃圾回收器还在开发中,经过我们测试在GC表现上G1没有比CMS更好,所以目前还是选择响应时间优先的CMS垃圾回收器。我们的JVM部分行为参数和性能参数如下:

    -Xms4g -Xmx4g -XX:PermSize=256m -XX:MaxPermSize=256m -Xmn2000m -XX:SurvivorRatio=10 -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:+CMSParallelRemarkEnabled -XX:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=82

    除了基本的配置还可以做一些参数调优,比如在6u23之后默认开启的压缩指针,随着JDK7发布带来的分层编译、大内存页、逃逸分析等非常值得尝试的优化。除了参数调优,应用代码本身也可以调整,使其对GC更友好。在CMS垃圾回收机制下,Minor GC时业务线程会暂停25ms左右,Major GC时业务线程会暂停500ms左右。用户的请求被暂停500ms是不能接受的,所以优化原则就是减少Major GC,也就是减少Young区晋升到Tenured区的对象数。可以通过JVM源生工具jstat观察JVM各个分区间对象的迁移情况,然后合理分配堆每一个分区的大小、调整Tenuring Threshold阀值。应用对象管理要尽可能缩短对象生命周期或尽可能少创建新对象,减少页面模板大小也是一个可行的办法。我们开发了TBJMap工具(已开源),可以分析JVM堆每一个分区里都有哪些内容,这对于优化应用代码非常具有参考价值。JVM性能表现的最佳状态是没有Major GC,在淘宝有些应用已经做到了这点。

      3. 二方包优化

    每一个工程都依赖很多jar包,这些jar包如果用的比较频繁对性能的影响至关重要,对二方包的优化不会随着业务代码的改变而性能下降,可以说一次优化永久受益。二方包优化有两个建议:可以做一次的工作不做多次,在bean copy的场景下使用CGLib代替BeanUtils,性能有超过20倍的提升;可以提前做的工作提前做,IP库二方包优化过程中把很多冗余操作提前处理掉,性能有接近1倍的提升。在技术选型时可以针对场景选择更优的二方包,比如LMAX-Exchange开源的高性能并发框架Disruptor。另外,如果改变了原来的二方包,代码不能提交回主干,将来会遇到版本升级困难的问题。

      4. 模板引擎优化

    通过前面的分析可知,Velocity模板渲染是最大的模块瓶颈,除了减小模板大小,还可以从模板框架优化下手。因为Velocity是解释型语言,性能相对较差;执行过程中还有大量的反射调用,效率可想而知;字符字节的转换也尤其消耗CPU。淘宝基于Velocity开发了语法兼容的Sketch框架,将Velocity模板编译成Java类执行,减少了反射调用,内部用字节存储页面,节约了从渲染到输出的两次编码转换。使用Sketch框架以后,很多应用整体吞吐量有超过20%的提升。另外,淘宝Sketch框架将于今年开源。

  • 应用自身优化
    1. 1. 压缩模板大小

    在很多系统中,模板大小和吞吐量成反比,如果能大幅减小模板和HTML的大小,会给吞吐量带来很大提升。最简单的减小模板方法,就是删除空行和多余空格。对于URL比较多的页面,去掉“http:”这五个可省略的字符、长URL压缩、用URL别名代替全链接都可以带来不错的效果。如果for循环里重复数据较多,可以把数据移到for循环之外,多次出现的只渲染一次,浏览器端渲染时再通过前端代码把重复内容放回去,这种业务上的去重,往往能带来很好的优化效果。

      2. 设置最佳并发

    并发用户数、资源利用率、吞吐量和响应时间的关系可以参考下图:
    dzBNr
    当服务器处于低负载区,随着并发用户数的增加,资源利用率和吞吐量直线上升,响应时间没有明显的变化;当服务器处于高负载区,随着并发用户数的增加,资源利用率趋于饱和,吞吐量达到最高点后开始下降,响应时间开始有明显的增加;这时并发用户数继续增加,服务器则处于假死状态,资源利用率继续趋于饱和,吞吐量开始急剧下降,响应时间开始急剧上升,直到系统不能处理任何请求,我们称之为服务器Down机。从这张图里我们可以得出,服务器吞吐量最大的时候对应的并发用户数,就是这个服务器的最佳并发数,当并发用户数大于这个值的时候,系统服务能力开始明显下降,做优化要找到这个最佳并发数,通过稳定性模块设置到系统中,稳定性模块可以对超过最佳并发数的请求进行限流,以保证系统达到最好的性能表现,不会因为大流量冲击而垮掉。我们一般通过线上压测来确定系统最佳并发数,对于CPU密集型应用也可以用如下公式计算:

    最佳并发 = ((CPU时间+CPU等待时间) / CPU时间) * CPU核数

      3. 代码瓶颈优化

    前面介绍了如何找到影响性能的瓶颈代码,可以针对代码瓶颈进行优化。举个例子,经过分析发现某系统每个请求都抛异常吞异常,导致服务器资源利用率上不去,吞吐量不高,修正后CPU使用率提高30%,系统吞吐量也提升近30%。抛JDK默认的异常比较影响性能,尤其是在线程调用栈很深的情况下,有的系统还使用异常作业务流程控制,有的异常直接被吞掉,危害都很大。taobao-jdk开发了异常监测功能,从JVM层面直接发现和暴露所有异常问题,杜绝了这一类瓶颈的出现。

      4. 外部调用优化

    淘宝系统目前处于第三代分布式架构,为了优化外部调用,开发了并行RPC、并行搜索等功能,对于适合的场景可以有效降低响应时间。某些场景使用更优的Protocol Buffers序列化框架,在某些对性能要求很高的场景,使用开发成本稍大、比Protocol Buffers还快20%的Kryo框架。

      5. 面向CPU编程

    对于CPU密集型应用,如果能减少CPU的使用则可以直接提升系统吞吐量。针对Web应用可以调低GZip压缩级别来降低HTTP Server对CPU的消耗。针对核心代码可以面向CPU编程:经常一起使用的Field可以放在一起,这样对CPU缓存比较友好;在多核服务器上对性能要求比较高的场景,可以补齐缓存行以减少伪共享的发生;按行处理不要按列处理数组,编写符合空间局部性的代码可以很好地提升性能;使用源生批量接口处理数组,这样一条CPU指令就可以完成操作;使用乐观策略(CAS)来代替同步和锁也可以有效提升性能。

      6. 架构优化

    架构调整往往要对系统伤筋动骨,开发周期很长,但却可以带来最好的优化效果。列举几个我们常用的架构优化方法:动态资源静态化,把需要服务器动态生成、更新不频繁的内容CDN化,内容变化了可以回源更新CDN,这样大幅减少了服务器的动态内容输出;后台依赖前台化,给后端服务暴露对外的HTTP接口,使服务器依赖转变成JS依赖,可以提升后端性能,并且把强依赖变成弱依赖,提升整个系统的稳定性;后端渲染前端化,对于数据远小于⻚面,页面布局比较规则的场景适用;DB依赖缓存化,这点业界用的非常之多;善用缓存,针对不同的场景可以缓存对象、缓存页面片段、缓存整个页面、缓存HTTP响应,使用缓存需要关注失效机制和数据预热,并尽可能提高缓存命中率。

  • 前端性能优化
    1. 1. 度量关键指标

    我们可以通过前端埋点和Navigation Timing接口来采集网页在用户浏览器上的关键指标,包括DNS查询时间、TCP连接建立时间、HTTP请求时间、页面下载时间、开始渲染时间、domReady、可交互时间、onLoad时间,使用阿里度等工具可以得到首屏时间。有了这些指标就可以衡量前端优化的效果。业界还有一些工具会给出很多优化建议,比如dynaTrace AJAX Edition、YSlow、Chrome插件SpeedTracer等,淘宝也根据Yahoo的34条军规,开发了自己的TSlow。

      2. 前端性能优化

    Yahoo34条军规已经成为前端WPO的标准,这里不再介绍。列几个对我们的场景比较适用的优化:减小Cookie大小;减少DNS查询并适当增加不同域名以优化资源并发加载;针对浏览器渲染,减少Dom数、按需延迟加载、次要信息异步化加载、使用BigRender技术控制渲染节奏优化首屏时间、使用前端模板引擎进行页面渲染。某应用后端数据大小是前端页面大小的1/10,如果只传数据不传页面可以大幅节省页面下载时间,我们采用前端渲染方案优化后,系统响应时间减少25%、页面大小减小60%、domReady时间减少60%、onLoad时间减少70%。在前端性能优化领域,Google一直在引领潮流,基于Chrome这个入口推动很多优化落地、推出PageSpeed、WebP、SPDY等技术,带给我们很多新的方向。

    相关 [淘宝网 应用 性能优化] 推荐:

    淘宝网前台应用性能优化实践

    - - 淘宝中间件团队博客
    本文曾发表于2013年4月的《程序员》杂志. 近年来,随着用户数和PV的增加,淘宝网的后端服务器数量增长很快;并且我们知道,Web页面延迟时间和转化率之间有着直接的关联. 出于提升系统吞吐量、降低成本、减少页面延迟、提升用户浏览体验、提高交易转化率的考虑,淘宝网在性能优化领域做了很多尝试. 本文将从应用性能分析、基础设施优化、应用自身优化、前端性能优化这四个方面,对淘宝网的优化尝试做一个总结.

    性能优化之Hibernate缓存讲解、应用和调优

    - - CSDN博客系统运维推荐文章
        近来坤哥推荐我我们一款性能监控、调优工具——JavaMelody,通过它让我觉得项目优化是看得见摸得着的,优化有了针对性. 而无论是对于分布式,还是非分布,缓存是提示性能的有效工具.     数据层是EJB3.0实现的,而EJB3.0内部也是通过Hibernate实现的,而Hibernate本身提供了很好的缓存机制,我们只需要学会使用它驾驭它就够了.

    Pora2应用中HBase高并发读写性能优化

    - - 搜索技术博客-淘宝
    淘宝搜索的个性化离线实时分析系统Pora已升级至Pora2,Pora2是在基于Yarn的流式计算框架IStream基础上开发的,同时为保证数据和消息的实时处理系统中较多地使用了HBase,是一个典型的高并发读写HBase的分布式应用. 系统在发布之初遇到了比较严重的性能问题,表现为处理速度跟不上实时日志,并且整个Hadoop/HBase集群压力大,连带其它应用受影响.

    Spring/Hibernate_应用性能优化的7种方法

    - - 企业架构 - ITeye博客
    对于大多数典型的 Spring/Hibernate 企业应用而言,其性能表现几乎完全依赖于持久层的性能. 此篇文章中将介绍如何确认应用是否受数据库约束,同时介绍七种常用的提高应用性能的速成法. 如何确认应用是否受限于数据库. 确认应用是否受限于数据库的第一步,是在开发环境中进行测试,并使用 VisualVM 进行监控.

    Android应用开发性能优化完全分析

    - - CSDN博客推荐文章
    其实有点不想写这篇文章的,但是又想写,有些矛盾. 当然了,本文不会就此编辑这么一次,因为技术在发展,工具在强大(写着写着Android Studio 1.4版本都推送了),自己的经验也在增加,所以本文自然不会覆盖所有性能优化及分析;解决的办法就是该文章会长期维护更新,同时在评论区欢迎你关于性能优化点子的探讨.

    Spring/Hibernate 应用性能优化的7种方法

    - - IT瘾-geek
    【编者按】对于大多数典型的 Spring/Hibernate 企业应用而言,其性能表现几乎完全依赖于持久层的性能. 此篇文章中将介绍如何确认应用是否受数据库约束,同时介绍七种常用的提高应用性能的速成法:. 如何确认应用是否受限于数据库. 确认应用是否受限于数据库的第一步,是在开发环境中进行测试,并使用 VisualVM 进行监控.

    Apache Flink OLAP引擎性能优化及应用

    - - InfoQ推荐
    导读:本次分享的主题为Apache Flink新场景——OLAP引擎,主要内容包括:. Apache Flink OLAP引擎. OLAP是一种让用户可以用从不同视角方便快捷的分析数据的计算方法. 主流的OLAP可以分为3类:多维OLAP ( Multi-dimensional OLAP )、关系型OLAP ( Relational OLAP ) 和混合OLAP ( Hybrid OLAP ) 三大类.

    从淘宝网首页看内容随机展示应用

    - - 互联网的那点事...
    开始之前先解释下相关概念,当然都是按自己的理解杜撰的. 从下图可以看到,现在的网站,特别是电子商务网站,都有内容切换模块. 基本上分为两种,一种是tab内容切换/js轮转切换,都是用户可以手动点击切换或自动切换,用户可以查看所有内容,但一次只能查看一个单元内容,称之为“显性隐藏”;一种是没有切换按钮,后台有一个一定容量的内容池子,在当前模块区域内只能显示固定数量的内容,除非用户刷新网页,用户看不到其余的内容,称之为“隐形隐藏”.

    开发高性能的MongoDB应用—浅谈MongoDB性能优化 - 吴纹羽

    - - 博客园_首页
    大数据时代的数据存储,非关系型数据库MongoDB(一).   “如何能让软件拥有更高的性能. ”,我想这是一个大部分开发者都思考过的问题. 性能往往决定了一个软件的质量,如果你开发的是一个互联网产品,那么你的产品性能将更加受到考验,因为你面对的是广大的 互联网用户,他们可不是那么有耐心的. 严重点说,页面的加载速度每增加一秒也许都会使你失去一部分用户,也就是说, 加载速度和用户量是成反比的.

    优化无止境,爱奇艺中后台 Web 应用性能优化实践

    - - IT瘾-tuicool
    爱奇艺视频生产智能云平台系统在今年进行了一次 重大升级,前端团队也趁此机会将 底层技术架构从三年前的 Arm.js(内部MVC框架)+ Java BFF + Velocity 模板完全切换到了 Vue.js + Node.js BFF 的技术栈. 新的前端应是一个拥有超过 十个业务模块的单页面应用,每个模块已经通过路由懒加载进行了拆分,同时公共的第三方依赖也拆分到了单独的 Vendor 文件.