Fastly Compute HTTP リクエストの作成 (1) Static/Dynamic バックエンド

2023/12/14に公開

この記事は Fastly Compute (旧 Compute@Edge) 一人アドベントカレンダー 13 日目の記事です。

Fastly Compute で最も多く利用することになるであろう HTTP リクエストの作成について紹介するシリーズ(Fastly Compute HTTP リクエストの作成)の第一回目です。Fastly Compute ではバックエンドと通信を起こす際に 2 種類の方法があり(Static Backend と Dynamic Backend)、それぞれの方式でできること実装方法や活用できるポイントが異なってきます。本稿ではこの 2 種類の方法について簡単にまとめてみたいと思います。

Static Backend

早速コード例で概要を掴んでいきます。次の例は Static Backend と呼ばれるベーシックな例で、予め定義されたバックエンドの名称を利用して通信する方法です。下記の例ではorigin_0と名付けられたバックエンド(Host)を利用しています。

# Rust
let req = Request::get("https://example.com/some/path");
let mut response = req.send("origin_0")?;

# Go
req, _ := fsthttp.NewRequest(fsthttp.MethodGet, "https://example.com/some/path", nil)
resp, err := req.Send(ctx, "origin_0")

# JavaScript
fetch("https://example.com/some/path", { backend: "origin_0" });

URL を別途指定しているのにこの origin_0 というパラメータについても事前に登録した上でコード上でも指定しないといけないのは若干冗長な気もすると思いますが、これはバックエンドサーバの Health Check 機能を活用する際に必要になってきます。(※Health Check については今後記事に追記か、または別途記事で説明します)

Dynamic Backend

もう一つの方法として、以下のように origin_0 バックエンドの事前登録を不要とする接続方法も提供されています。これは Dynamic Backend と呼ばれるもので、今年から提供が開始されています。以下のような実装イメージとなります。

# Rust
#[fastly::main]
fn main(req: Request) -> Result<Response, Error> {
  Backend::builder("my_backend", "www.fastly.com")
    .enable_ssl()
    .finish()?;
  Ok(req.send("my_backend")?)
}

# Go
func main() {
  opts := fsthttp.NewBackendOptions()
  opts.HostOverride("www.fastly.com")
  opts.UseSSL(true)
  fsthttp.RegisterDynamicBackend("my_backend", "www.fastly.com", opts)
  fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {    
    resp, _ := r.Send(ctx, "my_backend")
    w.Header().Reset(resp.Header)
    w.WriteHeader(resp.StatusCode)
    io.Copy(w, resp.Body)
  })
}

# JavaScript
import { allowDynamicBackends } from "fastly:experimental";
allowDynamicBackends(true);
async function app() {
  return fetch('https://www.fastly.com/');
}
addEventListener("fetch", event => event.respondWith(app(event)));

JavaScript のコードはかなり見通しが良くなりますね。Rust も比較的短く書くこともできそうで使い勝手が良さそうです。

活用例: 接続毎に動的にタイムアウト時間を変える

Dynamic Backend を使うと connect_timeout, first_byte_timeoutbetween_bytes_timeout といった設定を動的に変化させながら使うことができます。接続先や path によって許容できるタイムアウト時間が異なったりする場合には Dynamic Backend を使うと便利に接続管理ができますし、Compute の利用時間を最適化することにも役立つためうまく使えばコスト削減にも効いてきます。

以下は Rust の場合の実装例ですが、同様の内容は Go や JavaScript でも実装可能です。

let backend = Backend::builder("my_backend", "www.fastly.com")
  .override_host("www.fastly.com")
  .connect_timeout(Duration::from_secs(20))
  .first_byte_timeout(Duration::from_secs(20))
  .between_bytes_timeout(Duration::from_secs(20))
  .enable_ssl()
  .finish()?;
Ok(req.send(backend)?)

外部パッケージから外部に接続するケースについて(例:API クライアント)

この Dynamic Backend ですが、JavaScript の場合には fetch() に依存する外部パッケージを利用する場合にも有効化する必要が出てきそうです。

一方で Go の場合には SDK の方で http.RoundTripper インターフェースの実装が提供されているので、http.Client を利用する際にはこの例のように この Transport を利用して外部通信を実現することが可能です。

まとめ

本稿では Static Backend と Dynamic Backend の特徴や使い方ついて概要を紹介しました。明日は Readthrough Cache について紹介したいと思います。

Discussion