🌀

RustによるマクロサービスフレームワークSpin入門

2023/12/21に公開
2

この記事はAdvent of Spin 2023というオンラインチャレンジに参加した結果してSpinに入門した結果のまとめとなっています。学んだことのまとめですが、Spin入門としても利用いただけるかなとも思っています。

TL;DR

  • マクロサービスフレームワークのSpinをつかって、Rustでマイクロサービスを作ります。
  • JSONのやり取りや、key-valueストアの利用、ルーティングがカバーされています。
  • Hurlというツールを使ってテストファースト開発します。

Spinとは?

Spinとはマイクロサービスを作成するためのフレームワークで次の特徴があります。

  • イベント駆動型のフレームワーク
    • HTTP(S)とRedisにデフォルトで対応
    • いくつかのストレージに標準で対応
  • イベントハンドラーはWasmコンポーネントとして実装される
    • WASI preview 2に対応
    • 開発元が配布しているコンポーネントを、自分のアプリに組み込める
  • Rustだけでなく、GoやJavaScriptなどでもハンドラーを記述できる

コンポーネントモデルを早速製品に取り込んでいるというところが、最も大きなアピールポイントでした。

事前準備

次のものを準備しました。

準備するもの 説明
Spin サーバーレス webアプリフレームワークです。
Hurl cURL的なツール。テキストファイルに書かれたシナリオにそってアクセスし、その結果をテストすることができます。
spin cloudのアカウント 必須ではありません。spinで作成したプロジェクトをデプロイする先です。無料でアカウント作成できます。
Rustの開発環境 必須ではありません。JSやGoなどでも開発できるようです。
wasm32-wasiのターゲット Rustで開発する場合には必要です。

Spinのインストール

こちらにインストール方法が解説されています。macOSではHomebrewを使ってインストールできます。

% brew tap fermyon/tap
% brew install fermyon/tap/spin

XCodeのバージョンがチェックされます。私の場合は、古すぎてアップデートを求められました。このアップデートが一番時間がかかりました。

Hurl

Homebrewを使ってインストールできます

% brew install hurl

Spin Cloudへのサインアップとサインイン

GitHubアカウントでサインアップできます。無料です。カードの登録もいらないので、気軽でした。

サインアップ後、CLIからサインインします。以下のコマンド実行後、指示されるアドレスへブラウザーでアクセスすることでサインインできます。

% spin login

wasm32-wasiのターゲットへの追加

次のようにrustupコマンドを使って追加できます。

% rustup target add wasm32-wasi

hello-world

「Spinについて全く知らない人は、一通り知ってからやるといいと思う」という趣旨の説明があったので、まずはhello-worldを通してSpinに慣れて行きます。

まずspin newコマンドで、アプリを作成します。

% spin new hello-world-spin

コマンドを実行すると、下記のような画面が表示されます。ここで作成するアプリのテンプレートを選びます。矢印キーの上下で>を動かしてテンプレートを選んだら、Enterキーを押して決定します。

Pick a template to start your application with:
> http-c (HTTP request handler using C and the Zig toolchain)
  http-empty (HTTP application with no components)
  http-go (HTTP request handler using (Tiny)Go)
  http-grain (HTTP request handler using Grain)
  http-js (HTTP request handler using Javascript)
  http-php (HTTP request handler using PHP)
  http-py (HTTP request handler using Python)
  http-rust (HTTP request handler using Rust)
  http-swift (HTTP request handler using SwiftWasm)
  http-ts (HTTP request handler using Typescript)
  http-zig (HTTP request handler using Zig)
  redirect (Redirects a HTTP route)
  redis-go (Redis message handler using (Tiny)Go)
  redis-rust (Redis message handler using Rust)
  static-fileserver (Serves static files from an asset directory)

私はRustを使うので、http-rustを選びました。説明と、配置するパスを入力するとアプリが作成されます。

Description: hello world
HTTP path: /...

作製されたフォルダへ移動して、ビルドします。spin buildコマンドでアプリがビルドされます。

