Perché e come abbiamo migrato Babylon.js in Azure

Stai lavorando per una startup. Improvvisamente, l'anno difficile della codifica sta dando i suoi frutti, con il successo che aumenta la crescita e la domanda per la tua app web.

In questo tutorial, voglio usare umilmente una delle nostre più recenti "storie di successo" sul nostro framework di giochi open source WebGL, Babylon.js, e il suo sito web. Siamo stati entusiasti di vedere così tanti sviluppatori di giochi online provarlo. Ma per stare al passo con la domanda, sapevamo di aver bisogno di una nuova soluzione di web hosting. 

Mentre questo tutorial si concentra su Microsoft Azure, molti dei concetti si applicano a varie soluzioni che potreste preferire. Vedremo anche le varie ottimizzazioni che abbiamo implementato per limitare il più possibile l'ampiezza di banda in uscita dai nostri server al browser.

introduzione

Babylon.js è un progetto personale su cui lavoriamo da oltre un anno. Poiché si tratta di un progetto personale (vale a dire il nostro tempo e denaro), abbiamo ospitato il sito Web, le trame e le scene 3D su una soluzione di hosting relativamente economica utilizzando una piccola macchina Windows / IIS dedicata. Il progetto è iniziato in Francia, ma è stato rapidamente sul radar di diversi specialisti 3D e web in tutto il mondo, oltre che in alcuni studi di gioco. Siamo stati felici del feedback della community, ma il traffico era gestibile!

Ad esempio, tra febbraio 2014 e aprile 2014, abbiamo avuto una media di 7K + utenti / mese con una media di 16K + pagine visualizzate / mese. Alcuni degli eventi in cui abbiamo parlato hanno generato alcuni picchi interessanti:

Ma l'esperienza sul sito web era ancora abbastanza buona. Caricare le nostre scene non è stato fatto a velocità stellare, ma gli utenti non si sono lamentati molto.

Tuttavia, recentemente, un bravo ragazzo ha deciso di condividere il nostro lavoro su Hacker News. Siamo stati davvero felici per queste notizie! Ma guarda cosa è successo alle connessioni del sito:

Gioco finito per il nostro piccolo server! Ha lentamente smesso di funzionare e l'esperienza per i nostri utenti è stata davvero pessima. Il server IIS passava il suo tempo a servire grandi risorse e immagini statiche e l'utilizzo della CPU era troppo alto. Mentre stavamo per lanciare il progetto di esperienza di Assassin's Creed Pirates WebGL su Babylon.js, era giunto il momento di passare a un hosting professionale più scalabile usando una soluzione cloud.

Ma prima di esaminare le nostre scelte di hosting, parliamo brevemente delle specifiche del nostro motore e del nostro sito web:

  1. Tutto è statico sul nostro sito web. Al momento non abbiamo alcun codice lato server in esecuzione.
  2. Le nostre scene (file .babylon JSON) e textures (.png o .jpeg) potrebbero essere molto grandi (fino a 100 MB). Ciò significa che abbiamo assolutamente bisogno di attivare la compressione gzip sui nostri file di scena .babylon. Infatti, nel nostro caso, il prezzo verrà indicizzato molto sulla larghezza di banda in uscita.
  3. Il disegno nel canvas WebGL richiede controlli di sicurezza speciali. Ad esempio, non è possibile caricare le scene e le trame da un altro server senza CORS abilitato.

Crediti: vorrei ringraziare in modo particolare Benjamin Talmard, uno dei nostri Azure Technical Evangelist francesi che ci ha aiutato a trasferirci in Azure.

1. Passare ai siti Web di Azure e al servizio di scalabilità automatica

Dato che vorremmo trascorrere la maggior parte del nostro tempo scrivendo codice e funzionalità per il nostro motore, non vogliamo perdere tempo nell'impianto idraulico. Ecco perché abbiamo deciso subito di scegliere un approccio PaaS e non uno IaaS.

Inoltre, ci piaceva l'integrazione di Visual Studio con Azure. Posso fare quasi tutto dal mio IDE preferito. E anche se Babylon.js è ospitato su GitHub, utilizziamo Visual Studio 2013, TypeScript e Visual Studio Online per codificare il nostro motore. Come nota per il tuo progetto, puoi ottenere gratuitamente Visual Studio Community e una Prova di Azure.

