Além do navegador: Introdução ao WebAssembly sem servidor

Publicados: 2022-03-10
Resumo rápido ↬ Você provavelmente já ouviu falar do WebAssembly e porque é uma ferramenta poderosa no navegador. Neste artigo, exploramos por que o WebAssembly sem servidor pode ser igualmente poderoso fora do navegador e como começar a usá-lo.

Agora que o WebAssembly é suportado por todos os principais navegadores e por mais de 85% dos usuários em todo o mundo, JavaScript não é mais a única linguagem de navegador na cidade. Se você ainda não ouviu falar, WebAssembly é uma nova linguagem de baixo nível que roda no navegador. É também um destino de compilação, o que significa que você pode compilar programas existentes escritos em linguagens como C, C++ e Rust no WebAssembly e executar esses programas no navegador. Até agora, o WebAssembly tem sido usado para portar todos os tipos de aplicativos para a web, incluindo aplicativos de desktop, ferramentas de linha de comando, jogos e ferramentas de ciência de dados.

Observação: para um estudo de caso aprofundado de como o WebAssembly pode ser usado dentro do navegador para acelerar aplicativos da Web, confira meu artigo anterior.

WebAssembly fora da Web?

Embora a maioria dos aplicativos WebAssembly hoje sejam centrados no navegador, o WebAssembly em si não foi originalmente projetado apenas para a Web, mas realmente para qualquer ambiente de área restrita. Na verdade, recentemente tem havido muito interesse em explorar como o WebAssembly pode ser útil fora do navegador, como uma abordagem geral para executar binários em qualquer sistema operacional ou arquitetura de computador, desde que haja um tempo de execução do WebAssembly que suporte esse sistema. Neste artigo, veremos como o WebAssembly pode ser executado fora do navegador, de maneira serverless/Function-as-a-Service (FaaS).

Mais depois do salto! Continue lendo abaixo ↓

WebAssembly para aplicativos sem servidor

Em poucas palavras, as funções sem servidor são um modelo de computação em que você entrega seu código a um provedor de nuvem e permite que ele execute e gerencie o dimensionamento desse código para você. Por exemplo, você pode solicitar que sua função sem servidor seja executada sempre que chamar um endpoint de API ou que seja acionada por eventos, como quando um arquivo é carregado em seu bucket de nuvem. Embora o termo "sem servidor" possa parecer um equívoco, já que os servidores estão claramente envolvidos em algum lugar ao longo do caminho, é sem servidor do nosso ponto de vista, pois não precisamos nos preocupar em como gerenciar, implantar ou dimensionar esses servidores.