% cd hello-world-spin
% spin build

ビルドできることを確認したら、spin upコマンドを実行してアプリを起動します。下記の例では、http://127.0.0.1:3000/でアプリを起動します。

% spin up
Logging component stdio to ".spin/logs/"

Serving http://127.0.0.1:3000
Available Routes:
  hello-world-spin: http://127.0.0.1:3000 (wildcard)

http://127.0.0.1:3000/へブラウザーでアクセスし、次のような画面が表示されれば、アプリが起動しています。

作成したhello-worldアプリの実行画面

作成されたアプリのフォルダー構成

作成したアプリのフォルダー構成は、次のようになっています。ライブラリーパッケージに、spin用の設定が書かれたspin.tomlが追加されています。

.
├── Cargo.lock
├── Cargo.toml
├── spin.toml
├── src
   └── lib.rs

Cargo.tomlはWasmをビルドするための設定がlibセクションにあるのと、spinがdependenciesセクションに書かれているくらいで、大きな変更は加えられていません。

name = "hello-world-spin"
authors = ["Author Name <author-name@example.com>"]
description = "hello world"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = [ "cdylib" ]

[dependencies]
anyhow = "1"
http = "0.2"
spin-sdk = { git = "https://github.com/fermyon/spin", tag = "v2.0.1" }

Hurlを使ってみる

インストールしたHurlはテキストファイルを読んで、そこに書かれているシナリオ通りにHTTP/HTTPS通信を行います。作成したアプリの実行結果を確認するシナリオを次のようにetcフォルダー内に記述します。わかりやすさのために、hurlに与えるシナリオは.hurlという拡張子をつけることとしました。

% mkdir etc
% echo "GET http://127.0.0.1:3000/" > etc/get.hurl

作成したシナリオを実行します。標準では、結果を標準出力に出力します。

% hurl etc/get.hurl
Handling request to Some(HeaderValue { inner: String("http://localhost:3000/") })
Hello, Fermyon%

hurlはテストツールとしての側面もあります。--testオプションをつけると、テスト結果らしく表示されます。

hurl --test etc/get.txt
etc/get.txt: Running [1/1]
Handling request to Some(HeaderValue { inner: String("http://localhost:3000/") })
etc/get.txt: Success (1 request(s) in 3 ms)
--------------------------------------------------------------------------------
Executed files:  1
Succeeded files: 1 (100.0%)
Failed files:    0 (0.0%)
Duration:        5 ms

テストというからには、シナリオに検査する条件をかけます。省略された場合は、ステータスコードが200だと成功とみなされるようです。

先のシナリオを次のように変更します。これでhttp://localhost:3000がステータスコード301が返えすことを検査できます。

GET http://localhost:3000/
HTTP 301

検査結果は次のようになります。期待した値と異なるステータスコードが返ってきたことがわかります。

% hurl --test etc/get.txt
etc/get.txt: Running [1/1]
Handling request to Some(HeaderValue { inner: String("http://localhost:3000/") })
error: Assert status code
  --> etc/get.txt:2:6
   |
 2 | HTTP 301
   |      ^^^ actual value is <200>
   |

etc/get.txt: Failure (1 request(s) in 5 ms)
--------------------------------------------------------------------------------
Executed files:  1
Succeeded files: 0 (0.0%)
Failed files:    1 (100.0%)
Duration:        8 ms

先の修正は、エラー状態となることを意図していました。テストが通るよう、次のようにシナリオを修正します。今回は明示的に期待するステータスコードを記述しました。

GET http://localhost:3000/
HTTP 200

レスポンスに対するアサーションを書く

ステータスコード以外に、レスポンスへの検査をアサーションという形でかけます。シナリオにAssertsセクションを追加して、その中にアサーションを列挙します。例えばレスポンスヘッダーのContent-Typeの値がtext/plainでなければならないことは、次のように記述できます。

GET http://localhost:3000/
HTTP 200
[Asserts]
header "Content-Type" == "text/plain"

レスポンスボディに対するアサーション

