Babylon.js 迁移到 Azure 的原因和流程

你正在工作 对于一家初创公司来说。突然间,那一年辛苦的编码工作得到了回报——成功了 随着网络应用程序规模的不断增长和需求的增加。

在这个 教程,我想谦虚地使用我们最近的“成功案例”之一 围绕我们的 WebGL 开源游戏框架 Babylon.js 及其网站。我们很高兴看到这么多网络 游戏开发者尝试一下。但为了满足需求,我们知道我们需要一个新的 网络托管解决方案。

虽然本教程重点介绍 Microsoft Azure,但其中的许多内容 这些概念适用于您可能喜欢的各种解决方案。我们还将看到我们为限制尽可能多的各种优化 可能是从我们的服务器到您的浏览器的输出带宽。

简介

Babylon.js 是一个 我们已经进行了一年多的个人项目。因为这是一个个人项目(即 我们的时间和金钱),我们将网站、纹理和 3D 场景托管在 使用小型专用 Windows/IIS 机器的相对便宜的托管解决方案。 该项目在法国启动,但很快就引起了一些 3D 和 全球各地的网络专家以及一些游戏工作室。我们很高兴 社区的反馈,但流量是可控的!

例如,之间 2014 年 2 月和 2014 年 4 月,我们每月平均有超过 7000 名用户,平均 每月查看的页面数量超过 16K。我们一直在谈论的一些活动 生成了一些有趣的峰:

但是 网站上的体验还是足够好的。场景加载尚未完成 以惊人的速度,但用户并没有抱怨那么多。

但是, 最近,一位很酷的人决定在 Hacker News 上分享我们的工作。听到这样的消息我们真的很高兴! 但看看网站的连接发生了什么:

游戏结束我们的小服务器!它慢慢地 停止工作,我们的用户体验非常糟糕。 IIS 服务器 花费时间处理大型静态资产和图像,以及 CPU 使用率 太高了。当我们即将推出《刺客信条:海盗》WebGL 时 体验在 Babylon.js 上运行的项目,是时候切换到更具可扩展性的了 使用云解决方案进行专业托管。

但是之前 回顾我们的托管选择,让我们简要谈谈我们的具体情况 引擎和网站:

  1. 一切 在我们的网站上是静态的。我们目前没有任何服务器端代码 跑步。
  2. 我们的场景(.babylon JSON 文件)和纹理(.png 或 .jpeg) 文件可能非常大(最多 100 MB)。这意味着我们绝对 需要在我们的 .babylon 场景中激活 gzip 压缩 文件。事实上,在我们的例子中,定价将在很大程度上被索引 输出带宽。
  3. 绘制到 WebGL 画布中需要特殊的安全检查。你 例如,在没有启用 CORS 的情况下,无法从另一台服务器加载我们的场景和纹理。

鸣谢:我要特别感谢 Benjamin Talmard,我们的法国 Azure 技术人员之一 帮助我们迁移到 Azure 的布道者。

1. 迁移到 Azure 网站和 自动缩放服务

如我们所愿 为了花费大部分时间为我们的引擎编写代码和功能,我们 不想在管道上浪费时间。这就是为什么我们立即决定 选择 PaaS 方法而不是 IaaS 方法。

此外,我们 喜欢 Visual Studio 与 Azure 的集成。我几乎可以做任何事情 最喜欢的 IDE。即使 Babylon.js 托管在 GitHub 上,我们也使用 Visual Studio 2013, TypeScript 和 Visual Studio Online 来编写我们的引擎。作为您的注释 项目,您可以免费获得 Visual Studio Community 和 Azure 试用版。

迁移到 Azure 我花了大约五分钟:

  1. 我在管理页面中创建了一个新网站:http://manage.windowsazure.com(可以这样做 VS 内也是如此)。
  2. 我拿了 我们的源代码存储库中的正确变更集与之前的版本相匹配 目前在线。
  3. 我右键单击 Visual Studio 解决方案资源管理器中的 Web 项目。

现在来了 工具的出色之处。当我使用 Microsoft 登录到 VS 时 帐户绑定到我的 Azure 订阅,向导让我只需选择 Web 我想要部署的网站。

无需 担心复杂的身份验证、连接字符串或其他任何问题。

下一个,下一个, 下一步并发布”,几分钟后,在文章的末尾 我们所有资产和文件的上传过程,网站已启动并运行!

关于 配置方面,我们希望受益于酷炫的自动缩放服务。它 对我们之前的黑客新闻场景会有很大帮助。

