随着 jQuery、Dojo、YUI 等框架的兴起让构建 Web2.0 应用更加容易,但随之带来的定位等应用问题也越来越难,尤其是与性能相关的。dynaTrace Ajax Edition 是一个强大的底层追踪、前端性能分析工具,该工具不仅能够记录浏览器的请求在网络中的传输时间、前端页面的渲染时间、DOM 方法执行时间以及 JavaScript 代码的解析和执行时间,还可以跟踪 JavaScript 从执行开始,经过本地的 XMLHttpRequest、发送网络请求、再到请求返回的全过程。
dynaTrace Ajax 目前有两个版本,免费版和商业版,它们之间的区别可查看 版本比较,本文主要是针对免费版本的介绍。在 3.0 之前的版本只支持运行在 IE 浏览器下,包括 IE6、IE7、IE8, 在 3.0 Beta 版之后可同时支持在 IE 和 Firefox 浏览器上的性能跟踪。
下载 DynaTrace 最新的版本 , 双击安装文件,点击“下一步”便可完成安装,安装好的操作界面如图 1 所示:
图 1. 安装好的操作界面
点击中间齿轮状的图标可对工具的属性进行配置,如下图 2 所示:
图 2.Preferences( 属性配置 )
从上图的:
- “General ”面板:可设置服务端口,网络代理设置以及浏览器的启动路径等;
- “Agent ”面板:可设置获取参数的配置,例如可配置是否获取 DOM 的访问或方法参数和返回值等,默认会选择所有选项。
可以通过两种方式启动 DTA 跟踪您的页面:
- 直接通过工具启动,如图 1 所示,点击浏览器旁边的下拉按钮进入 “Manage Run Configurations” 或直接点击 “New Run Configuration” 添加所要跟踪的 URL。由于它可以运行在多页面的工作流之下,可以先输入起始网址,然后导航到其他网页,dynaTrace 会在后台监视一切。
图 3.Manage Run Configurations( 管理运行配置 )
- 从浏览器启动 dynaTrace: 先打开浏览器,进入需要跟踪的界面,再点击浏览器工具栏的按钮,如下图所示(使用前在浏览器插件中 Enable 该工具 ),Connected/not Connected 用以表示当前的追踪状态
图 4. 浏览器启动
在关闭浏览器之前,可以快速看一下 dynaTrace 软件界面,会看到在“Browsers ”下面有一个节点,那就是当前正在从浏览器中收集的信息。我们可以在运行浏览器的同时分析这些数据,也可以关闭浏览器,再从 Sessions 中分析捕获的信息。
此外,在实际的操作过程当中,可能会需要跟踪打开页面后的一系列操作(例如点击某个按钮触发的事件等), 免费版的 dynaTrace 跟踪的信息不能按照 Page 或者 Action 自动进行分离,这种情况下,我们可以通过在操作过程中通过添加标记 (Insert Mark ) 的方式从 PurePath 视图中来区分每个 Action 行为的时间分割。即在操作前添加一个标记,操作完成后再添加一个标记,再从 PurePath 视图中分析所添加标记之间的比较耗时的请求。如下图 5 所示:
图 5. 添加标记 (PurePath 视图 )
此外,它还具有导入导出的的功能即将收集到的数据导出为 session 文件再导入到 DynaTrace 里面进行分析。具体操作可通过在Cockpit 面板中 Sessions 文件夹下选择要导出的 Session,右键或者从工具条里的点击 Export Session 按钮即能完成导出操作,导入文件的操作与此类似。
在讨论 dynaTrace 工具之前,先简单了解一下在 Web2.0 下经常会碰到的一些客户端的性能问题,这些话题虽然不是本文的主题,但是和本文密切相关,因为您知道了大致存在的问题,再使用相应的工具去发现这些问题就会简单很多。
在 Web2.0 应用程序中,JavaScript 的执行常常会阻碍浏览器端资源的下载和增加页面的 Loading 的时间,导致这个问题的因素主要有:
- 浏览器本身的因素,例如在 IE 浏览器下 ,CSS Selectors 的查找速度相比其他浏览器如 Firefox 相对会慢很多
- CSS 对相同对象的查询次数太多
- 存在太多 Ajax 的 XMLHttpRequest 请求
- JS、CSS、图片数量过多,增加了网络传输开销
- DOM 的尺寸太大,一方面会增加内存的占用,另一方也会影响页面的性能,例如 CSS 的查询操作
- 丰富的 DOM 操作,例如创建新的 DOM 元素或是作为 HTML 形式添加新的元素等
- 过多的事件处理绑定(Event Handler Bindings)等
下面将结合实际工作中碰到的案例,介绍如何使用 dynaTrace 来跟踪和分析客户端的性能问题。
下面记录的结果是以我们目前正开发的一个实际项目(IBM Docs)中的一个案例 - 在 Web 中打开一个 PPT 文档,根据 dynaTrace 收集的信息来分析存在的性能问题。
从 Cockpit 面板中打开 Performance Report 视图,如图 6 所示:
图 6. 性能报告
性能报告视图中记录了所有访问的网页的详细信息,从这个视图当中我们可以得到以下信息:
- 载入页面所消耗的时间 :OnLoad Time[ms] 显示从页面开始载入到浏览器派发 onload 事件所经历的时间;Total Load Time[ms] 显示页面全部 load 完总共消耗的时间
- JavaScript 执行时间 :On Client[ms] 通过 JS API 或库执行的所有 JavaScript 函数所消耗的总时间
- 网络请求花了多长时间: 从 Remark 中可看到总共有多少请求数,其中有多少 XHR 请求等信息
- 服务器端所消耗时间: On Server[ms] 指客户端发出的所有请求在服务器过了多长时间开始响应所消耗的总时间
- 从右下方的各个面板中可以得到总体的性能分析报告(更详细的信息可查看 Cockpit 面板中的相应节点),例如:
- NetWork 中可看出有多少资源是从浏览器缓存中读取的,有多少的 HTTP 转发请求消耗了不必要的网络传输时间;合并同一个 domain 中的 CSS、JS 的请求可节省多长网络传输时间。
- TimeLine 中显示了页面的生命周期:该图反映了页面进程中网络资源下载,JavaScript 执行,页面发生渲染,CPU 使用情况,以及发生了哪些事件,例如:Load 事件、XMLHttpRequest 等信息。
在我的例子中,以下内容引起了我的注意:
- 网络耗时较长,请求数目太多:总共有 896 网络请求,其中有 300+ 个 request 是对图片的请求,300+ 个是从 cache 中对相同图片的读取。
- JavaScript 执行时间总耗时 22 秒: 从右下方的 JavaScript/Ajax(A) 报告中可看出有一个 OnLoad 的事件就消耗了总共 13 秒 的时间,双击可从右边窗口看出它的前后调用栈信息。
- Server 端处理总共花了 20 秒 的时间 : 这说明 Server 端也可能存在性能问题,可推荐大家使用 Performance Inspector工具去分析 Server 端的性能问题,这里不再详述。
- Remark 栏还显示了页面总共发出了 23 个 XMLHttpRequest 请求: 这可以从时间轴的 event 行中找出发生的时间点。下一节将会针对这些问题进行更详细的讨论。
时间轴视图可以通过双击 Cockpit 面板中的 TimeLine 节点打开或者在 Performance Report 中通过在某个 URL 上点击右键,选择“DrillDown-TimeLine ”打开。根据 性能报告视图 打开耗时比较长的 URL 的 TimeLine, 通过工具栏或右键菜单,可以打开更多选项,比如内容类型和 JavaScript 触发器的颜色值,或者显示更多事件,比如鼠标移动、点击和键盘事件。打开本案例的时间轴视图,如图 7 所示:
图 7. 时间轴
在此视图下,我们可观测到:
- CPU 占用率可显示 JavaScript 的执行导致浏览器占用 CPU 的时间
- JavaScript 执行所占用的时间:从上图中观察到右边蓝色块的那一段耗时比较长,鼠标悬停在这段上可以看到是由于 load event on 触发的,耗时将近 13 秒 的时间
- 浏览器 Rendering,悬停上去可发现大部分是由于在计算 layout 所需要的时间,一般在 IE 上面执行相对会比较明显
- 网络请求并行下载耗时,一方面来自请求的数目太多,其中一个比较明显的就是有一个 XMLHttpRequest 花在 Server 的处理耗时将近 7 秒的时间
- Event 轴显示了鼠标点击事件,XMLHttpRequest 事件和 OnLoad 事件
放大右边网络请求时间比较长的部分(在我的例子中,从 16s 到 29s 时间片 ), 通过在开始处点击鼠标左键拖拽到结束位置松开鼠标拖拽,视图将放大到下面截图中显示的时间片上,如下图 8 所示 :
图 8. 放大时间轴
通过放大的时间片右键选择“Drill Down to Timeframe e”进入 PurePath 视图,显示当前所放大的时间片上所有的活动。
PurePath( 路径视图 ) - JavaScript、DOM 和 Ajax 问题的详细说明
可以通过双击 Cockpit 面板中的 PurePath 节点打开也可以选中时间轴上的一段右键选择“Drill Down to Timeframe ”来到 PurePath 视图,进一步进入每个动作去观察哪些事件触发执行了 JavaScript 和哪些函数的执行耗时比较长。
这里接着上节所述进入 PurePath 视图 , 如下图所示:
图 9.PurePath 视图
鼠标点击上图中的第二个时间片即 JS 占用 14 秒的,面板同时会更新当前所选活的信息,显示 JavaScript 代码执行过程,包括每个方法的执行时间和调用的参数及返回值。我们不仅可以看到 JavaScript 方法,也能看到 DOM 访问和 Ajax 请求。
从详细信息栏我们可观察到
- Start : 一个活动的开始时间
- Duration[ms] : 活动的持续时间,包含子树的活动时间
- JS[ms] :JavaScript 执行总的耗时,包括异步的子树执行时间但不包括等待时间
- Total[ms] :活动本身从开始到结束的持续时间 , 不包含子活动的执行时间
- Exec[ms] :活动本身执行时间,不包括其子活动的需要的时间
- Size : 树中总的节点数,包含所有子活动的节点数。
鼠标点击上面任何一列可进行排序操作,根据 JS 执行时间长短通过鼠标点击展开也可以通过右键点击“扩展子树”展开层次图找到是哪个方法的调用导致执行了这么长的时间。从上图调用栈中可看出 contentDomHandle 来自应用程序的 JavaScript API 的调用总耗时最长,从它的子树中可观察到 JavaScript 执行的时间分布:
-
addContextMenu
<div> 方法执行次数比较多,虽然方法本身的执行时间 150ms,但调用次数比较多的话就会导致总的执行时间比较长。 -
SimulateSlideClick
耗时
2.5 秒 -
concord.util.events.publish
耗时
3 秒
为了更方便发现这些函数的性能问题,可以右键 contentDomHandle 方法,选择“Drill Down->Hot Spots ”进入 HotSpots 视图 。
另外,PurePath 视图提供了多种分析方法,您可以通过直接键入您要查找的内容来筛选或查找您需要的数据项,也通过右键菜单或工具栏按钮添加过滤规则可以让软件只显示特定信息。
综上所述,可以从 PurePath-->Drill Down 进入该视图,也可以从面板中直接打开 HotSpot 视图来分析浏览中访问过的所有 JavaScript、DOM 和页面渲染操作。
接着上一节的 contentDomHandle () 方法调用为例,如下图所示:
图 10.Hotspot 案例视图
从上图中可以看到每个方法的调用次数,JS 的执行时间以及总的执行时间等信息:
- Back Traces 栏显示了由谁调用了这个函数,调用了几次,从上图可看到该方法被 Dojo 的 <return-closure> 调用了 2 次,而方法本身调用的执行时间很短只有 3ms(Exec[ms])
- Forward Traces 栏显示了这个方法又调用了哪些函数,Invocations 表示该方法总共被调用了几次;活动总耗时 12.7s(Total[ms]),Exec[ms] 表示方法本身执行所需要的时间,JS[ms] 总的 JavaScript 的执行时间。
- 界面底部显示了在 Back Traces 树或 Forward Traces 树中选中的 JavaScript 的源码
从我的例子中,就会很明显的发现如下性能问题:
- addContexMenu(<div>) 被调用了 30 次,JavaScript 执行消耗了将近 7 秒。根据了解这个方法的作用就是为每个 Slide 添加右键菜单,也就是说文件包含 30 页就会被调用 30 次,这样不仅会增加浏览器的执行时间,也会占用比较多的内存。
- 对于其他两个比较耗时的方法,simulateSlideClick 和 events.publish 方法各调用了将近 3 秒和 2.5 秒的时间,调用次数也不多,这就需要扩展 Trace 去看是否存在性能问题或还有可以改进的地方;
到这里我们基本可以找出从时间轴视图中耗时 13 秒的 JavaScript 具体是被哪些函数的调用占用了,也发现了一些比较明显的性能问题。再回到 HotSpot 总的页面看是否还有其他性能问题 ( 从 Cockpit 面板中双击 HotSpot 节点 ),如下图所示:
图 11.HotSpot 视图
上图默认是按照操作或方法本身的耗时 (Exec[ms]) 不包括子方法来排序的 , 我们发现除了浏览器的渲染比较耗时之外,最有可能存在性能问题的就是应用程序方法的调用。例如在我这个案例中,就发现以下几个问题:
- loadState 总共(包括子方法)执行了 3.7 秒,方法本身就消耗了将近 2 秒的时间,这个方法仅被调用了一次,是否有改进的空间就需要通过源码看进去或直接跟开发人员沟通;
- dojo.destroy(<div>) 被调用了 122 次,总共花了 1.3 秒的时间;
双击 dojo.destroy(div),打开它的 Back Traces,如下图所示:
图 12.dojo.destroy
从上图可得知,dojo.destroy(<div>) 仅被 applySlideSorterStyles 方法调用了一次就执行了 1 秒的时间,这也是比较可疑的性能问题。另外,您也可以通过总的执行时间来排序,如下图所示,这里您可以找到最耗时的方法的入口:
图 13. 按总消耗时间排序
最后我们再来看一下 dynaTrace 的另一个视图 - Network 视图,通过双击左侧 Cockpit 面板中的 Network 节点,或从 Summary 视图中某个 URL 上右键选择“Drill Down – Network”进入到 Network 视图,该图显示了所有网络请求,如下图所示:
图 14.NetWork 视图
Network 视图高亮标记出超慢的请求以及连接等待时间。
这个视图下会用颜色标记每个请求,并且用红色高亮标记出耗时最长的下载请求。默认情况下会以 TimeLine 上的发生顺序来排列,您可以点击任何一列来进行排序。对于每个请求我们可以看到资源是否来自浏览器缓存(Cached 栏),请求类型(Network 或 Ajax),HTTP 状态,Mime 类型,大小,在 DNS、网络连接、服务器响应、网络传输和等待上消耗的时间。界面底部显示了 HTTP 请求和响应头以及返回的实际内容。
常见的性能问题及解决办法:
- JS 或 CSS 的个数太多:需适当的合并同域下的 JS 或 CSS 以降低客户端的请求数目
- JS 的尺寸太大导致在局域网的条件下下载时间太长:可以对尺寸比较大的文件在服务器端进行压缩,例如使用 Dojo ShrinkSafe 或 YUI 进行压缩
- 图片数量太多:可使用 CSS Sprites 将一些小的图片组合在一起成为一张图片,这样可以减轻服务器的负载,提高网页的加载速度
通常我们都是对性能上有问题的页面利用手动的方式访问每个页面再用 dynaTrace 记录和收集数据,但若是对每个页面都要记录或是针对每个不同的应用程序的版本仅对几个页面做这些操作也是需要付出比较大的人力。幸好 dynaTrace 还提供了我们一些新的 Feature 可以用脚本工具代替人工方式驱动浏览器自动收集数据。当您用像 Selenium、Watir、WebAii 这样的工具运行测试脚本时,dynaTrace 可以自动从每个浏览器 session 中收集性能信息。
如何使用 Selenium 整合 dynaTrace 实现自动化收集数据有两种方式 ;
- 使用 DynaTrace 提供的一些高级的 Features, 如 dynaTrace Selenium Runner(仅商业版用户) (com.dynatrace.webautomation.DynaTraceSeleniumRunner). 或 DynaTraceSeleniumHelper (com.dynatrace.webautomation.DynaTraceSeleniumHelper) 或使用 DynaTraceSelenium 替代 DefaultSelenium,如以下 code:
public class GoSpaceDynaTraceSeleniumTest { Selenium selenium = null; @Before public void startup() { selenium = new DynaTraceSelenium("localhost", 4444, "*iexplore", "http://localhost:9090"); selenium.start(); |
}
- 若使用的是免费版,通过配置环境变量来实现自动化。就是设置在浏览器启动时自动连接 dynaTrace,这样在 Selenium 的脚本开始执行启动浏览器时就会自动连接上 dynaTrace,让 dynaTrace 自动收集数据。对于自动添加 Mark 可在 Selenium 的脚本中插入如下代码:
void addMark(String marker) { defaultSelenium.runScript("try Unknown macro: { _dt_addMark('" + marker + "') } catch(e) { }"); } |
dynaTrace Ajax 是前端的软件开发工程师和性能分析师的非常有用且重要的工具。通过该工具的不断更新,功能的不断强大,所支持的浏览器的不断增加以及与持续集成工具相结合,这样就可以更容易、更早、更频繁地发现应用程序在不同浏览器上的性能问题。