Por qué y cómo hemos migrado Babylon.js a Azure

Estás trabajando para una startup. De repente, ese año difícil de codificación está dando sus frutos: con el éxito viene un mayor crecimiento y demanda de su aplicación web a escala.

En este tutorial, quiero usar humildemente una de nuestras "historias de éxito" más recientes en nuestro marco de juegos de código abierto de WebGL, Babylon.js, y su sitio web. Nos ha emocionado ver a tantos desarrolladores de juegos web probarlo. Pero para satisfacer la demanda, sabíamos que necesitábamos una nueva solución de alojamiento web. 

Si bien este tutorial se centra en Microsoft Azure, muchos de los conceptos se aplican a varias soluciones que quizás prefiera. También veremos las diversas optimizaciones que hemos implementado para limitar lo más posible el ancho de banda de salida de nuestros servidores a su navegador..

Introducción

Babylon.js es un proyecto personal en el que hemos estado trabajando durante más de un año. Como es un proyecto personal (es decir, nuestro tiempo y dinero), hemos alojado el sitio web, las texturas y las escenas en 3D en una solución de alojamiento relativamente barata utilizando una pequeña máquina dedicada de Windows / IIS. El proyecto comenzó en Francia, pero estuvo rápidamente en el radar de varios especialistas en 3D y web de todo el mundo, así como en algunos estudios de juegos. Estábamos contentos con los comentarios de la comunidad, pero el tráfico era manejable!

Por ejemplo, entre febrero de 2014 y abril de 2014, tuvimos un promedio de 7K + usuarios / mes con un promedio de 16K + páginas vistas / mes. Algunos de los eventos en los que hemos estado hablando han generado algunos picos interesantes:

Pero la experiencia en el sitio web todavía era lo suficientemente buena. Cargar nuestras escenas no se realizó a una velocidad estelar, pero los usuarios no se quejaban mucho..

Sin embargo, recientemente, un tipo genial decidió compartir nuestro trabajo en Hacker News. ¡Estábamos realmente felices por tales noticias! Pero mira lo que pasó con las conexiones del sitio:

Juego terminado para nuestro pequeño servidor! Poco a poco dejó de funcionar, y la experiencia para nuestros usuarios fue realmente mala. El servidor IIS pasaba su tiempo sirviendo grandes activos estáticos e imágenes, y el uso de la CPU era demasiado alto. Cuando estábamos a punto de lanzar el proyecto de la experiencia WebGL de Assassin's Creed Pirates que se ejecuta en Babylon.js, llegó el momento de cambiar a un alojamiento profesional más escalable mediante el uso de una solución en la nube.

Pero antes de revisar nuestras opciones de alojamiento, hablemos brevemente sobre los detalles de nuestro motor y sitio web:

  1. Todo es estático en nuestro sitio web. Actualmente no tenemos ningún código del lado del servidor en ejecución.
  2. Nuestros archivos de escenas (.Jabylon JSON) y texturas (.png o .jpeg) podrían ser muy grandes (hasta 100 MB). Esto significa que absolutamente necesitamos activar la compresión gzip en nuestros archivos de escena .babylon. De hecho, en nuestro caso, el precio se indexará mucho en el ancho de banda saliente.
  3. El dibujo en el lienzo de WebGL necesita controles de seguridad especiales. No puede cargar nuestras escenas y texturas desde otro servidor sin CORS habilitado, por ejemplo.

Créditos: Me gustaría agradecer especialmente. Benjamin Talmard, uno de nuestro evangelista técnico francés de Azure que nos ayudó a mudarnos a Azure.

1. Mudarse a los sitios web de Azure y al servicio de autoescala

Como nos gustaría pasar la mayor parte de nuestro tiempo escribiendo códigos y características para nuestro motor, no queremos perder tiempo en la plomería. Es por eso que inmediatamente decidimos elegir un enfoque de PaaS y no uno de IaaS.

Además, nos gustó la integración de Visual Studio con Azure. Puedo hacer casi todo desde mi IDE favorito. E incluso si Babylon.js está alojado en GitHub, estamos usando Visual Studio 2013, TypeScript y Visual Studio Online para codificar nuestro motor. Como nota para su proyecto, puede obtener la Comunidad de Visual Studio y una versión de prueba de Azure de forma gratuita.

Mudarme a Azure me tomó aproximadamente cinco minutos:

  1. Creé un nuevo sitio web en la página de administración: http://manage.windowsazure.com (también se podría hacer dentro de VS).  
  2. Tomé el conjunto de cambios correcto de nuestro repositorio de código fuente que coincide con la versión que estaba actualmente en línea.
  3. Hice clic derecho en el proyecto Web en el Explorador de soluciones de Visual Studio.