首先,你的 实例已在缩放选项卡中配置为标准模式。

然后,您可以 选择您想要自动扩展的实例数量,其中 CPU 状况以及计划时间。

在我们的例子中,我们已经 决定使用最多三个小型实例(1 核、1.75 GB 内存)并 如果 CPU 利用率超过 80%,则自动生成一个新实例。我们将 如果 CPU 下降到 60% 以下,请删除一个实例。自动缩放机制是 在我们的例子中始终开启 - 我们尚未设置具体的预定时间。

这个想法是 真正只为您在特定时间范围内需要的东西付费 负载。我喜欢这个概念。有了这个,我们就能够处理 借助此 Azure 服务,无需执行任何操作即可达到之前的峰值!

您还可以通过紫色图表快速查看自动缩放历史记录。在我们的例子中, 自从我们迁移到 Azure 以来,到目前为止我们从未检查过一个实例。我们是 下面将介绍如何最大程度地降低陷入自动缩放的风险。

总结一下 在网站配置中,我们想要启用自动 gzip 压缩 在我们的特定 3D 引擎资源上(.babylon.babylonmeshdata 文件)。这对我们来说至关重要,因为它可以节省高达 3 倍的带宽 并且 因此……价格。

网站是 运行在 IIS 上。要配置 IIS,您需要进入 web.config 文件。我们在本例中使用以下配置:

<system.webServer>

  <staticContent>

    <mimeMap fileExtension=".dds" mimeType="application/dds" />

    <mimeMap fileExtension=".fx" mimeType="application/fx" />

    <mimeMap fileExtension=".babylon" mimeType="application/babylon" />

    <mimeMap fileExtension=".babylonmeshdata" mimeType="application/babylonmeshdata" />

    <mimeMap fileExtension=".cache" mimeType="text/cache-manifest" />

    <mimeMap fileExtension=".mp4" mimeType="video/mp4" />

  </staticContent>

  <httpCompression>

    <dynamicTypes>

      <clear />

      <add enabled="true" mimeType="text/*"/>

      <add enabled="true" mimeType="message/*"/>

      <add enabled="true" mimeType="application/x-javascript"/>

      <add enabled="true" mimeType="application/javascript"/>

      <add enabled="true" mimeType="application/json"/>

      <add enabled="true" mimeType="application/atom+xml"/>

      <add enabled="true" mimeType="application/atom+xml;charset=utf-8"/>

      <add enabled="true" mimeType="application/babylonmeshdata" />

      <add enabled="true" mimeType="application/babylon"/>

      <add enabled="false" mimeType="*/*"/>

    </dynamicTypes>

    <staticTypes>

      <clear />

      <add enabled="true" mimeType="text/*"/>

      <add enabled="true" mimeType="message/*"/>

      <add enabled="true" mimeType="application/javascript"/>

      <add enabled="true" mimeType="application/atom+xml"/>

      <add enabled="true" mimeType="application/xaml+xml"/>

      <add enabled="true" mimeType="application/json"/>

      <add enabled="true" mimeType="application/babylonmeshdata" />

      <add enabled="true" mimeType="application/babylon"/>

      <add enabled="false" mimeType="*/*"/>

    </staticTypes>

  </httpCompression>

</system.webServer>

这个解决方案 工作得很好,我们甚至注意到加载场景的时间已经 与我们之前的主机相比有所减少。我猜这要归功于 Azure 数据中心使用更好的基础设施和网络。

但是, I have 一段时间以来一直在考虑迁移到 Azure。我的第一个想法不是 让网站实例为我的大量资产提供服务。从一开始,我就更感兴趣将我的资产存储在更适合设计的 Blob 存储中 那。它还将为我们提供一个可能的 CDN 场景。

2. 将资产移至 Azure Blob 存储、启用 CORS、Gzip 支持和 CDN

主要原因 在我们的例子中使用 blob 存储是为了避免加载我们网络的 CPU 为他们提供服务的站点实例。如果一切都通过 blob 提供 除了一些 HTML、JavaScript 和 CSS 文件之外,我们的网站实例还会有存储 自动缩放的机会很少。

但这引发了 需要解决两个问题:

  1. 作为 内容将托管在另一个域名上,我们将陷入 跨域安全问题。为了避免这种情况,您需要在 远程域(Azure Blob 存储)。
  2. Azure Blob 存储不支持自动 gzip 压缩。和我们 不想降低 CPU 网站使用率,但作为交换,我们要因为带宽增加而支付三倍的价格!

在 Blob 存储上启用 CORS