Passare ad Azure mi ha richiesto circa cinque minuti:

  1. Ho creato un nuovo sito Web nella pagina di amministrazione: http://manage.windowsazure.com (potrebbe essere fatto anche all'interno di VS).  
  2. Ho preso il changeset giusto dal nostro repository del codice sorgente corrispondente alla versione attualmente online.
  3. Ho fatto clic con il pulsante destro del mouse sul progetto Web in Visual Studio Solution Explorer.

Ora qui arriva la bellezza della lavorazione. Poiché ero collegato a VS utilizzando l'account Microsoft associato alla mia sottoscrizione di Azure, la procedura guidata mi consentiva semplicemente di scegliere il sito Web su cui mi piacerebbe distribuire.

Non c'è bisogno di preoccuparsi dell'autenticazione complessa, della stringa di connessione o di qualsiasi altra cosa.

Successivo, Avanti, Avanti e Pubblica"E un paio di minuti più tardi, al termine del processo di caricamento di tutti i nostri asset e file, il sito web era attivo e funzionante!

Dal lato della configurazione, volevamo beneficiare del servizio di autoscale. Avrebbe aiutato molto nel nostro precedente scenario di Hacker News.

Innanzitutto, la tua istanza è stata configurata in Standard modalità nel Scala linguetta.

Quindi, puoi scegliere fino a quante istanze ti piacerebbe ridimensionare automaticamente, in quali condizioni della CPU e anche in quali orari pianificati. 

Nel nostro caso, abbiamo deciso di utilizzare fino a tre piccole istanze (1 core, 1.75 GB di memoria) e di generare automaticamente una nuova istanza se la CPU supera l'80% del suo utilizzo. Rimuoveremo un'istanza se la CPU scende sotto il 60%. Il meccanismo di autoscaling è sempre attivo nel nostro caso, non abbiamo impostato orari pianificati specifici.

L'idea è davvero paga solo per quello di cui hai bisogno durante periodi di tempo e carichi specifici. Adoro il concetto. Con ciò, saremmo stati in grado di gestire picchi precedenti senza fare niente grazie a questo servizio di Azure!

Hai anche una rapida visione della cronologia della scalabilità automatica tramite il grafico viola. Nel nostro caso, da quando ci siamo spostati in Azure, non abbiamo mai esaminato un'istanza fino ad ora. E vedremo di seguito come ridurre al minimo il rischio di cadere in una scalabilità automatica.

Per concludere sulla configurazione del sito web, volevamo abilita la compressione gzip automatica sulle nostre specifiche risorse del motore 3D (.Babilonia.babylonmeshdata File). Questo è stato fondamentale per noi in quanto potrebbe risparmiare fino a 3 volte la larghezza di bandae quindi ... il prezzo.

I siti Web sono in esecuzione su IIS. Per configurare IIS, devi andare nel web.config file. Stiamo usando la seguente configurazione nel nostro caso:

                                    

Questa soluzione funziona abbastanza bene e abbiamo persino notato che il tempo di caricare le nostre scene è stato ridotto rispetto al nostro precedente host. Sto indovinando questo è grazie alla migliore infrastruttura e rete utilizzata dai data center di Azure.

Tuttavia, ho pensato di passare ad Azure per un po 'di tempo. E la mia prima idea era di non lasciare che le istanze del sito web servissero alle mie grandi risorse. Sin dall'inizio, sono stato più interessato a conservare le mie risorse nello storage blob meglio progettato per questo. Ci offrirebbe anche un possibile scenario CDN.

2. Spostamento delle risorse nell'archivio BLOB di Azure, abilitazione di CORS, supporto Gzip e CDN

Il motivo principale per cui utilizziamo lo storage blob nel nostro caso è evitare di caricare la CPU delle istanze del nostro sito Web per servirli. Se tutto viene servito tramite lo storage blob tranne alcuni file HTML, JavaScript e CSS, le nostre istanze del sito Web avranno poche possibilità di eseguire la scalabilità automatica.

Ma questo solleva due problemi da risolvere:

  1. Poiché il contenuto sarà ospitato su un altro nome di dominio, cadrà nel problema di sicurezza tra domini. Per evitare ciò, è necessario abilitare CORS sul dominio remoto (Archiviazione BLOB di Azure).
  2. Azzurro Blob Storage non supporta la compressione gzip automatica. E non vogliamo ridurre l'utilizzo del sito Web della CPU se in cambio paghiamo il triplo del prezzo a causa della maggiore larghezza di banda!

Abilitazione di CORS su Archiviazione BLOB

