elvis0123

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

转载于《TFS 在中国 (第一部分)》、《TFS 在中国 (第二部分)》

TFS 在中国 (第一部分)

在这篇博客中,我想和大家讲述一个真实项目的故事,在这个项目中,我和我的团队以及Adam Cogan先生倾注了大量的心血,完成了很多不可能完成的任务,最终证明了Team Foundation Server作为一个优秀的项目管理平台,可以为超大型团队提供完美的支持和性能。

leibrain

与 Brian Harry在 MVP Summit上的合影

每年2月,我都会飞到美国西雅图的微软总部参加一年一度的微软最有价值专家峰会,这一次的会议之所以有所不同,是因为在会议之前来自微软中国DPE 部门总裁的一通电话,问我是否可以与微软中国一起完成一个“不可能完成”的任务,为中国一家重量级的企业完成Team Foundation Server的验证工作。带着任务,我在西雅图与TFS之父 Brian Harry 进行了沟通,获得了来自TFS产品组的支持,并在回到北京的第二天飞赴中国南方的某个城市,开始了这次征程。当时我没有想到,这将是我10余年软件研发中 所遇到的最具挑战的项目,也是对微软的这款企业级产品的性能极端测试。

关于这个项目的一些背景:这家中国的企业拥有世界上最大最复杂的开发团队,有超过2万人的开发人员,并且对于服务于这些开发人员的研发平台有着近乎 苛刻的性能要求。我们所要实现的系统,需要支撑这些开发人员的复杂应用场景,同时经受住高并发同时快速响应的考验。这一切都需要构建在微软的TFS平台之 上,在这个项目中,我们大量使用了TFS对象模型以及TFS Integration Platform来支撑这一系列的需求。

项目开始的时候,我们并没有有意识到这将是怎样的一场考验;因为我们知道TFS已经在微软内部经过多个产品组的长期使用,微软管这个叫做:Dogfooding,也就是自己使用自己产品,用自己对于软件研发的苛刻要求来磨练这款产品。

但是,如同每一个现实中的故事,事情永远不会按照你期望的方式发生。当我们完成了大多数功能的编码的时候,我们得到了第一组性能测试数据 …

result1
非常令人失望的数据,所有的测试结果均5-6倍的高于目标值。

你看到的创建和查询操作不是简单的数据库操作,其中已经封装了大量复杂的业务逻辑,但是我们使用了高性能的SQL Server数据库服务器,虽然我们的数据库中有着超过2千万条数据,我还是对这个结果非常惊讶。

当然,如同每一个现实中的故事,事情还是会有一个完美的结局,这里你看到的是经过了一系列调优后的测试结果。

result2

最终,由于我们各个指标上都达到了客户苛刻的要求,客户对于结果满意。不过你需要知道的是,这样的数据量和指标是TFS产品组都没有遇到过的,我们已经把TFS的性能发挥到了极致。

在这个项目中,我们遇到了很多的问题,在寻求这些问题答案的过程中,我们也学到了很多,在这里我仅列出那些我觉得非常重要的分享给大家:

经验1:团队协作
对于任何的软件开发来说,团队协作永远是最重要的,软件开发再也不是一个人的工作,特别是当你面对一个复杂的系统。我们所面对的系统有着非常复杂架构,仅 就微软的产品来说,就包括了TFS 2012,SQL Server 2012,Windows Server 2012,IIS 8等,以及TFS Web Service及TFS Object Model。能够完成这样一个项目,很大程度上是由于我们获得了来自多个方面的支持,形成了这样的一个全球性的专家团队,大家目标一致,精诚合作。

在这个项目中,我获得了来自微软中国,微软总部,我的澳洲同事,以及来自全球各地的多位MVP的支持,绝对称得上是史上最精英团队,他们包 括:Brian Harry (微软院士,TFS之父),Aaron Hallberg (TFS研发团队),Tigao Pacoal (ALM MVP),Ramesh Rajagopal (微软研发团队),Julia Liuson (TFS研发团队,全球副总裁),易永明(微软中国技术专家) 等等很多人。

简单来说,想把事情做好,首先你需要有足够并且正确的资源;这个精英团队是这个项目能够成功的决定性因素。

经验2:测试提前
在这个项目中,我们全面使用了Scrum作为项目管理模型,并引入了大量敏捷开发方法。我们在第一个迭代中就开始使用持续集成,单元测试,自动化性能测试。不过,我们仍然遇到一个巨大的挑战,这就是直到项目的后期我们才获得足够的硬件来完成真实的压力模拟。

在前面你看了我们的第一份测试报告确实非常糟糕,不过令人欣慰的是,由于我们的Scrum模型使得我们给予了客户足够的透明度,客户对于这样的结果 并没有特别的失望和惊讶(因为大家都很清楚我们没有足够的硬件来测试足够的压力)。这也为我的团队争取了宝贵的时间,使得我们可以与正确的人取得联系,获 得足够的支持,并最终解决问题。

