受访人 Dan Farino 采访人 Ryan Slobojan 发布于 2009年5月25日 上午3时30分
大家好,我是Ryan Slobojan,这位是MySpace的Dan Farino。Dan,你能介绍一下你在MySpace的工作吗? 没问题。我是MySpace的首席系统架构师。简单地说,我开发了我们使用的许多后台自定义性能监视和排错工具。当初我刚到那里时,所遇到的问题主要是系 统依赖大量的手动配置,大量的手动管理,以及需要大量管理员来创建各种脚本来执行例如重启服务器等工作。你可能要在一个非常简单的问题上花费30分钟甚至 一个小时的时间。因此,一开始我主要关注于从系统管理的角度出发构建一些自动化工具,希望可以让排错或性能诊断等问题变得简单一些。 你能否想我们解释一下,对如此大型的站点进行调试和排错时遇到了哪些挑战呢? 很多时候问题的关键在于,如何从几千台服务器中找到出现问题的那台。所以我开发了一个性能监视系统,可以实时获取整个服务器场中每台机器的CPU、队列中 的请求数以及每秒处理的请求数量等这种类型的信息。这样我们就可以直观地看着屏幕上的一台红色的服务器:“喂,我的队列中累积请求了”。然后问题就变成 了:“好吧,我们已经知道哪台机器有问题了,不过现在该怎么做?”如果我们获取一个内存快照(memory dump)并发送给微软,可能要过一个星期才会有反应说“嗨,你们的数据库服务器挂了”。不过我们希望能在两个小时内发现这个问题,所以我们在右键菜单里 放了一些简单的工具,可以让一些运维人员——他们不是开发人员——发现说“我看到几百个线程在数据库访问时阻塞了,我估计是数据库的问题”。我们关注于为 出错的服务器提供高度可视化的方案,还开发了一些工具,可以让一些技术不那么强的管理员快速发现问题。 很有意思。你能否从技术角度大致描述一下MySpace的架构呢? 当然。可能从前端开始谈这个问题最为合适。我们有大约三千台Windows 2003 IIS 6服务器。这些代码运行在.NET 2.0上,少部分是.NET 3.5。不同的.NET的版本会造成各种麻烦问题,不过前端还是运行了.NET 2.0和3.5。我们使用经过调节的SQL Server作为后端存储。不过数据库查询的性能不足以应对站点对伸缩性的要求,因此我们开发了一个自定义的缓存组件。这是个简单的对象存储部件,通过自 定义的socket层进行通信,全部存储在64位.NET 2.0机器中的非托管内存中。因为我们最早遇到的问题之一便是.NET的垃圾收集器。就算在64位平台上,回收和压缩数以亿计的对象还是会造成较为明显的延迟。所以我们面向伸缩性的问题之一便是“必须自己编写非托管的内存存储”。我们在存 储的前端使用.NET进行通讯,并与这一存储层进行交互——我们称之为缓存层,这大大降低了数据库的压力。现在我们的数据库从SQL 2000升级至64位SQL 2005,这样内存中可以存放更多的数据,这带来了很大的性能提升。起初我们使用Windows 2000中的CodeFusion 5以及少量的数据库。我们最早在数据库伸缩性方面做的努力是进行了纵向划分——或横向划分,我不知道你们把这叫做什么方向——把每100万个用户放在不同 的数据库中。
这么做提高了伸缩性,也让我们能够轻松地添加硬件,并对错误进行隔离。如果一个数据库当机了,只有一小部分用户会在我们处理这个问题之前得到错误信息或正 在维护的消息。我们在Linux上构建了一个自定义的分布式文件系统,用于存放用户上传的媒体内容,并作负载均衡。所有的视频或mp3等内容都放在这个自 定义的DFS层上,实现了跨数据中心的冗余,并通过HTTP直接从磁盘中获取数据。 你们在扩大网站规模时遇到了哪些挑战?你提到,一开始你们使用了CodeFusion服务器,不过现在使用了数以千计的IIS服务器。你们是怎么进行切换呢?你们扩大网站规模时只是通过增加服务器数量吗? 许多服务器用来构建中心缓存了,这可以大大降低数据库服务器的压力。还有,如果你使用大量后端数据库,就可能会遇到一个问题:如果其中一个数据库服务器当 机了,那么它会很快拒绝来自Web服务器的请求,问题不大。不过,假如只是这个后端服务器变慢了,试想某台Web服务器正在为大量不同的用户提供服务,很 可能其中一个缓慢的请求就会阻塞在这个坏的数据库上。
不过此时其它请求依旧工作正常,不过迟早就会有另一个请求阻塞在坏数据库上。可能过了十几秒钟之后Web服务器上所有的线程就被这个数据库阻塞了。所以我 到那里之后第一个架构的东西便是错误隔离系统,它运行在每台Web服务器上说:“我只允许同时向同一范围的服务器发起x个请求”。这避免了单个坏服务器让 整个站点停止响应。原本我们会在扩展的同时可能会遇到越来越糟糕的情况,因为添加了更多可能会造成单点失败的地方,整个站点停止响应的可能性也提高了。现 在我们在IIS上部署了错误隔离模块,这样增加了网站的正常运行时间。再加上缓存层的功效,正常运行时间进一步增加了。 听上去你们做得很多事情都是在现有工具的基础上构建扩展,那么在微软平台上这么做的感觉如何? 感觉非常好。我们使用微软的Powershell。它早先的代码名(code name)是Monad,提到这个名称的原因是我们从Monad beta 3就开始在生产环境中使用它了。它出现的正是时候,因为那段时间我们几乎已经把所有推荐的Windows管理技术,例如VBScript、命令文件等,都 用到极限了。
那些东西在本机上工作的很好,如果你想要看另一台机器上的注册表配置也不错。不过如果要在数千台服务器运行命令的话,你就会遇到瓶颈。首先你只能每次访问 一台服务器,其次,如果遇到了一台当机的服务器,那么整个工作就停止了。为了解决这个问题,我自己写了一个简单的远程服务层,于是问题就又变成了“我怎么 样才能把它做的通用呢?”我们希望可以轻松地对一系列的服务器进行操作,比如执行一条命令,获得一些结果,进行处理,然后可能运行另外一条命令——也就是 说,这些操作形成了一个管道。
就在这时候PowerShell出现了,帮了很大的忙。PowerShell完全用.NET编写,它在同一个进程里运行所有的命令,这个好处在于命令A可 以输出各种东西,而不仅仅是字符串,并让命令B可以接着进行处理。它们之间可以顺着管道传递任意的.NET对象,这样就形成了一种非常强大的编程模型,让 那些只愿意坐下来写一点点命令的管理员们也乐于使用。于是我们就决定使用那些命令,例如我对MySpace说“VIP”,意思运行特定功能的一组虚拟 IP。如果我说“Profile VIP”,意思就是指运行profiles.myspace.com的所有几百台服务器,然后可以对它们统一进行处理。于是我们构造了一个叫做“Get VIP”的命令。
我们使用RunAgent命令的管道,可以并行地在远程机器上执行任何命令,并把任何对象放进管道里。例如你可以用PowerShell的语法来描述“告 诉我这个东西是true还是false,某个文件是不是存在”,然后把结果传递到另一个管道中,不断传递,这样就可以快速地处理一些特定的管理任务。人们 在写脚本的时候就不需要担心网络是否失败,也不用担心多线程执行的状况,因为我已经把这些问题处理掉了。我抽象了网络连接,抽象了并行特性,这样可以把一 些本来要花30分钟甚至1小时的工作用大约5秒钟就完成了,更可靠,可容易控制,更容易进行帐户管理或日志记录。 真厉害。我想你之前一定也听到过这样的问题,为什么你们会选择使用微软的技术而不是其他一些常见的选择呢? 嗯,在我到来之前其实就已经做出这个决定了,我刚到的时候系统运行在CodeFusion平台上,有大约两千万用户。我们有非常非常优秀的.NET开发人 员,不过我不确定我们到底是专门找了.NET方面的人才,还是找了最好的开发人员,然后他们正好谈到“.NET是个不错的东西,我们就试试看吧”。不过我 认为,事实上我们很早就已经使用微软平台了,因此延续使用.NET是一件很自然的事情。我也不是一个搞Java的人,我的背景和微软技术比较接近,微软也 给了我们不少帮助,让我们在使用.NET这个相对较新的技术时度过了一个个难关,所以它给我的感觉还是相当不错的。 刚才你提到,你们混合使用了.NET 2.0和.NET 3.5?它们配合的怎么样?有没有计划完全迁移到3.5上? 微软现在在版本号的使用上有些疯狂。我早先看了看他们的路线图,其实3.5不过是在2.0的基础上增加了一点扩展。所以你安装了3.5之后自然就有了 2.0的运行基础。这种做法有点奇怪,不过我觉得3.5中的有些东西似乎还不错,例如Windows Communication Foundation提供了很方便的web service功能,还包括了事务控制等特性,让我们放弃了老旧的remoting和web service编程模型。我们对WCF还没有使用太久,不过看上去这些东西很令人兴奋,它可以让我们摆脱不少已经存在多年的负担,以前我们不得不编写自己 的网络通信类库,只是因为remoting和web service功能上存在一些小毛病。 所以说,整个网站是建立在2.0和3.5之上的,你们使用的工具基于PowerShell。不过还有一点,你们整个系统里有没有配合使用的调试工具呢? 这些工具里我最喜欢的是一个叫做“Profiler”的工具。它使用C++编写,基于微软CLR侦测(profiling)接口,所以我们只要说“OK, 我们的堆栈转储接口可以展示一些实时的信息,比如当前各个部件都在做什么事情”,然后侦测接口就能说“好吧,我会每10秒对这个线程进行一次检查,我会从 头至尾监测一遍,我会告诉你每个调用花费了多少时间,告诉你每个异常的信息,每次内存分配,锁的竞争状况,还有各种依靠调试器查看静态状态等做法获取不到 的信息”。
这个Profiler的作用不光是在错误的情况下告诉我们发生了什么,还可以在正常状况下告诉我们那些请求对系统产生了什么样的影响。我们的系统中有大量 不同的人写的各式各样的模块,所以对我这样的人来说,就算已经对系统有所了解了,查看那些跟踪记录有时候也会有新发现,比如“我不知道我们居然做了这些事 情”。它可能是我们开发的技术最复杂的工具,这也是仅有的几个不使用.NET开发的工具之一,这是因为你无法编写.NET代码来监测.NET代码。这里我 们用了很多C++,有意思的是它利用了微软研究院的Detours类库,在技术上类似于他们用来偷偷摸摸为内核打补丁的方法:把现有代码导向别处,再调用 回来,这样程序并不知道自己已经被打上了补丁了。我们也用这种方式来获得一些微软的接口无法提供但有时候特别有用的一些信息。 你提到了C++,我记得你之前也谈到过VB和VBScript? 大约两年前我们使用VBScript。现在我们使用C#作为.NET开发语言。 在MySpace内部使用什么语言呢? 我必须提到CLR本身基本上完全是由C#编写的。我们内部也有一些“先驱”在尝试一些诸如F#之类的东西。F#来自微软研究院,看上去是个相当不错的东 西。我在不少工具里嵌入了IronPython脚本,因为我觉得就配置工作而言,它可以带来更多控制能力。与钩选几百个选择框相比,我现在只要几句 IronPython脚本就能完成工作了。就总体来说,我们用C#做前台和后台开发,在各种必要的时候就会用到IronPython和 PowerShell。 你们打算迁移到LINQ等各种.NET 3.5新特性上吗? 事实上LINQ给我的感觉可谓一天比一天深刻。我找不到任何理由不让所有的服务器升级,或者不让开发人员使用它。LINQ看上去是个非常巧妙的技术,对于 像我这种喜欢写SQL查询的人来说,可以用LINQ来处理XML,各种对象或内存中的数据,这种感觉实在是太酷了。可能有一天我们还可以使用自定义的 Provider,谁知道呢?真是个不错的东西,我希望很快就能在服务器上看到LINQ的使用。 对于最近发布的MySpace开发者工具,你们有没有打算让开发人员可以使用一些不同的语言,例如C#?还是让他们继续基于JavaScript或VBScript这样的脚本语言进行开发? 其实我还没有确定未来的打算。我知道刚发布的开发者平台是网站上的小部件(widget),可惜目前只支持JavaScript。也许您自己的富客户端应用程序可以调用系统中完整的API,不过我实在无法确定这方面内容。 对于MySpace这样的大型网站来说,如果你遇到了问题,例如服务器的负载到达了峰值,有没有办法可以创建一个补丁让问题直接消失,还是只能一点一点地改变,把问题分成几个部分依次解决? 这个问题很好,我觉得要从两方面来看这个问题。一是发现问题,二是解决问题。很多时候我们会发现“OK,站点的请求排队了,我们该怎么办?”我们其实不知 道为什么会发生这个情况,然后只是增加了几台服务器。好,似乎问题有所改善,不过其实你并不知道这时是否应该增加服务器,有可能只是后台的数据库刚好在进 行备份。这不仅在于你不知道哪里出现了错误,还有目前的做法可能在今后就不起作用了。所以一开始的困难在于,我们要如何开始识别这些问题,我们该如何让前 端网络操作中心(NOC,Network Operation Center)的人有能力指出这些问题。一旦这个困难解决了,那么他们就可以比较容易确定该向谁汇报问题。
当然,有可能这的确是一个代码上的问题,我们努力希望让中间层或后端的代码与前台兼容。如果我们发布了一个东西,结果在QA时正常,测试时正常,但是发布 到网站之后却有问题了但又不清楚是什么原因,那么最简单的方法是回滚这次发布或是禁用这个功能。一般来说,使用了正确的工具包之后,很少会出现找不出问题 所在的情况。一开始我们的工具包比现在要小得多,不过如果每天都会发现类似的问题,那么我们会说“好,那么我们用调试器检查一下”,第二天说“我再用调试 器看看这个问题”,而第三天:“算了,我要自己写一个工具,现在的那些派不上用场”。于是我们的工具包越来越完整,这样快速发现和诊断问题的能力也就提高 了。 整个排错的过程是什么样的呢?系统是如何适应伸缩要求,这一点上理论和产品上有没有什么区别呢?它又是如何影响系统架构的呢? 嗯,目前系统架构模型主要分为三层:前端,缓存层和数据库。所以要大幅度改进其伸缩性会是件困难重重的事情。我们一开始会说“我们现在的这条数据库查询不 太好,那么我们试试看简单的修复一下,把它放到缓存中去”。好吧,这没啥效果,那么我们可能要建立另一套系统了。我们就开始琢磨这个问题,我们有不少这样 的系统,如果性能出现问题了那么我们可以对它们分别进行优化,比如把这部分移出数据库,放到磁盘上,或者就放在内存中。如果某个地方出现了问题,那么我们 会开发一个新的系统。这在MySpace中并不多见,因为我们在一开始在水平分割上的考虑十分有效。不过当你打算获得更多9【译注:即99.99……%】 的可用时间时,就会在决策上有很大的改变。 这可能是个有2-3年历史的老问题了,不过在提及“.NET无法伸缩”的问题时,你会怎么答复呢? 我觉得不存在这方面问题,关键在于你有没有使用正确工具,是否有这方面的专家。事实上我觉得现在.NET已经是一个非常成熟的平台了。显然Java在这方 面领先于.NET,不过我想我们还是互联网上最大的.NET站点。我不知道你会把我们的可用时间和性能与同样规模的Java站点进行什么样的比较,我们遇 到的各种问题都不是.NET平台本身造成的。可能是我们自己的bug,可能是硬件问题,但是我没有真正遇到过.NET的问题,除了在垃圾收集方面我可能会 说“这的确很难进行伸缩”。
不过.NET上最好的地方莫过于它的可扩展性了,所以如果垃圾收集器在你16GB的机器上进行内存管理时表现不好,那么你可以使用Berkeley DB或你自己的非托管存储进行替换。要享受.NET的优势也不必把自己完全局限在.NET中。我想说.NET有良好的伸缩性,而且我们的规模也会越来越 大。你不妨过几年再来问我这个问题,看看我会怎么说。 你们在服务器或伸缩性方面有没有使用其他的微软企业产品? 应该说,有新东西出来的时候我们就会试试看。例如Enterprise Application Block、Remoting和Web Service我们都尝试过。如果你真想做一个大规模的应用程序,那么这就是我的建议:一般来说,我们最后会使用自己的实现,因为我们不需要如此通用的解 决方案,我们真正需要的是性能。我想说的是,我们尝试了某个东西之后就从中学到些东西,然后可能就会把其中有价值的地方给剥离出来,再为了达到我们的性能 或伸缩性需求重新写一个。我不觉得微软会在我们这种规模的应用程序中进行测试,所以好像是我们在为他们测试一样。所以,如果有些东西可以在几十台服务规模 的程序中工作正常,但是无法满足MySpace的伸缩性要求,我觉得这是一件稀松平常的事情。 你能否给出一个例子,关于对.NET或微软告诉你应该做的东西,却反其道而行之? 我觉得我写的Profiler算是一个吧。如果他们不会发现的话,我会使用很多肯定违反服务器担保条例的东西,例如因为性能为CLR代码打补丁。我们看了 很多C++运行时的代码,也一直用Reflector来查看内部情况,可能有时候还会在产品代码中用到反编译来修补一些我们觉得微软做的不好的东西。不过 我们尽可能避免这种做法,所以我举不出一个真正的例子来说明我们在什么地方反其道而行。不过每次遇到这些小问题时,我们会说“不如稍微修改一下,希望能够 有用”,而不是“好吧,我们重新用一个新的技术”。 你能多谈一写缓存层的东西吗?我对你处理一些常见的缓存问题的方法很感兴趣,例如更新之类的事情。 我们正好在处理这方面的问题。目前,缓存层既不是write through也不是read through的。基本上web服务器做的事情会分两步走。首先检查缓存,如果没有东西,那么web服务器会从数据库里取出对象并序列化,发送给用户页 面,然后异步地提交给缓存,然后下一次再重复这个过程。我相信在某些时候我们会使用一个更加传统的三层模型,这样web服务器不会直接连接数据库,不过目 前我们仍然基于简单的两层:web服务器和数据库,而缓存层只是简单的附属物。当以后规模越来越大时,我们就会着手把访问转移到缓存中,最终会让web服 务器连接缓存服务器,而不是数据库。
不过目前看来,对象存储工作得相当不错,我们也在这方面思考了很多。它只是用来存储对象,它并不知道保存了什么或者那些东西从哪里来,这对性能可能有些好 处,我不确定。不过从几年前我们需要缓存的时候,就把它设计成可以轻松增加的服务。我们现在有400台运行飞快的服务器,如果有东西变慢了,那么我们就会 进行升级。没什么特别的。
本文由赵劼翻译,朱永光审校。