CORS sullo storage blob è stato supportato per alcuni mesi. Questo articolo, Archiviazione Windows Azure: Introduzione a CORS, spiega come utilizzare le API di Azure per configurare CORS. Da parte mia, non volevo scrivere una piccola app per farlo. Ne ho trovato uno sul web già scritto: Cynapta Azure CORS Helper - Strumento gratuito per gestire le regole CORS per lo storage BLOB di Azure di Windows.

Ho quindi abilitato il supporto per GET e le intestazioni corrette sul mio contenitore. Per verificare se tutto funziona come previsto, apri semplicemente la barra di sviluppo F12 e controlla i registri della console:

Come puoi vedere, le linee di log verdi implicano che tutto funzioni bene.

Ecco un esempio di caso in cui fallirà. Se provate a caricare le nostre scene dalla nostra memoria BLOB direttamente dal vostro computer localhost (o da qualsiasi altro dominio), otterrete questi errori nei registri:

In conclusione, se vedi che il tuo dominio chiamante non si trova nella "Access-Control-Allow-Origin"Intestazione con un"L'accesso è negato"Subito dopo, è perché non hai impostato correttamente le tue regole CORS. È molto importante controllare le tue regole CORS; altrimenti, chiunque potrebbe utilizzare i tuoi beni, e quindi la tua larghezza di banda, costare soldi senza farti sapere! 

Abilitazione del supporto Gzip sul nostro storage Blob

Come ti stavo dicendo prima, Azure Blob Storage non supporta la compressione gzip automatica. Sembra anche il caso delle soluzioni della concorrenza come S3. Hai due opzioni per ovviare a questo:

  1. Gzip i file te stesso sul client prima del caricamento, caricarlo nell'archiviazione BLOB usando i tuoi strumenti classici e impostalo content-encoding intestazione a gzip. Questa soluzione funziona, ma solo per i browser che supportano gzip (esiste comunque un browser che non supporta gzip?). 
  2. Gzip i file sul client e carica due versioni nella memoria BLOB: uno con il valore predefinito.estensione e uno con il .extension.gzip, per esempio. Impostare un gestore sul lato IIS che catturerà la richiesta HTTP dal client, verificare l'intestazione Accept-Encoding impostato gzip e serve i file appropriati basati su questo supporto. Troverai maggiori dettagli sul codice da implementare in questo articolo: Pubblicazione del contenuto compresso GZip dalla rete CDN di Azure.

Nel nostro caso, non conosco alcun browser che supporti WebGL e non compressione gzip. Quindi se il browser non supporta gzip, non c'è alcun reale interesse ad andare oltre, poiché questo probabilmente significa che WebGL non è supportato.

Ho quindi scelto la prima soluzione. Poiché non abbiamo molte scene e non ne produciamo una nuova ogni giorno, attualmente sto utilizzando questa procedura manuale:

  1. Usando 7-zip, sto comprimendo il.Babilonia file sulla mia macchina usando la codifica gzip e "livello di compressione" a "più veloce”. Gli altri livelli di compressione sembrano generare problemi nei miei test.
  2. Carico il file utilizzando CloudBerry Explorer per Microsoft Azure Cloud Storage.
  3. Ho impostato manualmente l'intestazione HTTP content-encoding a gzip con CloudBerry.

So cosa stai pensando. Lo farò per tutti i miei file?!? No, potresti lavorare sulla costruzione di uno strumento o di uno script post-build che lo automatizzi. Ad esempio, ecco un piccolo strumento da riga di comando che ho creato:

string accountName = "yoda"; string containerName = "wwwbabylonjs"; string accountKey = "yourmagickey"; stringa sceneTextContent; // Il primo argomento deve essere la directory nella directory stringa di destinazione del contenitore BLOB di Azure = args [0]; prova StorageCredentials creds = new StorageCredentials (accountName, accountKey); Account CloudStorageAccount = nuovo CloudStorageAccount (creds, useHttps: true); Client CloudBlobClient = 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 (file di stringhe in Directory.GetFiles (specdir, specpart)) string path = Path.Combine (specdir, file); string sceneName = Path.GetFileName (percorso); Console.WriteLine ("Working on" + sceneName + "..."); CloudBlockBlob blob = sceneDirectory.GetBlockBlobReference (nome scena); blob.Properties.ContentEncoding = "gzip"; blob.Properties.ContentType = "application / babylon"; sceneTextContent = System.IO.File.ReadAllText (path); var bytes = Encoding.UTF8.GetBytes (sceneTextContent); utilizzando (MemoryStream ms = new MemoryStream ()) utilizzando (GZipStream gzip = new GZipStream (ms, CompressionMode.Compress, true)) gzip.Write (byte, 0, byte.Lunghezza);  ms.Position = 0; Console.WriteLine ("Gzip done."); blob.UploadFromStream (ms); Console.WriteLine ("Caricamento in" + accountName + "/" + containerName + "/" + directory + "done.");  catch (Exception ex) Console.WriteLine (ex); 

