---
title: Tower とミドルウェア
url: https://doc.liz6.com/ja/rust/12-async-ecosystem/01-tower-and-middleware
locale: ja
area: rust
tags:
- rust
- async-ecosystem
date: 2026-06-30
modified: 2026-07-16
description: Tower は Service トレイト（リクエストを処理して Future を返す）と Layer（Service をラップして新しい Service を生成する）という 2 層の抽象化により、非同期ミドルウェアスタックを構築します。ミドルウェアは積み重ね可能（タイムアウト→リトライ→レート制限→ビジネスロジック）であり、バックプレッシャは readiness の待機を通じて上位層へ伝播します。これは Rust 非同期エコシステムで最もエレガントなオニオンアーキテクチャの実装です…
---

# Tower とミドルウェア

> Tower は Service トレイト（リクエストを処理して Future を返す）と Layer（Service をラップして新しい Service を生成する）という 2 層の抽象化により、非同期ミドルウェアスタックを構築します。ミドルウェアは積み重ね可能（タイムアウト→リトライ→レート制限→ビジネスロジック）であり、バックプレッシャは readiness の待機を通じて上位層へ伝播します。これは Rust 非同期エコシステムで最もエレガントなオニオンアーキテクチャの実装です。

## Tower が解決する課題

ネットワークサービスを作成する際には、いくつかの横断的関心事（cross-cutting concerns）があります。具体的には、タイムアウト、レート制限、並行性制御、リトライ、ログ/metrics です。これらのロジックを各サービスにハードコードしてしまうと、コードの重複と組み合わせ可能性の欠如を招きます。例えば、gRPC ハンドラはビジネスロジックの処理とタイムアウト処理の両方を担わなければならず、HTTP ハンドラも同様のレート制限ロジックを必要とします。

Tower の答えは、サービスを `Service` トレイトとして抽象化し、`Layer` を用いてデコレータパターンでこれらの関心事を組み合わせることです。各ミドルウェアは1つのことだけを責任として持ち、Layer スタックがそれらを組み合わせて完全なサービスへと仕上げます。

## Service trait: 非同期の request → response

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

    fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>>;
    fn call(&mut self, req: Request) -> Self::Future;
}
```

このトレイトの2つのメソッドには明確な役割分担があります。

**`poll_ready`**: サービスが新しいリクエストを受け付けることができるかどうかを確認します。レートリミッターは残りのクォータをチェックし、並行性リミッターは空いているスロットをチェックします。サービスがまだ受信できない場合、`Poll::Pending` を返します。呼び出し側は、次にウェイクアップされるまで待つ必要があります。これが Tower に内蔵された **バックプレッシャ（背圧）メカニズム**です。下位層サービスの負荷が `poll_ready` を通じて上位層へ伝達され、追加のメッセージチャネルは不要です。

**`call`**: リクエストを実行し、Future を返します。`call` の `&mut self` シグネチャは、同じ Service インスタンスへの呼び出しが本質的に直列化されることを意味します。同じインスタンス上で並列実行が必要な場合は、`Buffer` ミドルウェアを使用して Clone 可能にします。

この2段階の分離は、Tower と他の多くのミドルウェアフレームワーク（Express や actix のミドルウェアなど）の核心的な違いです。それらのフレームワークは通常 `fn handle(&self, req) -> Future` という1つのメソッドしか持たず、バックプレッシャのセマンティクスを持ちません。

## Layer: デコレータパターンの関数化された表現

```rust
pub trait Layer<S> {
    type Service;
    fn layer(&self, inner: S) -> Self::Service;
}
```

Layer は Service ではなく、Service の**ファクトリ**です。内側のサービス（inner service）を受け取り、それをラップしたラッパーサービス（wrapper service）を返します。最大の利点は、**Layer を組み合わせられる**ことです。

```rust
let svc = ServiceBuilder::new()
    .layer(TimeoutLayer::new(Duration::from_secs(5)))
    .layer(ConcurrencyLimitLayer::new(100))
    .layer(RateLimitLayer::new(10, Duration::from_secs(1)))
    .service(my_service);