(以上2个经验都与技术无关,但是对于软件项目的成功来说,他们至关重要;在第二部分中我会列出更多的技术细节)

 

TFS 在中国 (第二部分)

让我们继续这个真实的项目故事,上一篇中的两个经验都是关于非技术因素的,下面的这些可能技术人员会更感兴趣。

经验3:为你的TFS数据创建索引

TIP: 参考SSW Rules to Better SQL Server Databases

使用数据索引是SQL数据库性能优化的基本方法之一。不过,大家应该知道对于TFS的数据库来说,直接对其中的数据结构进行修改是不推荐的方式,所以大多数人不会意识到其实我们可以对TFS数据库建立索引。

tfstuneindex图:对TFS数据建立索引后的性能提升非常明显

对于我们的应用来说,有2个定制的工作项字段非常频繁的被搜索,因此我们在这2个字段上添加了索引,这使得相关操作的整体性能提高了3倍左右。

以下是对工作项字段添加索引的命令:
witadmin indexfield /collection:CollectionURL /n:Name /index:on|off
witadmin是TFS所提供的工作项类型的后台操作命令,可以通过以下MSDN页面找到更多的信息:
http://msdn.microsoft.com/en-us/library/dd236909.aspx

经验4:在高性能系统上应该尽量避免使用虚拟化

在虚拟化火热的今天,大家都会认为使用Hyper-V来优化服务器基础架构是件好事,但是我们发现对于TFS的数据层服务器来说,由于大量的磁盘I/O操作,虚拟化会对服务器性能造成严重的影响,应该尽量避免。

在我们的项目中,将TFS数据层服务器迁移至硬件是我们在基础架构上所采取的首选措施,仅仅这一项就将相关操作的整体性能提升了2倍左右。请参考以下链接了解更多的信息:
http://msdn.microsoft.com/en-us/library/ms143506%28v=sql.105%29.aspx

说明:在迁移至硬件服务器之前,我们也对Hyper-V Pass-through磁盘进行了测试,但是结果并不让人满意,虽然Pass-through磁盘相对普通的VHD(动态或静态)来说确实提供了更好的 I/O性能,但是仍然无法满足我们的性能要求。关于Pass-through磁盘的性能测试请参考:
http://clusteringformeremortals.com/2009/09/25/hyper-v-pass-through-disk-performance-vs-fixed-size-vhd-files-and-dynamic-vhd-files-in-windows-server-2008-r2/

经验5:将数据库的日志文件单独放置于不同的物理磁盘

这个建议并不新鲜,任何的SQL性能调优的指导都会建议将SQL数据库的 数据/日志/临时文件 分别放置于不同的物理磁盘;这样SQL Server可以将I/O请求通过不同的硬件完成,从而提升性能。参考资料:
http://www.codeproject.com/Articles/43629/Top-10-steps-to-optimize-data-access-in-SQL-Server

经验6:在IIS 上激活Web Garden (maxProcesses) 设置

警告:一般来说,这一设置对于99%的应用来说并不推荐,但我们很幸运的属于那1%。其中的原因比较复杂,大家感兴趣可以参考:http://blogs.iis.net/chrisad/archive/2006/07/14/1342059.aspx

对于IIS来说,默认的设置会对同时处理的并发请求数量进行限制,并将无法处理的请求放入队列中等待,这意味着如果单个请求的事务处理时间过长,我们会有很长的等待队列。按照以上引用的IIS博客中的说法,Web Garden功能的设计目的只有一个

“为那些不存在计算依赖,但是会运行很长时间的请求提供扩展能力,从而避免消耗掉所有的工作进程。”

(这个话确实很难理解,你可能需要好好琢磨一下,另外看看我下面对TFS应用层的分析也会帮助你理解。)

现在的问题是,为什么TFS属于这幸运的1%而有别与一般的Web应用程序?
首先大家要明白的是,虽然TFS看上去很复杂,其实对于用户来说它就是一堆的Web Service而已。与一般应用不同的是TFS使用了大量的“动态数据结构”。所谓动态数据结构,就是在应用设计完成以后,仍然允许用户根据需要扩展更多 地数据域。在TFS中大量使用了这种技术,这也是为什么我们可以对工作项字段进行定制的原因。不过,这种灵活性造成的结果是在SQL Server上所执行的查询操作会复杂得多,而且会随着你所添加的定制量的增加而变得越发复杂。想象一下:你添加了定制字段的工作项如果要被提取出来,那 么TFS应用层所生成的查询需要被动态生成,而在SQL Server中所需要的行列转换的复杂度也会随之增加,如果考虑Query Plan的动态计算等因素,这将是怎样一个漫长的查询过程?

iiswebgarden

上面这张截图中大家看到的就是在IIS在收到大量并发操作时候所生成的队列。一般来说,任何>50ms的请求都已经是慢得要死了,而我们的队 列中竟然有125ms的天文数字。另外大家需要注意的是所有这些请求都处于ExecuteRequest状态,意味着IIS正在等待后端程序的处理完成。