Ahora aquí viene la maravilla de las herramientas. Cuando inicié sesión en VS utilizando la cuenta de Microsoft vinculada a mi suscripción de Azure, el asistente me permitió simplemente elegir el sitio web en el que me gustaría implementar.

No hay necesidad de preocuparse por la autenticación compleja, la cadena de conexión o lo que sea.

"Siguiente, Siguiente, Siguiente y Publicar"Y un par de minutos más tarde, al final del proceso de carga de todos nuestros activos y archivos, el sitio web estaba en funcionamiento!

En el lado de la configuración, queríamos beneficiarnos del servicio de autoescala. Habría ayudado mucho en nuestro escenario anterior de Noticias de Hacker.

Primero, tu instancia ha sido configurada en Estándar modo en el Escala lengüeta.

Entonces, puedes elegir hasta ¿Cuántas instancias te gustaría escalar automáticamente?, en qué condiciones de la CPU, y también en qué horarios programados. 

En nuestro caso, hemos decidido utilizar hasta tres instancias pequeñas (1 núcleo, 1.75 GB de memoria) y generar automáticamente una nueva instancia si la CPU supera el 80% de su utilización. Eliminaremos una instancia si la CPU cae por debajo del 60%. El mecanismo de autoescalado siempre está activado en nuestro caso, no hemos establecido horarios programados específicos.

La idea es realmente Solo paga por lo que necesites Durante plazos y cargas específicas. Me encanta el concepto. Con eso, hubiéramos podido manejar los picos anteriores sin hacer nada gracias a este servicio de Azure!

También tiene una vista rápida del historial de autoescalado a través de la tabla púrpura. En nuestro caso, desde que nos mudamos a Azure, nunca hemos revisado una instancia hasta ahora. Y a continuación veremos cómo minimizar el riesgo de caer en una autoescala..

Para concluir sobre la configuración del sitio web, quisimos habilitar la compresión gzip automática en nuestros recursos específicos del motor 3D (.Babilonia.babylonmeshdata archivos). Esto fue fundamental para nosotros, ya que podría ahorrar hasta 3 veces el ancho de banda.Y así ... El precio..

Los sitios web se ejecutan en IIS. Para configurar IIS, necesita ir a la web.config expediente. Estamos utilizando la siguiente configuración en nuestro caso:

                                    

Esta solución funciona bastante bien, e incluso nos dimos cuenta de que el tiempo para cargar nuestras escenas se ha reducido en comparación con nuestro host anterior. Supongo que esto se debe a la mejor infraestructura y red que utilizan los centros de datos de Azure.

Sin embargo, he estado pensando en mudarme a Azure por un tiempo. Y mi primera idea no fue dejar que las instancias del sitio web sirvieran a mis grandes activos. Desde el principio, he estado más interesado en almacenar mis activos en el almacenamiento de blobs mejor diseñado para eso. También nos ofrecería un posible escenario CDN..

2. Mover activos a Azure Blob Storage, habilitar CORS, Gzip Support y CDN

La razón principal para usar el almacenamiento de blobs en nuestro caso es evitar cargar la CPU de las instancias de nuestro sitio web para atenderlas. Si todo se sirve a través del almacenamiento de blob, excepto algunos archivos HTML, JavaScript y CSS, las instancias de nuestro sitio web tendrán pocas oportunidades de autoescala.

Pero esto plantea dos problemas a resolver:

  1. Como el contenido se alojará en otro nombre de dominio, caeremos en el problema de seguridad entre dominios. Para evitar eso, necesitas habilitar CORS en el dominio remoto (Azure Blob Storage).
  2. Azur Blob Storage no soporta compresión gzip automática. Y no queremos reducir el uso del sitio web de la CPU si a cambio estamos pagando tres veces el precio debido al aumento del ancho de banda!

Habilitando CORS en Blob Storage

CORS en el almacenamiento de blob ha sido compatible desde hace unos meses. Este artículo, Almacenamiento de Windows Azure: Introducción a CORS, explica cómo utilizar las API de Azure para configurar CORS. Por mi parte, no quería escribir una aplicación pequeña para hacer eso. He encontrado uno en la web ya escrito: Cynapta Azure CORS Helper - Herramienta gratuita para administrar las reglas de CORS para el almacenamiento de blobs de Windows Azure.

