Oltre il browser: Introduzione a WebAssembly serverless

Pubblicato: 2022-03-10
Riepilogo rapido ↬ Probabilmente hai sentito parlare di WebAssembly e perché è un potente strumento nel browser. In questo articolo, esploriamo perché WebAssembly serverless potrebbe essere ugualmente potente al di fuori del browser e come iniziare a usarlo.

Ora che WebAssembly è supportato da tutti i principali browser e da oltre l'85% degli utenti in tutto il mondo, JavaScript non è più l'unico linguaggio del browser in città. Se non hai sentito, WebAssembly è un nuovo linguaggio di basso livello che viene eseguito nel browser. È anche una destinazione di compilazione, il che significa che puoi compilare programmi esistenti scritti in linguaggi come C, C++ e Rust in WebAssembly ed eseguire tali programmi nel browser. Finora, WebAssembly è stato utilizzato per trasferire tutti i tipi di applicazioni sul Web, comprese applicazioni desktop, strumenti da riga di comando, giochi e strumenti di data science.

Nota: per un caso di studio approfondito su come WebAssembly può essere utilizzato all'interno del browser per velocizzare le applicazioni Web, consulta il mio articolo precedente.

WebAssembly fuori dal Web?

Sebbene la maggior parte delle applicazioni WebAssembly oggi siano incentrate sul browser, WebAssembly stesso non è stato originariamente progettato solo per il Web, ma in realtà per qualsiasi ambiente sandbox. In effetti, recentemente c'è stato molto interesse nell'esplorare come WebAssembly potrebbe essere utile al di fuori del browser, come approccio generale per l'esecuzione di binari su qualsiasi sistema operativo o architettura di computer, purché sia ​​presente un runtime WebAssembly che supporta quel sistema. In questo articolo, esamineremo come WebAssembly può essere eseguito al di fuori del browser, in modalità serverless/Function-as-a-Service (FaaS).

Altro dopo il salto! Continua a leggere sotto ↓

WebAssembly per applicazioni serverless

In poche parole, le funzioni serverless sono un modello di calcolo in cui si consegna il codice a un provider cloud e si lascia che eseguano e gestiscano il ridimensionamento del codice per te. Ad esempio, puoi chiedere che la tua funzione serverless venga eseguita ogni volta che chiami un endpoint API o che venga guidata da eventi, ad esempio quando un file viene caricato nel tuo bucket cloud. Sebbene il termine "serverless" possa sembrare un termine improprio poiché i server sono chiaramente coinvolti da qualche parte lungo il percorso, dal nostro punto di vista è serverless poiché non dobbiamo preoccuparci di come gestire, distribuire o ridimensionare quei server.

Sebbene queste funzioni siano generalmente scritte in linguaggi come Python e JavaScript (Node.js), ci sono una serie di motivi per cui potresti scegliere di utilizzare WebAssembly invece:

  1. Tempi di inizializzazione più rapidi
    I provider serverless che supportano WebAssembly (tra cui Cloudflare e Fastly segnalano che possono avviare funzioni almeno un ordine di grandezza più velocemente di quanto la maggior parte dei provider cloud possa fare con altri linguaggi. Raggiungono questo obiettivo eseguendo decine di migliaia di moduli WebAssembly nello stesso processo, il che è possibile perché la natura sandbox di WebAssembly offre un modo più efficiente per ottenere l'isolamento per cui vengono tradizionalmente utilizzati i contenitori.
  2. Non sono necessarie riscritture
    Uno dei principali vantaggi di WebAssembly nel browser è la possibilità di trasferire il codice esistente sul Web senza dover riscrivere tutto in JavaScript. Questo vantaggio è ancora valido nel caso d'uso serverless perché i provider cloud limitano i linguaggi in cui puoi scrivere le tue funzioni serverless. In genere, supporteranno Python, Node.js e forse pochi altri, ma certamente non C, C++ o Rust . Supportando WebAssembly, i provider serverless possono supportare indirettamente molte più lingue.
  3. Più leggero
    Quando eseguiamo WebAssembly nel browser, ci affidiamo al computer dell'utente finale per eseguire i nostri calcoli. Se questi calcoli sono troppo intensi, i nostri utenti non saranno contenti quando la ventola del loro computer inizierà a ronzare. L'esecuzione di WebAssembly al di fuori del browser ci offre i vantaggi di velocità e portabilità di WebAssembly, mantenendo al contempo leggera la nostra applicazione. Inoltre, poiché stiamo eseguendo il nostro codice WebAssembly in un ambiente più prevedibile, possiamo potenzialmente eseguire calcoli più intensivi.

Un esempio concreto

Nel mio precedente articolo qui su Smashing Magazine, abbiamo discusso di come abbiamo accelerato un'applicazione Web sostituendo i calcoli JavaScript lenti con il codice C compilato in WebAssembly. L'app web in questione era fastq.bio, uno strumento per visualizzare in anteprima la qualità dei dati di sequenziamento del DNA.

Come esempio concreto, riscriviamo fastq.bio come un'applicazione che utilizza WebAssembly serverless invece di eseguire WebAssembly all'interno del browser. Per questo articolo utilizzeremo Cloudflare Workers, un provider serverless che supporta WebAssembly ed è basato sul motore del browser V8. Un altro provider cloud, Fastly, sta lavorando a un'offerta simile, ma basata sul loro runtime Lucet.

Per prima cosa, scriviamo del codice Rust per analizzare la qualità dei dati dei dati di sequenziamento del DNA. Per comodità, possiamo sfruttare la libreria bioinformatica Rust-Bio per gestire l'analisi dei dati di input e la libreria wasm-bindgen per aiutarci a compilare il nostro codice Rust in WebAssembly.

Ecco uno snippet del codice che legge i dati di sequenziamento del DNA e genera un JSON con un riepilogo delle metriche di qualità:

 // Import packages extern crate wasm_bindgen; use bio::seq_analysis::gc; use bio::io::fastq; ... // This "wasm_bindgen" tag lets us denote the functions // we want to expose in our WebAssembly module #[wasm_bindgen] pub fn fastq_metrics(seq: String) -> String { ... // Loop through lines in the file let reader = fastq::Reader::new(seq.as_bytes()); for result in reader.records() { let record = result.unwrap(); let sequence = record.seq(); // Calculate simple statistics on each record n_reads += 1.0; let read_length = sequence.len(); let read_gc = gc::gc_content(sequence); // We want to draw histograms of these values // so we store their values for later plotting hist_gc.push(read_gc * 100.0); hist_len.push(read_length); ... } // Return statistics as a JSON blob json!({ "n": n_reads, "hist": { "gc": hist_gc, "len": hist_len }, ... }).to_string() }

Abbiamo quindi utilizzato lo strumento da riga di comando wrangler di Cloudflare per eseguire il lavoro pesante della compilazione in WebAssembly e della distribuzione nel cloud. Una volta terminato, ci viene fornito un endpoint API che accetta i dati di sequenziamento come input e restituisce un JSON con parametri di qualità dei dati. Ora possiamo integrare quell'API nella nostra applicazione.

Ecco una GIF dell'applicazione in azione:

GIF della nostra applicazione che effettua chiamate parallele a una funzione WebAssembly serverless e aggiorna i grafici con i dati che restituisce.
Invece di eseguire l'analisi direttamente nel browser, la versione serverless della nostra applicazione effettua diverse richieste POST in parallelo alla nostra funzione serverless (vedi barra laterale destra) e aggiorna i grafici ogni volta che restituisce più dati. (Grande anteprima)

Il codice completo è disponibile su GitHub (open-source).

Mettere tutto nel contesto

Per contestualizzare l'approccio serverless WebAssembly, consideriamo quattro modi principali in cui possiamo costruire applicazioni Web di elaborazione dati (ovvero app Web in cui eseguiamo analisi sui dati forniti dall'utente):

