我过去也和你们一样。
我过去是一名系统程序员,为银行、电信公司和其他工程师使用的基础设施工作。我从事操作系统的工作。我从事分布式中间件的工作。我从事编程语言的工作。我编写工具。我做了所有硬核系统程序员做的事情。
我知道规则。我知道吞吐量是扩展的真正考验。我知道数据必须保持一致性和持久性,并且关系数据库是确保原子性的方法,信息丢失绝不是一种选择。我知道随着服务器层数的增加,客户端变得越来越轻量级,最好的客户端将是包含最少状态并允许重要计算在计算云内部进行的客户端。我知道对遗留代码的支持对于任何新技术的采用至关重要,并且大多数遗留代码尚未编写。
但是两年前,我的世界发生了变化。我被要求担任 Project Darkstar 的技术架构师职位,这是一个面向大型多人在线游戏和虚拟世界市场的分布式基础设施。起初,这看起来像一个熟悉的系统。目标是通过动态添加(或减少)机器以匹配负载来实现灵活扩展。有一个持久层和一个通信层。我们还希望使编程模型尽可能简单,同时使系统能够利用 Sun(和其他公司)生产的新一代多核芯片的所有功能。这些都是我以前遇到过的问题,那么针对这个特定市场的这些特定版本的问题有多难呢?我同意花几个月时间在这个项目上,清理架构并确保它走在正确的轨道上,同时思考我可能想要解决的新研究课题。
三个月变成了两年(并且还在继续)。我发现了很多新的研究挑战,但它们都与寻找使在线游戏和虚拟世界环境扩展的方法有关。在这个过程中,我被介绍到一个不同的计算世界,它有不同的问题、不同的假设和不同的环境。有时我感觉自己像一个发现了新文明的人类学家。我仍在学习游戏的文化和实践,这是一个不同的世界。
理解这个新世界的第一件事是,它是娱乐产业的一部分。因此,游戏或虚拟世界最重要的目标是它要有趣。其他一切都次于这一首要指令。“有趣”不是一个客观的衡量标准,但目标是提供一种沉浸式的、全神贯注的体验,奖励玩家的出色表现,易于学习但难以精通,并且会让玩家一次又一次地回来。
大多数在线游戏都围绕一个故事和一个世界展开,故事和世界的丰富性在很大程度上决定了游戏的成功。游戏的设计侧重于故事和游戏玩法。用于实现游戏的代码的设计来得相当晚(并且通常被认为远没有那么有趣)。制作人领导构建游戏或虚拟世界的团队。团队成员包括作家、艺术家和音乐家,以及程序员。对游戏影响最小的群体是程序员;他们的工作是将其他人的愿景变为现实。
在线游戏或虚拟世界的计算环境与高科技产业服务的大多数市场中的环境几乎完全相反。客户端绝不是轻量级的;游戏玩家将使用他们能获得的最高端计算平台,或者专门为这些游戏的计算严苛性而设计的游戏机。这些客户端机器将拥有尽可能多的内存、最新最快的 CPU 以及自身具有超级计算能力的图形子系统。这些客户端还将具有相当大的持久存储容量,因为这些游戏的基本方法之一是将尽可能多的信息放在客户端上。
对重量级客户端的需求,部分是这些游戏发展演变的结果。在线游戏是从独立产品发展而来的,在独立产品中,一切都在本地机器上完成。然而,这不仅仅是行业内的熵;尽可能多地将内容保留在客户端上可以最大限度地减少与服务器的通信,无论是与服务器的调用次数,还是这些调用中传递的信息量。这种通信最小化是满足“有趣”这一首要指令所必需的,因为它是最大限度地减少这些游戏中延迟的方式之一。
延迟是乐趣的敌人——因此也是在线游戏和虚拟世界的敌人。这在在线游戏的情况下尤其有趣,在在线游戏中,客户端和服务器之间的连接延迟是无法控制的。因此,通信协议需要尽可能简单,并且从客户端传输到服务器的信息必须尽可能地适合单个数据包。此外,服务器需要设计得使其工作量非常小,确保无论它在做什么都可以非常快速地完成,以便可以将响应发送回玩家。已经开发出一些有趣的技巧来掩盖玩家无法避免的延迟。这些技巧包括在加载任务期间显示预先录制的剪辑,或者立即显示对动作结果的“最佳猜测”,然后在服务器响应时修复该猜测与实际结果之间的任何差异。
服务器的作用是双重的。最明显的是允许玩家在游戏环境中相互互动。随着这些游戏和世界变得越来越精细,这一作用变得越来越重要和越来越复杂。服务器的最初作用是允许玩家在游戏中相互竞争。现在,游戏和虚拟世界正在发展自己的社会,玩家可以在其中竞争,也可以合作或只是以各种方式互动。虚拟世界允许用户尝试新的个性;游戏让玩家合作完成他们个人无法完成的任务。在这两者中,玩家都发现该技术的主要吸引力在于使用它来连接其他人。
服务器的第二个作用是充当客户端之间真理的仲裁者。无论客户端是在游戏机还是在个人计算机上运行,控制权都掌握在玩家手中。这意味着玩家可以访问客户端程序,并且游戏的竞争性赋予了玩家修改客户端以利于自己的动机。即使在只有社交竞争的虚拟世界中,渴望“增强个人玩家的机会”(也称为“作弊”)也很常见。这就要求服务器(它是唯一不受玩家控制的组件)成为游戏真实状态的仲裁者。游戏服务器既用于阻止作弊(通过使其更加困难),又用于检测作弊(通过查看客户端报告的游戏状态与服务器持有的游戏状态之间的差异模式)。点对点技术似乎很自然地适合游戏服务器的第一个角色,但第二个角色意味着很少有游戏或世界足够信任其对等方以避免服务器组件。
在前一节中使用单数术语“服务器”代表了系统结构的 conceptual illusion,这种 illusion 只能由游戏或世界的客户端来维持。事实上,任何在线游戏或虚拟世界都将涉及大量服务器(或者已经惨败到没有人能够或想要记住该游戏或世界)。使用多个服务器是在当今在线世界中看到的水平上扩展游戏服务器组件的基本机制。据报道,《魔兽世界》拥有超过 500 万订阅者,任何时候都有数十万活跃用户。《第二人生》的报告使用量在《魔兽世界》的数量级之内,并且有证据表明 Webkinz 或 Club Penguin 等网站甚至更受欢迎。单个服务器无法处理如此大的负载,无论表示形式多么高效。即使单个服务器可以处理此负载,这样的服务器对于在需求低时(有时是同一游戏或世界)或在产品生命周期中需求减少时遇到的较小负载来说也太昂贵了。
拥有多个服务器意味着构建游戏的一部分是决定如何将负载分配到这些服务器上。在线游戏和虚拟世界中通常使用两种技术。有时只使用其中一种技术,有时两种都使用,具体取决于游戏或世界的性质。
第一种技术是利用游戏或世界的地理位置,将游戏分解为不同的区域,每个区域都可以映射到托管服务器。例如,《第二人生》中的一个岛屿对应于运行世界共享现实代码的物理服务器。同样,《魔兽世界》宇宙的不同区域托管在不同的物理机器上。在该区域中的任何人都将连接到同一服务器,并且该服务器上玩家之间的交互可以本地化(和优化)。世界不同部分发生的动作不太可能影响世界这一部分的动作,因此服务器之间的通信流量可以保持较小。
第二种技术称为分片。分片是游戏或虚拟世界一部分的副本。不同的分片驻留在不同的服务器上,分配给一个分片的玩家可以与分片中的世界和其他玩家互动,但看不到(或无法与)其他分片中的玩家或对象互动。分片不仅允许在世界中支持更多玩家,而且还允许不同的玩家集对世界进行独立探索。因此,当向游戏中添加新的任务或使命时,通常会复制多个分片,以便多个玩家(或玩家组)可以在其原始状态下体验任务或使命。
虽然分片和地理分解允许使用多个服务器来处理单个游戏或世界的负载,但它们确实给开发人员带来了重大挑战。通过创建世界各部分的不交互副本,分片将不同分片中的玩家彼此隔离。这意味着想要分享他们对世界或游戏的体验的玩家需要意识到正在提供的不同分片,并安排放置在同一分片中。随着想要在同一分片中的玩家数量增加(一些公会——在相当长一段时间内合作玩单个游戏的玩家群体——拥有数百名成员),协调放置到分片的难度增加,并干扰了世界的体验。虽然分片允许扩展,但它们是以玩家互动为代价来实现的。
地理分解不会限制玩家互动,但确实要求游戏设计师能够预测地理区域的大小,这将是分解的正确单位。如果一个地理区域变得非常受欢迎,则该区域的服务器过载会导致该区域的游戏速度变慢。如果地理区域不如最初预测的那样受欢迎,则计算机硬件(和金钱)将浪费在该部分上,因为没有足够的玩家在那里。由于地理分解硬连线到游戏或世界的代码中,因此更改分解以响应观察到的用户行为需要重写游戏或世界本身的一部分。这需要时间,可能会引入错误,并且成本很高。在进行此操作时,游戏玩法可能会受到不利影响。在极端情况下,这可能会产生重大的财务影响。当《魔兽世界》推出时,对游戏的需求远远超过了其容量,以至于订阅不得不关闭数月,同时重写了分发游戏的代码。
在一组机器上扩展是一个分布式计算问题,游戏和虚拟世界编程文化在这组问题上几乎没有经验。这绝不是扩展要求游戏程序员学习一套新技能的唯一地方。芯片设计趋势的变化也意味着这些程序员必须学习他们以前从未需要掌握的技能。
除了科学计算的最高端之外,没有其他类型的软件像游戏或虚拟世界程序那样积极地利用摩尔定律的进步。随着芯片变得更快,游戏和虚拟世界变得更加逼真、更复杂、更具沉浸感。认真的游戏玩家会投资购买他们能获得的最好的设备,然后使用超频等技术来进一步提高这些系统的性能。
然而,现在,芯片设计师已经决定以不同的方式利用摩尔定律。他们不是提高芯片的速度,而是在以相同(或有时更慢)的时钟速度运行的芯片上添加多个内核。这样做有很多充分的理由,从简化设计到降低功耗和散热,但这意味着当你在新芯片上运行程序时,单个程序的性能不会自动提高。一组程序的整体性能可能会提高(因为它们都可以并行运行),但单个程序的性能不会提高(除非它可以分解为多个协作线程)。然而,游戏是作为单线程程序编写的。
事实上,游戏和虚拟世界(尤其是这些程序的服务器端)应该是展示多核芯片和协作服务器组可能实现的性能提升的完美载体。游戏和虚拟世界是令人尴尬地并行的,因为其中发生的大多数事情都独立于其他正在发生的事情。在任何时候活跃在《魔兽世界》中的数十万玩家中,只有极少数人会与任何特定玩家互动。《第二人生》和几乎所有大型游戏或世界也是如此。
问题是围绕游戏和虚拟世界发展起来的文化并不是一种理解或过度熟悉利用这些系统中固有并行性所需的编程技术的文化。这些人是在单台(PC)机器上长大,运行单线程的人。要求他们掌握并发编程或分布式系统的复杂性会使他们无法专注于游戏或世界体验本身。即使他们有愿望,他们也没有时间和经验来利用这些新技术。
正是由于这些原因,我们启动了 Project Darkstar (http://www.projectdarkstar.com),这是一项研究工作,旨在构建一个服务器端基础设施,该基础设施将利用正在生产的多线程、多核芯片,并在大型机器组上扩展,同时向程序员呈现一种错觉,即他或她正在单线程、单机环境中开发。在一般情况下,隐藏线程和分布式可能不是一个好主意(有关完整论证,请参见 http://research.sun.com/techrep/1994/abstract-29.html)。然而,游戏和世界服务器倾向于遵循非常受限的编程模型,我们相信我们可以在其中隐藏并发和分布式。
该模型是一个简单的基于事件的模型,其中来自客户端的输入由服务器接收,然后服务器启动一个任务以响应该事件。这些任务可以更改世界的状态(通过移动玩家、更改对象的状态等)并启动通信。通信可以是到单个客户端,也可以是到订阅同一通信通道的所有客户端组。
我们选择此模型主要是因为这是大多数游戏和虚拟世界服务器已经构建的方式。当时的挑战是保持此模型,并允许以这种风格编写的服务器在多个内核(运行多个线程)和多个服务器上扩展。我们没有尝试获取现有代码并允许它在我们的系统中运行。这将使任务变得更加困难,并且不符合游戏和虚拟世界文化的现实。游戏和世界服务器是为每个游戏或世界从头开始编写的,可能会重用一些库,但一旦运行,很少会被重新托管到不同的环境中。将不同平台引入游戏的努力仅限于客户端,因为新的游戏机引入新玩家可能值得付出努力。
Darkstar 提供了一个容器,服务器在其中运行。该容器提供了一组服务的接口,允许游戏服务器保持持久状态、建立与客户端的连接以及构建与客户端集成的发布/订阅通道。游戏服务器代码的多个副本可以在 Darkstar 容器的多个实例中运行。每个副本都可以编写得好像它是唯一活动的副本(事实上,对于小型游戏或世界,它可能是唯一活动的副本)。每个服务器都构建为事件循环——主循环侦听与客户端的会话,该会话在客户端登录时建立。当消息传递时,调用事件循环。然后,循环可以解码消息并确定作为适当响应的游戏或世界动作。然后,它在容器内分派一个任务。
这些任务中的每一个都可以通过 Darkstar 数据服务读取或更改世界中的数据,与客户端通信,或通过通道向其他游戏或世界参与者组发送消息。在底层,任务被包装在一个事务中。事务用于确保不会发生对世界数据的冲突并发访问。如果一个任务试图更改正在被其他并发任务更改的数据,则数据服务将检测到该冲突。在这种情况下,冲突的任务之一将被中止并重新安排;另一个任务应运行完成。因此,当重试中止的任务时,冲突应该已经消失,并且任务应该运行完成。
这种并发控制机制确实要求所有任务都通过 Darkstar 数据服务访问其所有数据。这与通常的游戏或世界服务器编程方式不同,在通常的编程方式中,数据保存在内存中以减少延迟。通过使用过去 20 年数据库研究的结果,我们相信我们可以通过智能地缓存数据来使通过数据服务访问的惩罚保持较小。我们还相信,通过利用这些游戏中固有的并行性,即使单个数据访问存在少量惩罚,我们也可以随着玩家数量的增加而提高游戏的整体性能。我们的数据存储不是基于标准 SQL 数据库,因为我们不需要这种数据库提供的全部功能。我们需要的是能够快速访问以简单方式识别的持久存储对象。我们当前的实现为此使用了 Berkeley 数据库,尽管我们已经抽象了对其的访问,以便在需要时提供使用其他持久层的机会。
并发控制不是要求所有数据都通过数据存储访问的唯一原因。通过以持久方式备份数据而不是将其保存在主内存中,我们获得了一些过去游戏或世界中没有表现出的固有可靠性。将所有数据存储在内存中意味着服务器崩溃可能会导致自上次系统检查点以来游戏中或世界中发生的任何更改丢失。这有时可能是数小时的游戏时间,这可能会引起客户的相当大的惊慌,并导致昂贵的呼叫服务热线。通过持久地保存所有数据,我们相信我们可以确保在服务器崩溃的情况下,游戏或世界交互的损失不会超过几秒钟。在最佳情况下,玩家甚至不会注意到此类崩溃,因为服务器上的任务将以对玩家透明的方式转移到另一台服务器。
要求所有数据都保存在数据存储中的最大回报是,它有助于使由响应游戏中事件生成的任务可移植。由于运行 Darkstar 堆栈和游戏逻辑的机器集群中的任何机器都可以访问数据存储,因此没有数据不能从一台机器移动到另一台机器。我们对通信机制也做了同样的事情,确保连接游戏和某些客户端集的会话或通道通过 Darkstar 堆栈进行抽象。这允许我们将使用会话或通道的任务移动到另一台机器,而不会影响通过会话或通道交谈的任务的语义。
这种任务可移植性意味着我们可以动态平衡运行游戏或虚拟世界的一组机器上的负载。基于 Darkstar 堆栈的虚拟世界或游戏不是在编译时将游戏分解为区域或分片,而是在运行时在服务器机器网络中移动负载。虽然参与者可能会在移动期间看到延迟的短暂增加,但在移动后整体延迟将减少。通过移动任务,我们不仅可以平衡所涉及机器的负载,还可以尝试将访问同一组数据或相互通信的任务放在一起。所有这些机制都允许我们在游戏进行时确定哪些任务(以及哪些用户)应该放置在同一服务器上。
该项目正处于开发和部署的早期阶段。它基于开源许可模式和社区,因此我们依靠我们的用户来教育我们关于将构建在基础设施上运行的游戏和世界的社区的需求。这项研究一部分是计算机科学,一部分是人类学,但每种文化都有机会从另一种文化中学到很多东西。
即使在早期阶段,也很明显这将是一项复杂的冒险。虽然使用代码的早期经验表明,编程模型确实使游戏或世界服务器程序员无需考虑线程和锁定,但它也表明,在某些地方,他们确实必须了解系统底层并发的一些知识。其中最明显的是数据结构的设计。我们代码的最早用户之一从系统中获得了糟糕的性能。当我们查看代码时,我们发现每个任务都写入一个对象,更新全局游戏状态。通过以这种方式设计服务器,此用户有效地序列化了系统中运行的所有任务,使得服务器不可能从游戏中固有的并行性中获得任何优势。一些小的重新设计,将单个对象分解为许多(小得多的)对象,消除了这个特定的瓶颈,从而提高了整体性能。这种经验也告诉我们,我们需要教育系统的用户设计可以并行访问的独立数据结构。
我们自己的实现并非没有一些激动。当我们从在单台机器上运行的多线程服务器转移到在多台机器上运行的实现时,我们预计单机系统的性能会有所下降。我们很高兴地发现单节点系统的性能下降远没有我们想象的那么大,但我们发现额外的机器降低了整个系统的容量。当呈现这些测量结果时,理解这一点并不令人意外——多台机器上发生争用的可能性大于单台机器,并且发现和从这种争用中恢复所需的时间更长。我们正在努力消除瓶颈,以便添加设备实际上可以增加容量。
衡量系统的性能尤其具有挑战性,因为缺乏对目标服务器要求的明确概念。游戏开发商以保密而闻名,并且游戏或虚拟世界的特征负载的概念并不是一个有据可查的概念。我们有一些由团队或我们游戏界认识的人编写的示例,但我们不能确定这些示例是否准确反映了行业正在编写的内容。我们希望围绕该项目开始形成的开源社区将有助于生成有用的性能和压力测试。
从更广阔的角度来看,该项目一直并将继续是一项有趣的实验,旨在为多线程、分布式系统世界构建抽象级别。我们正在解决的问题并不新鲜。大型 Web 服务场在高度可变的需求方面有许多相同的问题。科学网格在多台机器上扩展方面也有类似的问题。搜索网格在处理大规模环境解决令人尴尬但并非完全并行的问题方面也存在类似的问题。
使在线游戏和虚拟世界有趣地与众不同的是,与这些其他领域相比,它们带来了非常不同的要求。交互式、低延迟环境与网格、Web 服务或搜索截然不同。娱乐行业的增长也使工程学科与其他学科截然不同。在这种新环境中解决这些问题具有挑战性,并增加了我们关于如何在新兴的多线程、多核、分布式系统类上编写软件的一般知识。
最棒的是,它很有趣。
JIM WALDO 是 Sun Microsystems 实验室的杰出工程师,他在那里进行大规模分布式系统的研究。在(重新)加入 Sun Labs 之前,他是 Jini 的首席架构师,Jini 是一个基于 Java 的分布式编程系统。他在 Apollo Computer 和 Hewlett-Packard 工作了八年,在那里他领导了第一个对象请求代理的设计和开发,并为将该技术纳入第一个 OMG CORBA 规范做出了贡献。Waldo 是哈佛大学的兼职教师,他在计算机科学系教授分布式计算。他拥有哲学博士学位,拥有语言学和哲学硕士学位,并且从未上过真正的计算机科学课程。
最初发表于 Queue 第 6 卷,第 7 期—
在 数字图书馆 中评论本文
Walker White、Christoph Koch、Johannes Gehrke、Alan Demers - 更好的脚本,更好的游戏
2007 年,视频游戏产业收入达到 88.5 亿美元,几乎与电影在票房的收入一样多。这些收入中的大部分来自大型团队创作的热门游戏。虽然大型开发团队在软件行业中并不鲜见,但游戏工作室往往拥有独特的开发人员集合。软件工程师在游戏开发团队中所占比例相对较小,而团队的大部分由内容创作者组成,例如艺术家、音乐家和设计师。
Mark Callow、Paul Beardow、David Brittain - 大型游戏,小型屏幕
在创建和分发移动 3D 游戏时,立即变得明显的一件事是,手机市场与更传统的游戏市场(例如游戏机和掌上游戏设备)之间存在根本差异。其中最引人注目的是交付平台的数量;设备的严重限制,包括方向可以改变的小屏幕;有限的输入控制;处理其他任务的需要;非物理交付机制;以及手机性能和输入能力的变化。
Nick Porcino - 游戏图形:走向革命之路
从平铺块背景上的彩色精灵到现代游戏的沉浸式 3D 环境,这是一个漫长的旅程。曾经是单个游戏创作者的工作现在已成为一项多方面的制作,涉及来自各个创意学科的员工。下一代游戏机和家用电脑硬件将带来可用计算能力的革命性飞跃;每秒万亿次浮点运算 (teraflop) 或更高的性能将来自商品硬件。
Dean Macri - 可扩展性问题
在 90 年代中期,我曾经在一家公司工作,这家公司开发多媒体信息亭演示程序。我们最大的客户是英特尔,我们经常为他们制作演示程序,这些演示程序会出现在新款电脑上,并在像 CompUSA 这样的大型电脑零售店的显眼处展示。在那个时候,从商务到消费的各种应用领域,性能都是关键。我们制作演示程序,用以展示,例如,新款处理器在电子表格重新计算速度上能比前代快多少(那时还需要手动重新计算)。即使是普通用户也能一眼就能看出这些差异,而且这一点至关重要。