Entonces simplemente habilité el soporte para GET y los encabezados adecuados en mi contenedor. Para comprobar si todo funciona como se esperaba, simplemente abra la barra de desarrollo F12 y verifique los registros de la consola:

Como puede ver, las líneas de registro verdes implican que todo está funcionando bien.

Aquí hay un caso de ejemplo donde fallará. Si intenta cargar nuestras escenas desde nuestro almacenamiento de blob directamente desde su máquina localhost (o cualquier otro dominio), obtendrá estos errores en los registros:

En conclusión, si ve que su dominio de llamada no se encuentra en elAcceso-Control-Permitir-Origen”Encabezado con un“Acceso denegado"Justo después de eso, es porque no has establecido tus reglas CORS correctamente. Es muy importante controlar tus reglas CORS; de lo contrario, cualquier persona podría usar sus activos y, por lo tanto, su ancho de banda, costándole dinero sin avisarle.! 

Habilitando el soporte Gzip en nuestro almacenamiento de blobs

Como te decía antes, Azure Blob Storage no admite la compresión gzip automática. También parece ser el caso de las soluciones de la competencia como S3. Tienes dos opciones para solucionar eso:

  1. Gzip los archivos tú mismo en el cliente antes de subir, cárguelo en el almacenamiento de blob utilizando sus herramientas clásicas y configure el codificación de contenido encabezado a gzip. Esta solución funciona, pero solo para los navegadores que admiten gzip (¿hay un navegador que todavía no sea compatible con gzip?). 
  2. Gzip los archivos en el cliente y subir dos versiones en el almacenamiento de blob: uno con el predeterminado.extensión y uno con el .extension.gzip, por ejemplo. Configure un controlador en el lado de IIS que detectará la solicitud HTTP del cliente, verifique el encabezado aceptar-codificar ajustado a gzip y sirva los archivos apropiados basados ​​en este soporte. Encontrará más detalles sobre el código para implementar en este artículo: Servicio de contenido comprimido de GZip desde el CDN de Azure.

En nuestro caso, no conozco ningún navegador que admita WebGL y no compresión gzip. Entonces, si el navegador no es compatible con gzip, no hay ningún interés real en ir más lejos, ya que esto probablemente significa que WebGL tampoco es compatible.

Por eso he elegido la primera solución. Como no tenemos muchas escenas y no estamos produciendo una nueva cada día, actualmente estoy usando este proceso manual:

  1. Usando 7-zip, estoy comprimiendo el.Babilonia archivos en mi máquina usando codificación gzip y "nivel de compresión" a "lo más rápido". Los otros niveles de compresión parecen generar problemas en mis pruebas..
  2. Subo el archivo usando CloudBerry Explorer para Microsoft Azure Cloud Storage.
  3. Establecí manualmente el encabezado HTTP codificación de contenido a gzip con CloudBerry.

Sé lo que estás pensando. ¿Voy a hacer eso para todos mis archivos? No, podrías trabajar en la construcción de una herramienta o un script posterior a la compilación que automatice eso. Por ejemplo, aquí hay una pequeña herramienta de línea de comandos que he construido:

string accountName = "yoda"; string containerName = "wwwbabylonjs"; string accountKey = "yourmagickey"; string sceneTextContent; // El primer argumento debe ser el directorio en el directorio de cadena de destino de Azure Blob Container = args [0]; intente StorageCredentials creds = new StorageCredentials (accountName, accountKey); Cuenta de CloudStorageAccount = new CloudStorageAccount (creds, useHttps: true); Cliente de CloudBlobClient = account.CreateCloudBlobClient (); CloudBlobContainer blobContainer = client.GetContainerReference (containerName); blobContainer.CreateIfNotExists (); var sceneDirectory = blobContainer.GetDirectoryReference (directory); string [] filesArgs = args.Skip (1) .ToArray (); foreach (string filespec en filesArgs) string specdir = Path.GetDirectoryName (filespec); string specpart = Path.GetFileName (filespec); if (specdir.Length == 0) specdir = Environment.CurrentDirectory;  foreach (archivo de cadena en Directory.GetFiles (specdir, specpart)) string path = Path.Combine (specdir, file); string sceneName = Path.GetFileName (ruta); Console.WriteLine ("Trabajando en" + sceneName + "…"); CloudBlockBlob blob = sceneDirectory.GetBlockBlobReference (sceneName); blob.Properties.ContentEncoding = "gzip"; blob.Properties.ContentType = "application / babylon"; sceneTextContent = System.IO.File.ReadAllText (ruta); 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 ("Subiendo en" + accountName + "/" + containerName + "/" + directory + "done.");  catch (Exception ex) Console.WriteLine (ex); 