Embora essas funções geralmente sejam escritas em linguagens como Python e JavaScript (Node.js), há vários motivos pelos quais você pode optar por usar o WebAssembly:

  1. Tempos de inicialização mais rápidos
    Provedores sem servidor que suportam WebAssembly (incluindo Cloudflare e Fastly relatam que podem iniciar funções pelo menos uma ordem de magnitude mais rápido do que a maioria dos provedores de nuvem com outras linguagens. Eles conseguem isso executando dezenas de milhares de módulos WebAssembly no mesmo processo, que é possível porque a natureza de área restrita do WebAssembly torna uma maneira mais eficiente de obter o isolamento para o qual os contêineres são tradicionalmente usados.
  2. Não são necessárias reescritas
    Um dos principais recursos do WebAssembly no navegador é a capacidade de portar o código existente para a web sem ter que reescrever tudo para JavaScript. Esse benefício ainda é válido no caso de uso sem servidor porque os provedores de nuvem limitam em quais linguagens você pode escrever suas funções sem servidor. Normalmente, eles suportam Python, Node.js e talvez alguns outros, mas certamente não C, C++ ou Rust . Ao oferecer suporte ao WebAssembly, os provedores sem servidor podem oferecer suporte indireto a muito mais idiomas.
  3. Mais leve
    Ao executar o WebAssembly no navegador, contamos com o computador do usuário final para realizar nossos cálculos. Se esses cálculos forem muito intensos, nossos usuários não ficarão felizes quando o ventilador do computador começar a zumbir. A execução do WebAssembly fora do navegador nos oferece os benefícios de velocidade e portabilidade do WebAssembly, além de manter nosso aplicativo leve. Além disso, como estamos executando nosso código WebAssembly em um ambiente mais previsível, podemos potencialmente realizar cálculos mais intensivos.

Um exemplo concreto

Em meu artigo anterior aqui na Smashing Magazine, discutimos como aceleramos um aplicativo da Web substituindo cálculos lentos de JavaScript por código C compilado para WebAssembly. O aplicativo da web em questão era o fastq.bio, uma ferramenta para visualizar a qualidade dos dados de sequenciamento de DNA.

Como exemplo concreto, vamos reescrever o fastq.bio como um aplicativo que faz uso do WebAssembly sem servidor em vez de executar o WebAssembly dentro do navegador. Para este artigo, usaremos o Cloudflare Workers, um provedor sem servidor que oferece suporte ao WebAssembly e é construído sobre o mecanismo de navegador V8. Outro provedor de nuvem, Fastly, está trabalhando em uma oferta semelhante, mas com base em seu tempo de execução Lucet.

Primeiro, vamos escrever algum código Rust para analisar a qualidade dos dados de sequenciamento de DNA. Por conveniência, podemos aproveitar a biblioteca de bioinformática Rust-Bio para lidar com a análise dos dados de entrada e a biblioteca wasm-bindgen para nos ajudar a compilar nosso código Rust para o WebAssembly.

Aqui está um trecho do código que lê os dados de sequenciamento de DNA e gera um JSON com um resumo das métricas de qualidade:

 // 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() }

Em seguida, usamos a ferramenta de linha de comando wrangler da Cloudflare para fazer o trabalho pesado de compilar para WebAssembly e implantar na nuvem. Uma vez feito, recebemos um endpoint de API que recebe dados de sequenciamento como entrada e retorna um JSON com métricas de qualidade de dados. Agora podemos integrar essa API em nosso aplicativo.

Aqui está um GIF do aplicativo em ação:

GIF do nosso aplicativo fazendo chamadas paralelas para uma função WebAssembly sem servidor e atualizando gráficos com os dados que ele retorna.
Em vez de executar a análise diretamente no navegador, a versão serverless do nosso aplicativo faz várias solicitações POST em paralelo à nossa função serverless (veja a barra lateral direita), e atualiza os gráficos cada vez que retorna mais dados. (Visualização grande)

O código completo está disponível no GitHub (código aberto).

Colocando tudo em contexto

Para colocar a abordagem do WebAssembly sem servidor em contexto, vamos considerar quatro maneiras principais pelas quais podemos criar aplicativos da Web de processamento de dados (ou seja, aplicativos da Web em que realizamos análises nos dados fornecidos pelo usuário):

Esta figura mostra quatro maneiras de estruturar o processamento de dados em um aplicativo Web: no servidor (sem WebAssembly), no navegador usando JavaScript, no navegador usando WebAssembly e WebAssembly sem servidor.
Quatro opções de arquitetura diferentes que podemos adotar para aplicativos que processam dados. (Visualização grande)

Conforme mostrado acima, o processamento de dados pode ser feito em vários locais:

  1. Lado do servidor
    Essa é a abordagem adotada pela maioria dos aplicativos da web, onde as chamadas de API feitas no front-end iniciam o processamento de dados no back-end.
  2. JavaScript do lado do cliente
    Nesta abordagem, o código de processamento de dados é escrito em JavaScript e executado no navegador. A desvantagem é que seu desempenho será afetado e, se seu código original não estiver em JavaScript, você precisará reescrevê-lo do zero!
  3. WebAssembly do lado do cliente
    Isso envolve compilar o código de análise de dados para o WebAssembly e executá-lo no navegador. Se o código de análise foi escrito em linguagens como C, C++ ou Rust (como costuma ser o caso no meu campo de genômica), isso evita a necessidade de reescrever algoritmos complexos em JavaScript. Ele também fornece o potencial para acelerar nosso aplicativo (por exemplo, conforme discutido em um artigo anterior).
  4. WebAssembly sem servidor
    Isso envolve a execução do WebAssembly compilado na nuvem, usando um tipo de modelo FaaS (por exemplo, este artigo).

Então, por que você escolheria a abordagem sem servidor em detrimento das outras? Por um lado, em comparação com a primeira abordagem, ela tem os benefícios do uso do WebAssembly, especialmente a capacidade de portar código existente sem ter que reescrevê-lo em JavaScript. Comparado com a terceira abordagem, o WebAssembly sem servidor também significa que nosso aplicativo é mais leve, pois não usamos os recursos do usuário para processamento de números. Em particular, se os cálculos estiverem bastante envolvidos ou se os dados já estiverem na nuvem, essa abordagem faz mais sentido.

Por outro lado, no entanto, o aplicativo agora precisa fazer conexões de rede, portanto, o aplicativo provavelmente será mais lento. Além disso, dependendo da escala da computação e se ela pode ser dividida em partes de análise menores, essa abordagem pode não ser adequada devido às limitações impostas por provedores de nuvem sem servidor em tempo de execução, CPU e utilização de RAM.

Conclusão

Como vimos, agora é possível executar o código do WebAssembly sem servidor e colher os benefícios do WebAssembly (portabilidade e velocidade) e das arquiteturas de função como serviço (escalonamento automático e preços por uso ). Certos tipos de aplicativos — como análise de dados e processamento de imagens, para citar alguns — podem se beneficiar muito com essa abordagem. Embora o tempo de execução sofra por causa das viagens de ida e volta adicionais à rede, essa abordagem nos permite processar mais dados por vez e não esgotar os recursos dos usuários.