```

`ServiceBuilder::new().layer(A).layer(B).service(S)` は実際には以下を構築します。
```
A::layer(B::layer(S))
```
つまり `A << B << S` です。各 Layer が内側のサービスをラップします。実際にリクエストが到来したときの実行順序は外側から内側へ向かいます。リクエスト → A → B → S → B → A → レスポンス。

## バックプレッシャの理解

バックプレッシャがない場合、並列リクエストがサービスに直接押し寄せ、サービスがオーバーロードされる可能性があります。Tower の `poll_ready` により、ミドルウェアは**リクエストを受け取る前**に拒否することができます。

<svg viewBox="0 0 720 340" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Source Han Sans CN','Microsoft YaHei',sans-serif" role="img" aria-label="poll_ready の3つの戻り値とそれに対応する処理方法">
  <defs>
    <marker id="arrowTower" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" fill="#475569"/></marker>
  </defs>
  <rect width="720" height="340" fill="#ffffff"/>
  <text x="360" y="28" text-anchor="middle" font-size="17" font-weight="700" fill="#1f2933">poll_ready() の3つの結果: リクエスト前に探知</text>

  <rect x="90" y="132" width="100" height="36" rx="6" fill="#e2e8f0"/>
  <text x="140" y="155" text-anchor="middle" font-size="12" font-weight="700" fill="#334155">リクエスト</text>

  <line x1="190" y1="150" x2="226" y2="150" stroke="#475569" stroke-width="1.7" marker-end="url(#arrowTower)"/>

  <rect x="230" y="120" width="170" height="60" rx="8" fill="#4f46e5"/>
  <text x="315" y="155" text-anchor="middle" font-size="13" font-weight="700" fill="#ffffff">poll_ready()?</text>

  <line x1="400" y1="140" x2="468" y2="75" stroke="#475569" stroke-width="1.6" marker-end="url(#arrowTower)"/>
  <line x1="400" y1="150" x2="468" y2="150" stroke="#475569" stroke-width="1.6" marker-end="url(#arrowTower)"/>
  <line x1="400" y1="160" x2="468" y2="225" stroke="#475569" stroke-width="1.6" marker-end="url(#arrowTower)"/>

  <rect x="470" y="50" width="210" height="50" rx="8" fill="#ffedd5" stroke="#f97316"/>
  <text x="575" y="72" text-anchor="middle" font-size="12.5" font-weight="700" fill="#9a3412">Pending</text>
  <text x="575" y="90" text-anchor="middle" font-size="10.5" fill="#c2410c">呼び出し側は待機必須（非同期通知）</text>

  <rect x="470" y="125" width="210" height="50" rx="8" fill="#ffffff" stroke="#ef4444" stroke-width="1.5"/>
  <text x="575" y="147" text-anchor="middle" font-size="12.5" font-weight="700" fill="#dc2626">Err(e)</text>
  <text x="575" y="165" text-anchor="middle" font-size="10.5" fill="#dc2626">サービス利用不可（エラーを直接返す）</text>

  <rect x="470" y="200" width="210" height="50" rx="8" fill="#dcfce7" stroke="#4ade80"/>
  <text x="575" y="222" text-anchor="middle" font-size="12.5" font-weight="700" fill="#166534">Ready</text>
  <text x="575" y="240" text-anchor="middle" font-size="10.5" fill="#15803d">call() を呼び出す</text>

  <rect x="60" y="270" width="600" height="56" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="76" y="292" font-size="12.5" fill="#3730a3">poll_ready と call の分離は Tower に内蔵されたバックプレッシャメカニズムです。リクエスト受信前に就绪状態を探知し、</text>
  <text x="76" y="312" font-size="12.5" fill="#3730a3">下位層の負荷は poll_ready を通じて直接上位層へ伝達され、追加のメッセージチャネルは不要です。</text>
</svg>

つまり、`ConcurrencyLimitLayer` が並行性の上限に達すると、新しい `poll_ready` は Pending を返します。パニックしたり、拒絶したりするのではなく、**優雅に延期**されます。呼び出し側の Future はスロットが空いた後にウェイクアップされ、処理を続行します。この一連のプロセスは Future の poll モデル内で自然に表現され、追加のセマフォやチャネルは不要です。

## 一般的なミドルウェア

- **`Timeout`**: `call` が返す Future が指定時間後にまだ Ready にならない場合 → タイムアウトエラーを返します。注意：これは内部の実行を停止しません（tokio では `tokio::select!` や `JoinHandle::abort` を使わない限り、基盤となるタスクを強制的にキャンセルできません）。
- **`ConcurrencyLimit`**: 内部カウンターで同時に処理されるリクエスト数を制限します。カウンターが上限に達すると poll_ready は Pending を返します。
- **`RateLimit`**: トークンバケットアルゴリズムでリクエストレートを制限します。トークンが不足している場合、poll_ready は Pending を返します。
- **`Buffer`**: `call(&mut self)` が共有できない問題を解決します。内部でチャネルを使用してリクエストをワーカースレッドへ転送し、外部には Clone 可能なインターフェースを公開します。
- **`Retry`**: 失敗したリクエストを自動的にリトライします。リトライ条件と指数バックオフを設定可能です。ただし、冪等性に注意が必要です。リトライは冪等な操作（GET, PUT）に対してのみ安全です。
- **`Trace`**: 各リクエストの所要時間を自動的に記録します。ビジネスロジックに手動で時刻計測コードを挿入する必要はありません。

## tonic は tower のネイティブな消費者です

```rust
tonic::transport::Server::builder()
    .layer(TimeoutLayer::new(Duration::from_secs(5)))
    .add_service(GreeterServer::new(MyGreeter))
    .serve(addr).await?;
```

各 gRPC メソッドは自動的に `Service` となります。tonic の codegen は、あなたのハンドラに対して `Service` impl を生成します。つまり、tower のすべてのミドルウェアを、変更を加えることなく任意の gRPC サービスに適用できるということです。

## 参考

- **tower**: docs.rs/tower (README に設計哲学の説明があります)
- **tonic**: github.com/hyperium/tonic

*Keywords: tower, Service, Layer, middleware, tonic, poll_ready, backpressure*