レスポンスヘッダーだけでなく、レスポンスボディに対するアサーションも記述できます。レスポンスボディに"Hello"という文字列が含まれていることの検査は、次のようにcontainsという述語を使って記述できます。

GET http://localhost:3000/
HTTP 200
[Asserts]
header "Content-Type" == "text/plain"
body contains "Hello"

アサーションの構造

アサーションはクエリ、述語、期待する値の3つ組で構成されています。上述の例は次のような構造をしています。

クエリ 述語 期待する値
header "Content-Type" == "text/plain"
body contains "Hello"

利用できるクエリは、次の12種類です。

  • status
  • header
  • url
  • cookie
  • body
  • bytes
  • xpath
  • jsonpath
  • regex
  • variable
  • duration
  • certificate

hello-worldアプリにルーティングを追加する

Spinのルーティングは2段階に分かれます。

  1. 呼び出すコンポーネントの決定
  2. コンポーネント内でのルーティング

1のことをSpinは「トリガー」と呼んでいます。トリガーはspin.tomlの記述に基づいて行われます。

2は自分で実装することになるのですが、Rust向けのSDKに用意されているRouterを利用すると良さそうです。

トリガー

spin.tomlには次のような記述が何個もあらわれます。これがトリガーの設定です。この記述は「HTTPでのアクセスで、パスが/で始まる場合は、hello-world-spinというコンポーネントを利用する」と解釈されます。

[[trigger.http]]
route = "/..."
component = "hello-world-spin"

上記の記述は次のような構造を持っています。

記述 解釈 説明
[[trigger.http]] HTTPでのアクセスで イベントの種類
route = "/..." パスが/で始まる場合は、 ルート
component = "hello-world-spin" hello-world-spinというコンポーネントを利用する コンポーネントの指定

Spinが標準で対応しているイベントは、次の2種類です。Redisアクセスの場合は[[trigger.redis]]と記述します。

  • HTTPアクセス
  • Redisアクセス

route属性の値にある...はワイルドカードです。次の3つ全てがマッチします。

  • /
  • /hello-world
  • /hello/world

コンポーネント内でのルーティング

トリガーの設定では、パターンを使ったパスやクエリストリングからパラメーターの切り出しや、HTTPメソッドに応じたコンポーネントの処理の切り替えといった細かなルールを記述できません。これらはコンポーネントの責任とされています。つまりいわゆるルーティングは、コンポーネント内で実装することとなります。

Rust向けSDKにはRouterの実装が提供されているため、これを使うと細かい文字列操作をしなくても済むので良いでしょう。おおまかな説明はドキュメントのRouting in a Componentで解説されています。細かいAPIドキュメントを見つけることができませんでしたが、ソースコード内部で利用しているクレートが参考になるように思いました。

hello-worldへのルーティングの追加

コンポーネントにルーティングを追加して、次の振る舞いを実装します。

GET http://localhost:3000/world
HTTP 200
[Asserts]
body == "Hello, world"

GET http://localhost:3000/
HTTP 200
[Asserts]
body == "Hello, Spin"

lib.rsを次のように変更しました。apiモジュールを追加してAPIを実装した点と、handle_hello_world_spin関数にRouterを追加して、APIとパスとの関連付けを行った点の2点が変更点です。

use spin_sdk::http::{Request, Response, Router};
use spin_sdk::http_component;

/// A simple Spin HTTP component.
#[http_component]
fn handle_hello_world_spin(req: Request) -> Response {
    let mut router = Router::new();
    router.get("/:name", api::greet);
    router.handle(req)
}

mod api {
    use spin_sdk::http::{IntoResponse, Params, Request};

    pub fn greet(_: Request, params: Params) -> anyhow::Result<impl IntoResponse> {
        let name = params.get("name").unwrap_or("Spin");
        let greetings = format!("Hello, {}", name);
        Ok(http::Response::builder()
            .status(200)
            .header("content-type", "text/plain")
            .body(greetings)?)
    }
}