经过以上分析后,我们得出的结论是,在满足以下2个条件的情况下你可以使用Web Garden来改进性能:
1. 你的应用没有使用 InProcess Session
2. 你的应用是无状态的(可能你会觉得和第1点重复,其实不完全是)

幸运的是,TFS应用层的Web服务完全满足以上条件,这也是为什么我们可以在TFS上很容易的实现NLB(网络负载均衡),而不需要额外的设置的原因。

决定使用Web Garden以后,下一个问题是我们应该使用多少用户进程(Worker Process)?
其实这个问题很难回答,在实际中我们也是不停地试出来的,影响的因素其实很多,比如:硬件本身的配置,应用被调用的场景等等。不过一个相对通用的建议是, 不要超过你的CPU物理核的数量,这是因为CPU用来调度不同的工作进程的开销会抵消掉可能获得的性能提升。因此,一般的建议是maxprocess = (CPU物理核数量) -1
(看到经验3里面的Worker Process数量为7了么?那是因为我们使用了8核的CPU)

经验7:如何找到你应用中的性能瓶颈?

TIP:关于性能调优,SSW的黄金规则是永远不要在未经评估的请下进行任何调整,请参考:
http://rules.ssw.com.au/Management/RulesToSuccessfulProjects/Pages/NoPerformanceWithoutMetrics.aspx

你可能注意到了,目前为止我们还没有对应用本身进行任何的修改,无论是TFS本身的Web服务或者是定制的代码,都没有进行过任何修改。

当客户提出性能问题的时候,一般开发人员的反应是:难道我的代码有问题,然后马上根据自己臆想出来的最佳方式修改代码,这是绝对错误的。在这种情况 下,我们首先要考虑的是那些调整是事半功倍的,以上的调整,无论是服务器基础架构,SQL Server还是IIS;我们的原则都是自下而上,尽量不对代码进行修改,因为这样我们才能最大化我们的调优结果,而且避免由于代码修改而造成的额外测试 以及可能衍生的bug。

这就是我们所说的ROI原则,对于我们的项目而言,添加索引可能改进10x的性能,但是你很难搞到10x于现在的硬件,所以SQL 数据库调优是一定的首选。

不幸的是,在尝试了以上所有的可能性后,我们仍然无法达到指标;是时候对代码动手术了。我们使用了Visual Studio 中内置的Performance Wizard来进行性能评估,如下图:

perwizrdvs2010
图:VS 2010和VS 2012中的Performance Wizard

perwizrdvs2013
图:VS 2013中这个功能叫做 Performance and Diagnostics

以下是我们获取的第一份评估报告

tfsapi
图:你可以很容易的分辨出我们的瓶颈在于NewWorkItem()这个API操作

对NewWorkItem()这个API我们进行了进一步分析,发现其中最耗时的操作其实是承载对象的实例化操作。这时问题来了,对这个操作我们无 能为力,因为我们必须要这个承载对象才能执行需要的操作。经过了一些调研后,我们决定使用“对象池”的方式来尽量多的缓存一些预先实例化好的对象,需要的 时候直接取出使用,完成后再放回去。最终这个对象池为又将性能提升了22%左右。

关于对象池技术,请参考:http://msdn.microsoft.com/en-us/library/ms751482.aspx

经验8:如何发现那些最耗时的Web Service操作?

既然TFS是基于Web Service的,那么发现最耗时的Web Service调用也是进行调优的评估内容之一,这里我们激活了TFS的客户端Web Service跟踪,获得了以下报告:

tfstracing
上图中我们发现最耗时的操作是GetMetadataEx2[WorkItemTracking]操作,这其实就是NewWorkItem()中实例化对 象的一个主要操作;这一分析再一次证实对象池的方向是正确的。对于这个GetMetadataEx操作,大家可能并不了解,其实这与TFS的操作机制有 关:TFS的对象模型会在本地保存一个缓存,其中包括了工作项定制内容的定义(XML格式),这些数据其实非常庞大,因此造成了实例化操作的缓慢。

以下为激活跟踪的配置:

tfstracecocnf

关于客户端跟踪配置请参考:
http://blogs.msdn.com/b/buckh/archive/2010/04/23/how-to-see-the-tfs-server-calls-made-by-the-client.aspx

关于服务器端跟踪配置请参考:
http://blogs.msdn.com/b/edhintz/archive/2007/03/30/tfs-client-tracing.aspx

经过了这一系列的评估后,我们尝试了2种代码调优方案:
1. 使用对象池技术
2. 直接调用Web Service (这样也可以避免metadata的缓存操作)

对2种方案测试后我们发现结果很接近,经过一些讨论后,我们决定使用方案1,因为方案2中我们必须使用未经微软公开的API,这样长远来看存在维护风险。

故事到这里也就结束了,我们的客户对结果很满意,我的团队也学到了很多。希望这些分享也能对你有帮助。

posted on 2013-07-24 15:02  elvis  阅读(1526)  评论(0编辑  收藏  举报