WebAssembly, обычно называемая wasm, является одной из самых быстрорастущих платформ в Интернете за последние годы. Это низкоуровневый формат двоичных инструкций, изначально разработанный для запуска высокопроизводительных веб-приложений. Его главное преимущество заключается в том, что он не зависит от языка, поскольку он действует как цель компиляции для других языков более высокого уровня, таких как C, Rust, Go, Python и т. д. Его цель, безусловно, не в том, чтобы заменить JavaScript как язык программирования, лежащий в основе Интернета, а в том, чтобы обеспечить синергию с ним, позволив большему количеству языков работать в браузере. . Wasm по умолчанию эффективен, быстр и безопасен. Его использование может повысить производительность веб-приложений, особенно в сочетании с веб-воркерами, за счет выгрузки некоторых тяжелых вычислительных операций из основного потока.

Поскольку wasm не делает никаких предположений о среде, в которой он работает, мы можем запускать его даже вне браузера. В этой области было предпринято много усилий, чтобы предоставить среду выполнения WebAssembly, такую ​​как проект альянса байткодов wasmtime или популярный проект wasmer, который поддерживает более широкий различных языков и платформ, или даже Docker с его бета-интеграцией wasm , которая может стать альтернативой контейнерам для упаковки ваших приложений. Благодаря растущей поддержке и разработке WASI (интерфейс системы WebAssembly), который обеспечивает стандартный интерфейс между кодом WebAssembly и операционной системой или средой выполнения, в которой он выполняется, wasm может обращаться к файловой системе, выполнять сетевые запросы и взаимодействовать с другими системными ресурсами согласованным и переносимым образом. Потенциал экосистемы WebAssembly огромен, и мы только на пороге.

Как бы я ни любил говорить о wasm (возможно, позже я напишу о нем подробную статью), в облачном пространстве есть еще один чрезвычайно популярный и мощный инструмент, заслуживающий внимания: бессерверные функции. . Этот сдвиг парадигмы произвел революцию в облачных вычислениях, предоставив экономичное решение для запуска высокомасштабируемых рабочих процессов по запросу без выделенного сервера. Они позволяют выполнять бизнес-логику, не беспокоясь о базовой инфраструктуре и способах ее управления. Они представляют собой наиболее детальный способ разработки приложений, и на основе этой концепции могут быть построены целые архитектуры. Они также проявляют себя в сочетании с платформами, управляемыми данными, объединяя выполнение функций, подобно конечным автоматам.

Предпосылка проста. Как правило, понятие бессерверных функций состоит из двух основных компонентов: триггеров и действий. Триггеры — это указанные вами события, для которых ваша функция будет активироваться и выполняться. Они могут варьироваться от простого HTTP-запроса до изменений в таблицах вашей базы данных, и эти события могут различаться в зависимости от поставщика облачных услуг. Ваши действия — это фрагменты кода, которые будут выполняться при возникновении события. Как бы просто это ни звучало, потенциал этого подхода велик, особенно потому, что вы платите только за то, когда ваш код действительно выполняется. Таким образом, вы никогда не будете беспокоиться о простаивающих серверах, которые бесполезно тратят ресурсы.

Когда вы думаете о wasm в сочетании с бессерверными решениями, приходит шокирующее осознание. WebAssemblyможет позволить разработчикам писать код на языке по своему выбору, а затем компилировать его в wasmдвоичный файл, который можно запускать на сервере, не требуя каких-либо изменений в серверной среде. . Это может упростить развертывание кода в бессерверной среде, поскольку избавляет от необходимости беспокоиться о совместимости и зависимостях. Это также может дать некоторые преимущества в производительности за счет использования низкоуровневого аспекта wasm. Еще одним преимуществом является то, что вы можете запускать любой язык как бессерверную функцию независимо от того, поддерживается ли он платформой, учитывая, что он может быть скомпилирован в wasm, что может иметь решающее значение для компании, которая уже инвестировала в определенный языковой стек, который поставщики облачных услуг не намерены поддерживать или поддерживать.

Одной из наиболее многообещающих платформ для минимальных бессерверных функций является Cloudflare Workers. Он позволяет запускать код JavaScript и/или WebAssembly в пограничной сети Cloudflare. Это позволяет разработчикам создавать и развертывать приложения, работающие в непосредственной близости от конечных пользователей, что обеспечивает более высокую производительность и меньшую задержку. Рабочие процессы можно использовать для реализации пользовательской логики для обработки определенных типов запросов. Они основаны на спецификации сервис-воркеров, которая определяет сценарий, который запускается в фоновом режиме веб-страницы и может соответствующим образом перехватывать и изменять сетевые запросы.

Допустим, мы хотим запустить программу Go как бессерверную функцию. Мы хотим, чтобы он извлекал информацию из html-кода на основе пользовательского селектора. Мы можем добиться этого с помощью библиотеки goquery :

func extract(body string, selector string) []string  {
 doc, err := goquery.NewDocumentFromReader(body)
 if err != nil {
  panic(err)
 }

 var results []string
 doc.Find(selector).Each(func(i int, s *goquery.Selection) {
  html, err := s.Html()
  if err != nil {
   panic(err)
  }
  results = append(results, html)
 })

 return results
}