変更適用後、テストを実行すると、次のようなエラーが発生します。

  |
2 | HTTP 200
  |      ^^^ actual value is <404>
  |

これは次の'/'に対するテスト結果でした。

GET http://localhost:3000/
HTTP 200
[Asserts]
body == "Hello, Spin"

nameパラメーターが指定されていない場合のパターンを認識できず、ルートが決められなかったため404が返ってきているようです。そこで'/'専用のパターンを次のように追加したところ、テストは全て通るようになります。

fn handle_hello_world_spin(req: Request) -> Response {
    let mut router = Router::new();
    router.get("/", api::greet);
    router.get("/:name", api::greet);
    router.handle(req)
}

Advent of Spin 2023 Challenge #1

長いhello-worldを終え、ようやく1番目の問題に挑戦します。別のアプリを作成します。

% spin new advent-of-spin2023-challenge01

作成されたspin.tomlのトリガーは以下のようになっています。route/dataとなっているのは、問題の仕様からです。次の節で述べますが、/dataに実装するエンドポイントが配置されているように仕様で求められています。

[[trigger.http]]
route = "/data"
component = "advent-of-spin2023-challenge01"

なお、advent-of-spin-2023-01という名前でアプリを作成しようとすると、エラーが発生しました。-で区切られた各セグメントは文字で始まらなければならないルールだそうです。adevent-of-spin-y2023-challenge01といった名前なら問題なさそうです。

% spin new advent-of-spin-2023-01
error: Invalid value "advent-of-spin-2023-01" for '<NAME>': Each segment of the name must start with a letter. '2023', '01' do not start with a letter

仕様

詳細はこちらに記載されていますが、まとめると次のようになろうかと思います。テストを見た方が簡潔でわかりやすいかもしれません。

  • /index.htmlがフロントエンド
  • フロントエンドから参照されるエンドポイントが/data
    • GETとPOSTを受け付ける
      • POSTの場合:受け付けたJSONに記述されているvalueの値で、保存している値を更新する
      • GETの場合:保持している値をJSON形式で出力する
      • クエリストリングの値をキーとして値を保存する
    • 出力のcontent-typeはapplication/json

静的ファイル配信用のコンポーネント追加

静的ファイルの配信をするために、静的ファイルを配信するためのコンポーネントを追加します。コンポーネントはSpinの開発元が配布しているものを利用します。

Spinでは、1つのアプリに複数のコンポーネントを利用することができます。spin addコマンドで、アプリへのコンポーネントを追加できます。利用するspin-fileserverコンポーネントは、用意されているテンプレートを利用して追加すると設定が簡単に行えます。

% cd advent-of-spin2023-challenge01
% spin add -t static-fileserver

上述のコマンドを実行してコンポーネントを追加した後のspin.tomlは、次のようになっています。[[trigger.http]]が2つあることに注目してください。2つ目のものが、静的ファイル配信用のコンポーネントに関する設定です。/data以外へのアクセスはstatic-fileコンポーネントが処理するように設定しています。

spin_manifest_version = 2

[application]
name = "advent-of-spin2023-challenge01"
version = "0.1.0"
authors = ["Author Name <author-name@example.com>"]
description = "Advent of Spin 2023 Challenge 01"

[[trigger.http]]
route = "/data"
component = "advent-of-spin2023-challenge01"

[component.advent-of-spin2023-challenge01]
source = "target/wasm32-wasi/release/advent_of_spin2023_challenge01.wasm"
allowed_outbound_hosts = []
[component.advent-of-spin2023-challenge01.build]
command = "cargo build --target wasm32-wasi --release"
watch = ["src/**/*.rs", "Cargo.toml"]

[[trigger.http]]
route = "/..."
component = "static-file"

[component.static-file]
source = { url = "https://github.com/fermyon/spin-fileserver/releases/download/v0.1.0/spin_statics.wasm", digest = "sha256:650376c33a0756b1a52cad7ca670f1126391b79050df0321407da9c741d32375" }
files = [{ source = "assets", destination = "/" }]