Para usarlo, podría hacer lo siguiente:

UploadAndGzipFilesToAzureBlobStorage Scenes / Espilit C: \ Boulot \ Babylon \ Scenes \ Espilit \*.Babilonia* para empujar una escena que contiene múltiples archivos (Nuestras escenas incrementales con varios .babylonmeshdata archivos).

O simplemente:

UploadAndGzipFilesToAzureBlobStorage Scenes / Espilit C: \ Boulot \ Babylon \ Scenes \ Espilit \Espilit.babylon para empujar un archivo único.

Para comprobar que gzip funcionó como se esperaba con esta solución, estoy usando Fiddler. Cargue su contenido desde la máquina cliente y verifique los rastreos de la red si el contenido devuelto está realmente comprimido y puede descomprimirse:

Habilitando CDN

Una vez que haya realizado los dos pasos anteriores, solo debe hacer clic en un solo botón en la página de administración de Azure para habilitar CDN y asignarlo a su almacenamiento de blobs:

¡Es así de simple! En mi caso, necesito simplemente cambiar la siguiente URL: http://yoda.blob.core.windows.net/wwwbabylonjs/Scenes a http://az612410.vo.msecnd.net/wwwbabylonjs/Scenes. Tenga en cuenta que puede personalizar este dominio CDN a su propio si desea.

Gracias a eso, podemos servirle nuestros activos 3D de una manera muy rápida, ya que lo atenderemos desde una de las ubicaciones de nodos aquí enumeradas: Ubicaciones de nodos de la Red de entrega de contenido de Azure (CDN).

Nuestro sitio web se encuentra actualmente alojado en el centro de datos de Azure del norte de Europa. Pero si vienes de Seattle, harás ping en este servidor solo para descargar nuestros archivos básicos index.html, index.js, index.css y un par de capturas de pantalla. Todos los activos 3D se entregarán desde el nodo de Seattle cerca de usted.!

Nota: Todas nuestras demostraciones están usando la experiencia totalmente optimizada (almacenamiento de blob usando gzip, CDN y almacenamiento en caché de base de datos).

3. Uso de HTML5 IndexedDB para evitar volver a descargar los activos

Optimizar los tiempos de carga y controlar los costos de ancho de banda de salida no se trata solo del lado del servidor. También puedes construir algo de lógica del lado del cliente para optimizar las cosas. Afortunadamente, lo hemos hecho desde la versión 1.4 de nuestro motor Babylon.js. He explicado con gran detalle cómo he implementado el soporte para IndexedDB en este artículo: Uso de IndexedDB para manejar sus activos 3D WebGL: compartiendo comentarios y sugerencias de Babylon.JS. Y encontrará cómo activarlo en Babylon.js en nuestra wiki: almacenar en caché los recursos en IndexedDB.

Básicamente, solo tienes que crear un .babylon.manifest archivo que coincide con el nombre de la .Babilonia y, a continuación, establezca lo que desea almacenar en caché (texturas y / o escena JSON). Eso es.

Por ejemplo, verifique qué sucede con la escena de demostración de Hill Valley. La primera vez que lo cargue, aquí están las solicitudes enviadas:

153 articulos y 43.33 MB recibidos. Pero si has aceptado dejar babylonjs.com "Usa almacenamiento adicional en tu computadora", Esto es lo que verás la segunda vez que cargarás la misma escena:

1 elemento y 348 bytes! Solo estamos comprobando si el archivo de manifiesto ha cambiado. Si no, estamos cargando todo desde el DB y estamos ahorrando más de 43 MB de ancho de banda.

Por ejemplo, este enfoque se está utilizando en los juegos de Assassin's Creed Pirates:

Pensemos en eso:

  • El juego lanza casi inmediatamente después de que se haya cargado una vez, ya que los activos se sirven directamente desde la base de datos local.
  • Tu almacenamiento web está menos estresado y se utiliza menos ancho de banda-le cuesta menos dinero!

Ahora eso satisfará tanto a tus usuarios como a tu jefe.!

Este artículo es parte de la serie web dev tech de Microsoft. Estamos muy contentos de compartir Microsoft Edge y lo nuevo Motor de renderizado EdgeHTML con usted. Obtenga máquinas virtuales gratuitas o realice pruebas a distancia en su dispositivo Mac, iOS, Android o Windows @ http://dev.modern.ie/.