Однако мы не сможем взаимодействовать со средой выполнения workerd , которая поддерживает Cloudflare Workers, используя только этот код. Нам нужно будет импортировать "syscall/js" и подготовить нашу функцию для взаимодействия со средой выполнения:

func extract(this js.Value, args []js.Value) interface{}  {
 doc, err := goquery.NewDocumentFromReader(io.NopCloser(strings.NewReader(args[0].String())))
 if err != nil {
  panic(err)
 }

 var results []interface{}
 doc.Find(args[1].String()).Each(func(i int, s *goquery.Selection) {
  html, err := s.Html()
  if err != nil {
   panic(err)
  }
  results = append(results, html)
 })

 return js.ValueOf(results)
}

И последнее, что нам нужно сделать, это прикрепить нашу функцию Go к контексту, чтобы вызвать ее из нашего воркера. Окончательный файл должен выглядеть примерно так:

package main

import (
 "fmt"
 "io"
 "strings"
 "syscall/js"

 "github.com/PuerkitoBio/goquery"
)

func main() {
 fmt.Println("Attaching golang function to context...")
 js.Global().Set("exec_worker", js.FuncOf(extract))
 select {}
}

func extract(this js.Value, args []js.Value) interface{}  {
 doc, err := goquery.NewDocumentFromReader(io.NopCloser(strings.NewReader(args[0].String())))
 if err != nil {
  panic(err)
 }

 var results []interface{}
 doc.Find(args[1].String()).Each(func(i int, s *goquery.Selection) {
  html, err := s.Html()
  if err != nil {
   panic(err)
  }
  results = append(results, html)
 })

 return js.ValueOf(results)
}

Чтобы скомпилировать нашу программу Go в WebAssembly, мы можем использовать следующую команду:

GOOS=js GOARCH=wasm go build -o worker.wasm worker.go

Однако мне удалось добиться большего успеха с tinygo при развертывании в Cloudflare Workers. Вы можете настроить это так:

wget -O ./tinygo.deb https://github.com/tinygo-org/tinygo/releases/download/v0.26.0/tinygo_0.26.0_amd64.deb
sudo dpkg -i tinygo.deb
rm tinygo.deb
tinygo build -o worker.wasm -target=wasm worker.go

Не забудьте скачать последнюю версию deb-пакета с официального сайта

Теперь, когда у нас есть двоичный файл wasm, мы можем перейти к настройке функции Edge. Чтобы создать рабочий процесс Cloudflare, мы можем использовать интерфейс командной строки wrangler . Мы можем запустить это, чтобы инициализировать новый проект обработчика:

npx wrangler init demo_worker

Чтобы иметь возможность запускать Go в контексте WebAssembly , нам понадобится оболочка, обычно называемая связующим кодом, которая, к счастью, предоставлена ​​командой Golang. Для наших целей мы скопируем версию tinygo в наш проект:

cp $(tinygo env TINYGOROOT)/targets/wasm_exec.js .

Если вы обнаружите какие-либо проблемы с использованием оболочки по умолчанию, вы можете скопировать тот, который я использовал в демонстрационном проекте, который вдохновил эту статью. Вы также можете столкнуться с отсутствующим объектом производительности, который я исправил, включив этот скрипт полифилла производительности от Пола Айриша.

С учетом этого мы можем написать основную точку входа нашего воркера:

import "./polyfill_performance.js"
import "./wasm_exec.js"
import wasm from 'worker.wasm'

const go = new Go()
const load = WebAssembly
.instantiate(wasm, go.importObject)
.then((instance) => {
  go.run(instance)
  return instance
})
 
const processRequest = async (event) => {
  if(event.request.method === "POST") {
    await load
    try {
      const body = await event.request.json()
      const response = await fetch(body["url"])
      const html = await response.text()
      const results = await exec_worker(html, body["selector"])
      for(const result of results) {
        console.log(result)
      }
      return new Response("Function executed successfully!")
    } catch (error) {
      console.log(error)
      return new Response("Invalid parameters supplied in request body...")
    }
  }
  return new Response(`Method Not Supported: ${event.request.method}`)
}

addEventListener("fetch", event => event.respondWith(processRequest(event)))

Теперь вы можете запустить эту функцию локально, используя:

npx wrangler dev src/index.js

Вы также можете проверить свою функцию, выполнив команду curl:

curl -X POST localhost:8787 -d "{\"url\": \"https://example.com\", \"selector\": \"h1\"}"

Когда вы, наконец, закончите, вы можете опубликовать свою бессерверную функцию, используя:

npx wrangler publish

Поздравляем! Вы создали свою первую бессерверную функцию на основе WebAssembly! Чтобы увидеть полностью работающий проект, вы можете посетить следующий репозиторий, который я сделал: https://github.com/Ignema/goseek.

В целом, wasm и бессерверные функции — это две мощные технологии, которые можно использовать вместе для создания масштабируемых и эффективных приложений в облаке. Сочетая преимущества производительности и переносимости WebAssembly с масштабируемостью и экономичностью бессерверных функций, разработчики могут создавать высокопроизводительные приложения, способные справляться с широким спектром рабочих нагрузок. Независимо от того, создаете ли вы отдельное веб-приложение или интегрируете WebAssembly в существующую бессерверную архитектуру, эти технологии предлагают ряд возможностей для создания эффективных масштабируемых приложений в облаке.