Oltre il browser: Introduzione a WebAssembly serverless
Pubblicato: 2022-03-10Ora 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).
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:
- 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. - 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. - 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:
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):
Come sopra indicato, il trattamento dei dati può essere effettuato in più sedi:
- 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. - 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! - 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). - 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.