blob 上的 CORS 存储已经支持几个月了。本文“Windows Azure 存储:CORS 简介”介绍了如何使用 Azure API 来 配置 CORS。就我而言,我不想编写一个小应用程序来做到这一点。我有 在网上找到一个已经写好的:Cynapta Azure CORS Helper – Free Tool 管理 Windows Azure Blob 存储的 CORS 规则。

然后我就 在我的容器上启用了对 GET 和正确标头的支持。检查是否 一切都按预期进行,只需打开 F12 开发者栏并检查 控制台日志:

如您所见, 绿色日志行意味着一切运行良好。

这里有一个 它将失败的示例案例。如果您尝试从我们的 blob 加载我们的场景 直接从本地主机(或任何其他域)存储,您将得到 日志中的这些错误:

总而言之, 如果您发现在“Access-Control-Allow-Origin”中找不到您的调用域 标头后面带有“访问被拒绝”,这是因为您 没有正确设置您的 CORS 规则。 控制自己的情绪非常重要 CORS 规则;否则,任何人都可以使用您的资产,从而您的 带宽,花钱而不让你知道!

在 Blob 存储上启用 Gzip 支持

就像我一样 之前告诉过您,Azure Blob 存储不支持自动 gzip 压缩。竞争对手的解决方案似乎也是如此 S3。您有两种选择来解决这个问题:

  1. 上传前在客户端上对文件进行 Gzip 压缩自己上传,将其上传到 Blob 中 使用经典工具进行存储并设置 content-encoding 标头 到 gzip。此解决方案有效,但仅适用于支持 gzip 的浏览器(是 还有一个浏览器不支持 gzip 吗?)。

  2. 在客户端上自行对文件进行 Gzip 压缩,并在 blob 存储:一个使用默认 .extension,另一个使用.extension.gzip, 例如。在 IIS 端设置一个处理程序来捕获 HTTP 请求 从客户端检查 accept-encoding 设置为 gzip 的标头,并根据此支持提供相应的文件。你会发现更多细节 有关本文中要实现的代码:从 Azure CDN 提供 GZip 压缩内容。

就我们而言,我 不知道有哪个浏览器支持 WebGL 而不是 gzip 压缩。所以如果 浏览器不支持 gzip,没有真正的兴趣继续下去,因为这个 可能意味着 WebGL 也不被支持。

因此我选择了第一个解决方案。因为我们没有很多场景,而且我们也没有 每天生产一个新的,我目前正在使用这个手动过程:

  1. 使用 7-zip,我正在压缩 .babylon 我的机器上的文件使用 gzip 编码并将“压缩级别”设置为“最快”。 其他压缩级别似乎在我的测试中产生了问题。
  2. 我使用适用于 Microsoft Azure 的 CloudBerry Explorer 上传文件 云储存。
  3. 我使用 CloudBerry 手动将 HTTP 标头 content-encoding 设置为 gzip

我知道什么 你在想。我要对我的所有文件执行此操作吗?!?不,你可以工作 构建一个可以自动执行此操作的工具或构建后脚本。为了 例如,这是我构建的一个小命令行工具:

string accountName = "yoda";

string containerName = "wwwbabylonjs";

string accountKey = "yourmagickey";

string sceneTextContent;



// First argument must be the directory into the Azure Blob Container targeted

string directory = args[0];



try

{

StorageCredentials creds = new StorageCredentials(accountName, accountKey);

CloudStorageAccount account = new CloudStorageAccount(creds, useHttps: true);

CloudBlobClient client = account.CreateCloudBlobClient();

CloudBlobContainer blobContainer = client.GetContainerReference(containerName);

blobContainer.CreateIfNotExists();



var sceneDirectory = blobContainer.GetDirectoryReference(directory);



string[] filesArgs = args.Skip(1).ToArray();



foreach (string filespec in filesArgs)

{

    string specdir = Path.GetDirectoryName(filespec);

    string specpart = Path.GetFileName(filespec);



    if (specdir.Length == 0)

    {

        specdir = Environment.CurrentDirectory;

    }

    foreach (string file in Directory.GetFiles(specdir, specpart))

    {

        string path = Path.Combine(specdir, file);

        string sceneName = Path.GetFileName(path);



        Console.WriteLine("Working on " + sceneName + "...");



        CloudBlockBlob blob = sceneDirectory.GetBlockBlobReference(sceneName);

        blob.Properties.ContentEncoding = "gzip";

        blob.Properties.ContentType = "application/babylon";



        sceneTextContent = System.IO.File.ReadAllText(path);



        var bytes = Encoding.UTF8.GetBytes(sceneTextContent);

        using (MemoryStream ms = new MemoryStream())

        {

            using (GZipStream gzip = new GZipStream(ms, CompressionMode.Compress, true))

            {

                gzip.Write(bytes, 0, bytes.Length);

            }

            ms.Position = 0;

            Console.WriteLine("Gzip done.");

            blob.UploadFromStream(ms);

            Console.WriteLine("Uploading in " + accountName + "/" + containerName + "/" + directory + " done.");

        }

    }

}

}

