【译】游戏性能的状态报告

发表于2016-02-21
评论3 2.6k浏览

【译】游戏性能的状态报告

作者:CHRIS LUNDQUIST

翻译出处:http://blogs.unity3d.com/2015/12/02/the-state-of-game-performance-reporting/

原文作者未做权利声明,视为共享知识产权进入公共领域,自动获得授权

  许多人使用过我们的游戏性能报告服务来获取崩溃报告,并对我们计划的跟进产生了疑问。我和我的团队已经阅读了大家所有的帖子,进而我们知晓了大家都期待很酷的新功能! 所以,带着需求和想法来吧!我们已经花费过去的几个月改造了从支持一个项目到支持一个超酷的、多层次的用来处理所有数以万计的Unity游戏的web应用程序的基础设施。继续阅读以了解更多我们所做的吧。


背景

  Unity 游戏性能起源于一年前作为一个Unity黑客周的项目,仅仅是为了尝试新事物。 我们当中有来自不同背景的人付出努力,并且每个人负责一部分的崩溃服务,包括有:

· Javascript的界面

· 界面所使用的导轨接口

· 崩溃报告摄取通道

  用户界面的变化可能是最明显的。你可能已经注意到developer.cloud.unity3d.com平台已经推出,其目的是统一访问我们越来越多的服务。

  对于这三块的崩溃服务,在过去的12个月里变化最大的是摄取通道。这些摄入量的变化(幸运地)是不可见的,但它们是至关重要的,因为我们要支持一切Unity游戏。

它是如何工作的呢

本来, 摄入管道应该是像这样的:

编辑器插件 -> 节点 -> SQS + DynamoDB -> 导轨 -> MySQL

  编辑器插件监听了异常,对它们批处理,然后把它们发送到节点。节点监听了事件,把它们放入 DynamoDB,然后它以SQS消息的形式发送到导轨以找到Dynamo中对应的事件。然后导轨把它从Dynamo中拿回来,进行处理,并将数据存储在MySQL中。虽然这个工作流程显然是成立的,但至少这并不是很优雅的做法。   

  当时SQS有一个相当小的消息规格限制;不足以来存储各种规模的异常。这就是为什么SQS消息仅仅把事件的声明存储在Dynamo。SQS 已经扩容了消息的大小限制为2GB(可缓解我们存储异常的困扰)。起初,我们把我们收到的每个事件存储在Dynamo,是为了以防万一我们犯了什么巨大的错误,因为我们总是可以通过重播事件来重新导入数据。

当我们实际操作时发生了什么呢

  我们在GDC ‘15期间推出我们的黑客小项目,并且我们进行得比我们想象中要活跃。我们预期的是每天有数以千计的异常——实际上我们做到了数以百万计。我们不得不限制某些每秒发送以千计异常的项目速率。

  除了运行问题以外,我们注意到我们的设置有一个很大的瓶颈。事件(异常)放入SQS和Dynamo,仅从导轨获取它们,并处理它们把它们放到数据库中。我们所花费的时间,仅是导轨方面,每个异常前后就花了75ms!

  对于原设置的一个乐观的事情是,接受事件和读取事件的方法是解耦的。这种设计可以在我们更新代码的同时,轻松启动或终止处理,而不会丢失任何事件。

我们接下来做的

抽象地说,处理一个崩溃报告包括以下步骤:

1. 跟踪它的轨迹,

2. 通过轨迹找到或创建,

3. 增加计数器,

4. 将它与我们所看到它的操作系统、平台相关联。

  当然,仅对于快捷方面,我打算的是用我没学过的(Golang)来替换我不喜欢(节点)。我尝试这么做,但也明白它不能再做的更好了,因为Golang的AWS库还是太年轻。所以我决定尝试替换整个摄入通道,仅仅是简化它。

我的目标是这样写的:

编辑器插件 -> Go -> MySQL

  我想要的是简单和快捷。我不想看到因冗长的记录日志或滥用的Ruby进程而产生的磁盘空间警报。我的流程是这样走的:

  我最初的实现是来自一个导轨的直译。它仍然是执行MySQL的select语句,然后创建行或更新计数器。

  我的第一个优化是删除了所有报告之间被复制的语句。这些重复的语句是SELECT 语句,如‘SELECT id FROM operating_systems where name = “Windows 7”’。这些语句是完全安全的应用缓存,并且我很大程度的利用了 Hashicorp’s go LRU hash来处理它。然后我执行了相同的最优化缓存崩溃痕迹,目的是我不必在我每次看到同样的异常时都要去访问数据库。

  我不得不在这些每个LRU散列中执行相当数量的锁定,虽然感觉它并不是很像Go类型的,但是它行之有效。我还做了一件事情就是做了 细粒度锁 ,目的是我可以同时更新不同的密钥。

  我遇到的下一个瓶颈大概要这么说:每写一个事件我都要去增加计数器。我的数据库保守数量在1~100,000,000。一次一个。

  我知道我想要批处理我所写的,但我想用一个可靠的方法。我又再次拿起了Hashicorp的 LRU 散列,它提供了一个 上逐出钩。这样,当崩溃报告从我的内存被驱逐出时,它就会被写入数据库。但转念一想,”如果我没有得到足够的特定崩溃报告来触发一个驱逐呢?”所以,我去掉了它并添加另一个方法让你可以以存活时间来添加一个条目。

  重要注意,存活时间存在于每个条目。这样一来,每个存活时间的逐出是交错的,使之不产生 异常成群 的数据库写入。

  鉴于所有上述考虑,一个AWS t2.medium 实例可以(突发)约10000请求每秒,这是相当不错的作用。

  我们还计划在不同地区拥有边缘服务器。你的游戏将在最近的地理区域给服务器发送报告。这些服务会做同样的批处理,然后它们会将事件转发到数据库所在的区域。它们会使用相同的逐出钩来生成一个HTTPS请求而不是数据库调用。

  TL;DR:, 我知道这里没有太多关于游戏性能报告的新闻,但我们从未忘记它。我希望这篇文章能帮助你理解我们一直在幕后所付出的。在我们的论坛与我们保持交流哦!

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引