Questa figura mostra quattro modi in cui possiamo strutturare l'elaborazione dei dati in un'app Web: sul server (senza WebAssembly), nel browser utilizzando JavaScript, nel browser utilizzando WebAssembly e WebAssembly serverless.
Quattro diverse scelte architetturali che possiamo adottare per le app che elaborano i dati. (Grande anteprima)

Come sopra indicato, il trattamento dei dati può essere effettuato in più sedi:

  1. Lato server
    Questo è l'approccio adottato dalla maggior parte delle applicazioni Web, in cui le chiamate API effettuate nel front-end avviano l'elaborazione dei dati sul back-end.
  2. JavaScript lato client
    In questo approccio, il codice di elaborazione dei dati viene scritto in JavaScript e viene eseguito nel browser. Lo svantaggio è che le tue prestazioni subiranno un successo e se il tuo codice originale non era in JavaScript, dovrai riscriverlo da zero!
  3. WebAssembly lato client
    Ciò comporta la compilazione del codice di analisi dei dati in WebAssembly e l'esecuzione nel browser. Se il codice di analisi è stato scritto in linguaggi come C, C++ o Rust (come spesso accade nel mio campo di genomica), ciò ovvia alla necessità di riscrivere algoritmi complessi in JavaScript. Fornisce inoltre il potenziale per accelerare la nostra applicazione (ad esempio, come discusso in un articolo precedente).
  4. WebAssembly senza server
    Ciò comporta l'esecuzione del WebAssembly compilato sul cloud, utilizzando un tipo di modello FaaS (ad esempio questo articolo).

Allora perché dovresti scegliere l'approccio serverless rispetto agli altri? Per prima cosa, rispetto al primo approccio, presenta i vantaggi derivanti dall'utilizzo di WebAssembly, in particolare la possibilità di trasferire il codice esistente senza doverlo riscrivere in JavaScript. Rispetto al terzo approccio, WebAssembly serverless significa anche che la nostra app è più leggera poiché non utilizziamo le risorse dell'utente per il calcolo dei numeri. In particolare, se i calcoli sono abbastanza coinvolti o se i dati sono già nel cloud, questo approccio ha più senso.

D'altra parte, tuttavia, ora l'app deve stabilire connessioni di rete, quindi l'applicazione sarà probabilmente più lenta. Inoltre, a seconda della scala del calcolo e della possibilità di essere scomposto in parti di analisi più piccole, questo approccio potrebbe non essere adatto a causa delle limitazioni imposte dai provider di cloud serverless sull'utilizzo di runtime, CPU e RAM.

Conclusione

Come abbiamo visto, ora è possibile eseguire il codice WebAssembly in modalità serverless e sfruttare i vantaggi sia di WebAssembly (portabilità e velocità) sia di quelli delle architetture function-as-a-service (ridimensionamento automatico e prezzi per uso ). Alcuni tipi di applicazioni, come l'analisi dei dati e l'elaborazione delle immagini, solo per citarne alcuni, possono trarre grandi vantaggi da un tale approccio. Sebbene il runtime soffra a causa dei viaggi di andata e ritorno aggiuntivi alla rete, questo approccio ci consente di elaborare più dati alla volta e di non prosciugare le risorse degli utenti.