catch (Exception ex)

{

Console.WriteLine(ex);

}

为了使用它,我 可以执行以下操作:

UploadAndGzipFilesToAzureBlobStorage 场景/Espilit C:\Boulot\Babylon\Scenes\Espilit\*.babylon* 推送包含多个的场景 文件(我们的增量场景包含多个 .babylonmeshdata 文件)。

或者简单地说:

UploadAndGzipFilesToAzureBlobStorage 场景/Espilit C:\Boulot\Babylon\Scenes\Espilit\Espilit.babylon 推送唯一文件

检查一下 gzip 使用此解决方案按预期工作,我使用的是 Fiddler。从客户端加载您的内容 机器并检查网络痕迹,返回的内容是否确实 已压缩且可以解压缩:

启用CDN

一旦你 完成前两个步骤后,您只需单击其中的一个按钮即可 用于启用 CDN 并将其映射到您的 Blob 存储的 Azure 管理页面:

就是这样 简单的!就我而言,我只需更改以下 URL:http://yoda.blob.core.windows.net/wwwbabylonjs/Scenes 到http://az612410.vo.msecnd.net/wwwbabylonjs/Scenes。请注意,您可以自定义此 CDN 如果您愿意,可以将域名添加到您自己的域名中。

多亏了这一点, 我们能够以非常快速的方式为您提供 3D 资产,因为您将得到服务 从此处列出的节点位置之一:Azure 内容交付网络 (CDN) 节点位置。

我们的网站是 目前托管在北欧 Azure 数据中心。但如果你来的话 从西雅图,您将 ping 该服务器只是为了下载我们的基本 index.html, index.js、index.css 文件和一些屏幕截图。所有 3D 资产将 从您附近的西雅图节点提供服务!

注意:我们所有的演示 正在使用完全优化的体验(使用 gzip、CDN 和 DB 的 Blob 存储) 缓存)。

3. 使用 HTML5 IndexedDB 避免 重新下载资源

优化 加载时间和控制输出带宽成本不仅仅是服务器端的问题。 您还可以构建一些逻辑客户端来优化事物。 幸运的是,我们从 Babylon.js 引擎 v1.4 开始就做到了这一点。我有 详细解释了我如何实现对 IndexedDB 的支持 在本文中:使用 IndexedDB 处理 3D WebGL 资产:分享 Babylon.JS 的反馈和技巧。您将找到如何激活它 我们的 wiki 上的 Babylon.js:在 IndexedDB 中缓存资源。

基本上,你 只需创建一个与 .babylon 名称匹配的 .babylon.manifest 文件 场景,然后设置您想要缓存的内容(纹理和/或 JSON 场景)。那是 它。

例如, 检查 Hill Valley 演示场景的情况。第一次加载时,以下是发送的请求:

153 项和 已收到 43.33 MB。但如果您同意让babylonjs.com“使用额外的存储空间 在您的计算机上”,这是您第二次加载时会看到的内容 同一场景:

1 项和 348 字节!我们只是 检查清单文件是否已更改。如果没有,我们将加载所有内容 来自数据库,我们节省了 43+ MB 的带宽

例如, 这种方法正在《刺客信条海盗》游戏中使用:

让我们考虑一下 那:

  • 游戏 加载一次后几乎立即启动,因为资源是 直接从本地数据库提供服务。
  • 您的网络存储压力更小,使用的带宽也更少 -花费更少的钱!

现在就可以了 让您的用户和老板都满意!

本文是网络开发技术的一部分 来自微软的系列。我们很高兴与您分享 Microsoft Edge 和新的 EdgeHTML 渲染引擎。免费 虚拟机或在 Mac、iOS、Android 或 Windows 设备上进行远程测试 @ http://dev.modern.ie/。

以上就是Babylon.js 迁移到 Azure 的原因和流程的详细内容,更多请关注双恒网络其它相关文章!

简简单单挺好的

联系我们 订单查询