static-fileコンポーネントの自体の設定が、[component.static-file]セクションに記述されています。fieles属性に、ファイルシステム上のパスとURL上のパスとの対応関係が記述できます。今回はすべてのファイルがassetsフォルダに入っていることにしました。

source属性で、アプリで利用するWasmコンポーネントの位置を指定します。GitHubでホストされているspin_static_fs.wasmを利用します。このままでも動作しますが、最新版のv0.2.1を利用するように変更しました。digestの値はリリースページにあるchecksums-v0.2.1txtに書かれています。

[component.static-file]
source = { url = "https://github.com/fermyon/spin-fileserver/releases/download/v0.2.1/spin_static_fs.wasm", digest = "sha256:5f05b15f0f7cd353d390bc5ebffec7fe25c6a6d7a05b9366c86dcb1a346e9f0f" }
files = [{ source = "assets", destination = "/" }]

静的ファイルの配置

assetsフォルダを作成し、設定した通り静的ファイルを配信できるようにします。空のindex.htmlを配置しました。

% mkdir assets
% touch assets/index.html

テストの設定

Advent of Spinのレポジトリ公開されているhurlファイルをもとに、テストを作成します。ファイルをそのまま利用してもよかったのですが、結果の見やすさのために、テストを3つのファイルに分割しました。

% mkdir tests
% cat > tests/test01.hurl # Test 1 の内容を test01.hurl にコピペ
% cat > tests/test01.hurl # Test 2 の内容を test01.hurl にコピペ
% cat > tests/test01.hurl # Test 3 の内容を test01.hurl にコピペ

ビルドと起動、そしてテスト。

とりあえずビルドして起動します。

(省略)
Serving http://127.0.0.1:3000
Available Routes:
  advent-of-spin2023-challenge01: http://127.0.0.1:3000/data
  static-file: http://127.0.0.1:3000 (wildcard)

その後、テストを実行します。index.htmlの有無を確かめるテスト(test01)以外は全て失敗しています。つまり、satic-fileコンポーネントは意図通り動作していることがわかりました。

