Rustの新しいWebフレームワークaxumのtokio、tower、hyperとの関係を整理する

# はじめに
RustのWeb周りって複雑ですよね。。さて、2021年7月に新たなWebフレームワーク`axum`が発表されました。有識者?が「axumがRustのWebフレームワークのデファクトスタンダードになっていくのではないか」と言っている[記事](https://developers.cyberagent.co.jp/blog/archives/31110/)を見たので、2021/9月現在のaxumの現状を整理してみました。まずaxumの土台になっているクレート、`tower`、`hyper`、`tokio`について説明します。

:::note warn
この記事はRustの文法をある程度知っていることを前提にしています。
:::


# tower
:::note warn
以下で書かれているのはtower 0.4.8時点での情報です。
:::

`tower`は「リクエストを受け付けて、いつかレスポンスを返す物」を実装するための土台を用意しています。代表的なものはHTTPですが、他のプロトコルも実装可能です。また、タイムアウトや流量制限、バッファリングなどのミドルウェアの実装を提供しています。
`tower`は以下のようなトレイトを提供しています。

## towerが提供するトレイト

### Service
`Service`はリクエストを受け取って、レスポンスを生成する機能を提供するトレイトです。これが`tower`の核です。以下のような定義になっています。

```rust
trait Service<Request> {
  type Response;
  type Error;
  type Future: Future<Output = Result<Self::Response, Self::Error>>;

  fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>>;

  fn call(&mut self, Request) -> Self::Future;
}
```
call関数がリクエストを処理する関数です。callは`Self::Future`つまり、`Future<Output = Result<Self::Response, Self::Error>>`を返します。つまり成功すれば、`Self::Response`、失敗すれば`Self::Error`を返す`Future`です。`response`がジェネリクスではなく、関連型なのは、ある構造体が、あるリクエストに対して関連するレスポンスは1つにしたいからです。関連型とジェネリクスの違いについてはこの[記事](https://qiita.com/statiolake/items/2c9b59976844cf9d1308)が参考になります。

poll_readyはリクエストを処理する準備ができているかどうかを返す関数です。

### Layer
`Layer`は`Service`の[デコレーター](https://ja.wikipedia.org/wiki/Decorator_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3)です。以下のような定義になっています。

```rust
trait Layer<S> {
  type Service;

  fn layer(&self, inner: S) -> Self::Service;
```

あるミドルウェアが`Layer`トレイトを実装している場合、layer関数を呼ぶことで、サービスにミドルウェアの機能を付加した新しいサービスを生成することができます。現時点でtowerに用意されているミドルウェアは、少なくとも私が見たものはすべて`Layer`トレイトを実装していました。ただし、`ServiceBuilder`を使えば、`Layer`トレイトを実装していなくても、サービスに機能を付加することは可能です。

### ServiceBuilder
`Service`の[ビルダー](https://ja.wikipedia.org/wiki/Builder_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3)です。`ServiceBuilder`を用いることで以下のようにサービスを生成することができます。

```rust
  ServiceBuilder::new()
    .concurrency_limit(10)
    .buffer(100)
    .service(svc);
```

# hyper
`hyper`は低レベルのHTTPライブラリです。`axum`ではhyperのBody構造体がHTTPのリクエストとレスポンスのボディとして使われています。`Server`も`hyper`の物が使われています。他にもいろいろ使われているかもしれません。

# tokio
`tokio`はRustの非同期ランタイムです。RustはGoのようにグリーンスレッドをスケジューリングするランタイムは言語に付属していません。したがって、外部クレートを用いる必要があります。Rustの非同期処理というのは、FutureをExecutorという実行器に渡すことでTaskを生成し、ExecutorがそのTaskをスケジューリングして実行するといった流れで行われます。tokioはRustで非同期処理を実行するためのランタイムを提供しています。

Future、Executor、Task。。なんそれ？という人はtokioのチュートリアルを[和訳](https://zenn.dev/magurotuna/books/tokio-tutorial-ja)してくれている方がいるので、これを読んで見ると良いと思います。

# axum
:::note warn
以下で書かれているのはaxum 0.2.5時点での情報です。
:::
axumはRustのWebフレームワークです。`tokio`、`tower`、`hyper`の上で実装されています。(もちろん他にもたくさんのクレートに依存しています。(ex. `tower-http`等)

最初にサンプルプログラムをお見せします。`{"first_name":"Jeff", "last_name":"Dean", "age":58}`のようなJSONをPOSTで投げると、`<p>Hello, Jeff Dean! 58 years old! Looks young!</p>`のようなレスポンスを`Content-Type: text/html`で返すサーバーです。

```rust
use axum::{
    handler::post,
    Router,
    response::Html,
    Json,
};
use tokio::time::Duration;
use tower::timeout::TimeoutLayer;
use serde::Deserialize;

#[derive(Deserialize, Debug)]
struct User {
    first_name: String,
    last_name: String,
    age: u32,
}

#[tokio::main]
async fn main() {
// Routerはhandlerとserviceを合成するための構造体です。
// towerのミドルウェア、Timeoutを使いタイムアウト機能を付加しています。
    let app = Router::new().route("/", post(handler)).layer(TimeoutLayer::new(Duration::from_secs(1)));

// hyperのServerでappをserviceに変換したものを実行します。
    axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
        .serve(app.into_make_service())
        .await
        .unwrap();
}

// Json<User>がExtractor、Html<String>がIntoResponseを実装している構造体
async fn handler(Json(user): Json<User>) -> Html<String> {
// ほんとはサニタイズしないとアカンよね。。
    Html(format!("<p>Hello, {} {}! {} years old! Looks young!</p>", user.first_name, user.last_name, user.age))
}
```


以下のような概念が存在します。

## handlers
Requestを処理してResponseを返す関数です。正確に言うと、0個以上の`Extoractors`を引数にとり、`IntoResponse`を実装しているなにかを返す非同期の関数です。

## Extractors
`FromRequest`を実装している型です。つまりリクエストから変換できる型ということです。handlerの引数として使われます。

## Response
`IntoResponse`を実装している物はhandlerの返り値になることができます。


## Middleware
`axum`は`tower`の上で実装されているので、`tower`のミドルウェアを使うことができます。サンプルプログラムではタイムアウト機能を付加しています。ハンドラ内にsleep関数を書き、ワザとタイムアウトするようにして、httpieでpostリクエストを送ってみたところ、

```
http: error: ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response')) while doing a POST request to URL: http://localhost:3000/
```

というエラーになりました。（ほんとはサーバー側で[エラーハンドリング](https://docs.rs/axum/0.2.5/axum/#error-handling)しなきゃだめっぽいですが。。)

## Serviceのルーティング
「axumはtowerの上に実装されているんやから、serviceはルーティングできひんのか？」という疑問を持つ方もいらっしゃるかもしれません。安心してください。使えます。この[セクション](https://docs.rs/axum/0.2.5/axum/#routing-to-any-service)を御覧ください。

#　終わりに
この記事では`axum`の`tokio`、`tower`、`hyper`との関係性を概観しました。この記事を書いていて、一番懸念したのは「なんか小難しいなあ。Go使えばよくね？」と思われるのではないかということです。確かに自分もほとんどの場面でGoで十分な気がします。ただRustにはゼロコスト抽象化の信念を持っているため、Goより高いパフォーマンスを発揮する可能性がある(Goのランタイムは非常に優秀らしいので、Goに勝つのは大変そうですが。。）、GCによる予測不能な停止がない、メモリ安全性を持っている、代数的データ型やパターンマッチング等、Goにはない機能があるなどの利点が、あることにはあるのでGoより適したユースケースもあるんじゃないかなと思っています。というかあってほしいです。


# 参考文献
1. https://github.com/tokio-rs/axum
1. https://github.com/hyperium/hyper
1. https://github.com/tower-rs/tower
1. https://github.com/tower-rs/tower/blob/master/guides/building-a-middleware-from-scratch.md
1. https://tokio.rs/blog/2021-05-14-inventing-the-service-trait
1. https://docs.rs/axum/0.2.5/axum/
1. https://docs.rs/hyper/0.14.13/hyper/
1. https://docs.rs/tower/0.4.8/tower/
1. https://zenn.dev/magurotuna/books/tokio-tutorial-ja