Per usarlo, potrei fare quanto segue:

UploadAndGzipFilesToAzureBlobStorage Scene / Espilit C: \ Boulot \ Babylon \ Scene \ Espilit \*.Babilonia* per spingere una scena contenente più file (le nostre scene incrementali con mulipli .babylonmeshdata File).

O semplicemente:

UploadAndGzipFilesToAzureBlobStorage Scene / Espilit C: \ Boulot \ Babylon \ Scene \ Espilit \Espilit.babylon spingere a file unico.

Per verificare che gzip stia funzionando come previsto usando questa soluzione, sto usando Fiddler. Carica il contenuto dal computer client e controlla le tracce di rete se il contenuto restituito è realmente compresso e può essere decompresso:

Abilitazione di CDN

Una volta completati i due passaggi precedenti, è sufficiente fare clic su un singolo pulsante nella pagina di amministrazione di Azure per abilitare CDN e associarlo all'archiviazione BLOB:

È così semplice! Nel mio caso, ho bisogno di cambiare semplicemente il seguente URL: http://yoda.blob.core.windows.net/wwwbabylonjs/Scenes per http://az612410.vo.msecnd.net/wwwbabylonjs/Scenes. Tieni presente che puoi personalizzare questo dominio CDN a tuo piacimento se lo desideri.

Grazie a ciò, siamo in grado di offrirti le risorse 3D in modo molto veloce, poiché ti verrà servito da una delle posizioni dei nodi elencate qui: Posizioni dei nodi Azure Content Delivery Network (CDN).

Il nostro sito Web è attualmente ospitato nel datacenter Azure del Nord Europa. Ma se vieni da Seattle, esegui il ping di questo server solo per scaricare i nostri file index.html, index.js, index.css di base e un paio di schermate. Tutte le risorse 3D saranno servite dal nodo di Seattle proprio vicino a te!

Nota: tutti i nostri demo utilizzano l'esperienza completamente ottimizzata (archiviazione blob tramite gzip, CDN e DB caching).

3. Utilizzare HTML5 IndexedDB per evitare di scaricare nuovamente le risorse

Ottimizzare i tempi di caricamento e controllare i costi della larghezza di banda in uscita non è solo sul lato server. È anche possibile creare un lato logico del client per ottimizzare le cose. Fortunatamente, lo abbiamo fatto dalla v1.4 del nostro motore Babylon.js. Ho spiegato in dettaglio come ho implementato il supporto per IndexedDB in questo articolo: Utilizzo di IndexedDB per gestire le risorse WebGL 3D: condivisione di feedback e suggerimenti di Babylon.JS. E scoprirai come attivarlo in Babylon.js sul nostro wiki: Memorizzare le risorse nella cache in IndexedDB.

Fondamentalmente, devi solo creare un .babylon.manifest file corrispondente al nome del .Babilonia scena, quindi imposta ciò che desideri memorizzare nella cache (texture e / o scena JSON). Questo è tutto.

Per esempio, controlla cosa sta succedendo con la scena demo di Hill Valley. La prima volta che lo carichi, ecco le richieste inviate:

153 articoli e 43.33 MB ricevuti. Ma se hai accettato di lasciare babylonjs.com "usa memoria aggiuntiva sul tuo computer", Ecco cosa vedrai la seconda volta che caricerai la stessa scena:

1 articolo e 348 byte! Stiamo solo controllando se il file manifest è cambiato. In caso contrario, stiamo caricando tutto dal DB e stiamo risparmiando 43+ MB di larghezza di banda.

Ad esempio, questo approccio viene utilizzato nei giochi di Assassin's Creed Pirates:

Pensiamo a questo:

  • Il gioco lancia quasi immediatamente dopo che è stato caricato una volta, poiché le risorse sono servite direttamente dal DB locale.
  • La tua memoria web è meno stressata e viene utilizzata meno larghezza di banda-ti costa meno soldi!

Ora soddisferà sia i tuoi utenti che il tuo capo!

Questo articolo fa parte della serie di web dev tech di Microsoft. Siamo entusiasti di condividere Microsoft Edge e il nuovo Motore di rendering EdgeHTML con te. Ottieni macchine virtuali gratuite o test in remoto sul tuo dispositivo Mac, iOS, Android o Windows @ http://dev.modern.ie/.