% hurl tests/* --test
tests/test01.hurl: Running [1/3]
tests/test01.hurl: Success (1 request(s) in 53 ms)
tests/test02.hurl: Running [2/3]
Handling request to Some(HeaderValue { inner: String("http://localhost:3000/data?advent") })
error: Assert status code
  --> tests/test02.hurl:7:6
   |
 7 | HTTP 201
   |      ^^^ actual value is <200>
   |

tests/test02.hurl: Failure (1 request(s) in 5 ms)
tests/test03.hurl: Running [3/3]
Handling request to Some(HeaderValue { inner: String("http://localhost:3000/data?advent") })
error: Assert failure
  --> tests/test03.hurl:6:0
   |
 6 | header "Content-Type" == "application/json"
   |   actual:   string <text/plain>
   |   expected: string <application/json>
   |

error: Invalid JSON
  --> tests/test03.hurl:7:1
   |
 7 | jsonpath "$.value" == "of Spin"
   | ^^^^^^^^^^^^^^^^^^ the HTTP response is not a valid JSON
   |

tests/test03.hurl: Failure (1 request(s) in 1 ms)
--------------------------------------------------------------------------------
Executed files:  3
Succeeded files: 1 (33.3%)
Failed files:    2 (66.7%)
Duration:        62 ms

コンポーネント内ルーティングの設定

src/lib.rsを開き、コンポーネント内ルーティングの設定をします。Routerオブジェクトにルールを追加していけば良いのですが、getメソッドやpostメソッドに指定するパスは絶対パスである必要があります。私は次のようにルーティングの設定をしました。

use spin_sdk::http::{Request, Response, Router};
use spin_sdk::http_component;
use url::Url;

/// A simple Spin HTTP component.
#[http_component]
fn handle_advent_of_spin2023_challenge01(req: Request) -> Response {
    let mut router = Router::new();
    router.get("/data", api::show_whishlist);
    router.post("/data", api::update_whishlist);
    router.handle(req)
}

mod api {
    pub fn update_whishlist(_: Request, _: Params) -> anyhow::Result<impl IntoResponse> {
        let json_string = String::new();

        Ok(Response::builder()
            .status(201)
            .header("content-type", "application/json")
            .body(json_string)
            .build())
    }

    pub fn show_whishlist(_: Request, _: Params) -> anyhow::Result<impl IntoResponse> {
        let json_string = String::new();
        Ok(Response::builder()
            .status(200)
            .header("content-type", "application/json")
            .body(json_string)
            .build())
    }
}

POSTされたデータによるデータの更新

仕様からPOSTされたデータを保存して置く必要があることがわかります。Spinは標準で次のストレージを利用できます。なおMySQLはSpinに含まれません。別途サービスを設定し、起動する必要があります。またMySQL以外のRDBMSも利用できますが、操作するライブラリーがwasm32-wasiにビルドできることが必要です。

今回は簡単に使用できるkey-valueストア(KVS)を利用することにしました。

key-valueストアの設定

KVSを利用するためには、spin.tomlのcomponent設定に、次のように利用するKVSの名前を登録する必要があります。今回はデフォルトのKVSを表す"default"を登録しました。

[component.advent-of-spin2023-challenge01]
source = "target/wasm32-wasi/release/advent_of_spin2023_challenge01.wasm"
allowed_outbound_hosts = []
key_value_stores = ["default"]

KVSに保存できるオブジェクトは、Serde::Deserializeを実装している必要があります。またJSON形式のデータを扱うことを考えると、Serdeserde_jsonが必要です。次のようにcargo addコマンドで依存するクレートに追加しておきます。なおSerdeはderiveマクロと共に利用するので、deriveフィーチャーを有効にしておきます。

% cargo add serde -F derive
% cargo add serde_json

POSTのハンドラーの実装

POSTハンドラーのやることは、次の3つです。

  1. POSTされたデータをJSONとして構文解析する
  2. 解析されたデータのvalue属性の値で、保存されている値を更新する
  3. 更新されたデータを{ value: 値 }の形式でJSONとして返す

まずやりとりするデータ構造を作ります。JSONに変換できるように、deriveマクロを使ってSerde::DeserializeSerde::Serializeを実装します。次のスニペットでは2つのトレイトに加えて、デバッグ用にDebugトレイトも実装しています。また、いくつかのメソッドも実装しています。

mod api {
// 中略

    #[derive(serde::Serialize, serde::Deserialize, Debug)]
    struct Whishlist {
        value: String,
    }

    impl Whishlist {
        fn new(value: String) -> Whishlist {
            Whishlist { value }
        }
        fn empty() -> Whishlist {
            Whishlist::new(String::new())
        }
    }

// 中略        
}

あとは上記の手順をupdate_whitelist関数に実装します。設定したdefaultのKVSはStore::open_default関数が返すハンドル経由で利用できるようになりますgetメソッドで保存されている値を取得できます。次のスニペットが実装した例です。

mod api {
    // 中略
    pub fn update_whishlist(req: Request, _: Params) -> anyhow::Result<impl IntoResponse> {
        let store = Store::open_default()?;
        let posted: Whishlist = serde_json::from_slice(req.body())?;
        let key = req.query();

        store.set_json(key, &posted)?;
        let json_string = serde_json::to_string(&posted)?;

        Ok(Response::builder()
            .status(201)
            .header("content-type", "application/json")
            .body(json_string)
            .build())
    }
    // 中略
}

上記を実装した上でSpinを再起動すると、2つ目のテストまでパスするようになります。

なお、Response::builderはHTTPレスポンスを作成するためのビルダーオブジェクトを返します。headerメソッドを呼ぶことで、HTTPヘッダーを変更できます。同様にbodyメソッドでHTTPボディを設定できます。設定が終わったら、buildメソッドを呼んで設定済みのResponseオブジェクトを取得します。

GETハンドラーの実装

POSTハンドラーと同様にGETハンドラーを実装します。ハンドラーのやることは、次の3つです。

  1. KVSから保存されているwatchlistを取得する
  2. 取得したwatchlistを、期待される形のJSONに変形する
  3. 変形したJSONをレスポンスとして返す

上記の3つを順に実装すれば、3つ目のテストもパスできます。実装した例が次のスニペットです。

mod api {
    // 中略
    pub fn show_whishlist(req: Request, _: Params) -> anyhow::Result<impl IntoResponse> {
        let store = Store::open_default()?;
        let key = req.query();
        let whilshlist = store.get_json(key)?.unwrap_or(Whishlist::empty());
        let json_string = serde_json::to_string(&whilshlist)?;

        Ok(Response::builder()
            .status(200)
            .header("content-type", "application/json")
            .body(json_string)
            .build())
    }    
    // 中略
}

提出

spin deployコマンドで、作成したアプリをFremyonのCloudサービスに配置できます。配置したらここに記載されている通りhurlコマンドを実行して提出完了です。お疲れ様でした。

% spin deploy
Uploading advent-of-spin2023-challenge01 version 0.1.0 to Fermyon Cloud...
Deploying...
Waiting for application to become ready................. ready
Available Routes:
  advent-of-spin2023-challenge01: https://advent-of-spin2023-challenge01-somewhere-in.fermyon.app/data
  static-file: https://advent-of-spin2023-challenge01-somewhere-in.fermyon.app (wildcard)

まとめと感想

環境を作るところから始めて、hello-world、複数のコンポーネントを組み合わせてアプリを作るところまで一通り体験できました。触れた内容は次のようになるかと思います:

  • Spinの導入
  • Hurlを使ったテストファースト開発
  • ルーティング
  • 複数コンポーネントを組み合わせた開発
  • KVSの利用

組み込みのルーターが素朴で、やらなきゃいけないことが多いなあという印象を受けました。ただ、やり取りはされるデーターは全てJSONなりgRPCになっているという前提だと、これでいいのかもしれません。データシリアライゼーションライブラリーが組み込み出ないのも、開発者の選択肢を狭めたくないということなのかもしれません。

また大きなコンポーネントを作るというよりは、小さいコンポーネントを組み合わせて大きな機能を作るという方針のようにも見えました。その方針にWasmコンポーネントは悪くない選択なようにも感じています。

公式サイトのドキュメントが過不足はないんだけれど、分散していて手厚い感じがしないようにも思いました。せめてcrates.ioのページへのリンクや、doc.rsにあるSDKのドキュメントへのリンクがあれば、いろいろ探す手間が省けてよかったようにもおもいます。公式サイトの色使いやフォントが私の目には優しくなかくドキュメントを読み進めるのが難しく感じたのも、欲しい情報が探しにくいと感じた一因かもしれません。

アプリをOCIレジストリー経由で公開できるspin registoryというコマンドを見つけたので、これを使って自作のアプリをOCIレジストリに公開するのが次のアクションになるかなと考えています。またアプリをFermyon以外のクラウドサービスに配置する方法についても興味があります。あとは自作のコンポーネントをうまく使って再利用性を上げるシナリオが見つかればいいなとも思っています。

レファレンス

Discussion

makoto-developermakoto-developer

RustでWebアプリケーションを作るのが当たり前な時代がいつかくるかもしれないですね。

乳牛乳牛

また大きなコンポーネントを作るというよりは、小さいコンポーネントを組み合わせて大きな機能を作るという方針のようにも見えました。その方針にWasmコンポーネントは悪くない選択なようにも感じています。

こちらの記事を拝読して公式の言う「for building WebAssembly microservices 」が腑に落ちました。 One component = One microservice という考え方っぽいですね。

究極はOne component = One endpointの粒度でも構成できそうですが、そうせずに「まずはざっくりパス単位で分ける」「そしてComponent内で更にRouterを使う」という設計にしているのは過去にこういうことに悩んだのでベターな方法だなとおもって一人納得しました。