Contents
非同期ハンドラの基礎
Actix-webはRust言語で構築された高性能なWebフレームワークであり、非同期処理を前提とした設計が特徴です。同期処理では1リクエストごとにスレッドが割り当てられますが、非同期ハンドラではイベントループによる軽量タスク実行が可能になります。
このアプローチにより、以下のようなメリットがあります:
- 並列処理の効率化:1つのスレッドで複数リクエストを処理できる
- リソース削減:スレッド作成コストとメモリ消費が大幅に低減
- レスポンスタイム改善:IO待ち中に他の処理を実行可能
async fn + impl Responderの基本構文
非同期ハンドラの実装は、async fnで定義し戻り値にimpl Responderを指定するシンプルな構文です。
実装サンプルコード
|
1 2 3 4 5 6 7 8 9 10 11 12 |
use actix_web::{web, HttpResponse}; // データベースから非同期で取得する関数の例(実装が必要) async fn fetch_from_db() -> String { "ダミーデータ".to_string() } async fn get_data() -> impl actix_web::Responder { let data = fetch_from_db().await; // 非同期IO処理 HttpResponse::Ok().json(data) } |
Responderトレイトの役割
impl Responderを返す関数は、以下の責務を持ちます:
- HTTPレスポンス生成:
HttpResponseやJson<T>などの実装済み構造体を返す - 自動変換処理:Actixが内部で
Body::new()などを呼び出してResponseに変換 - エラーハンドリング:
Result<impl Responder, Error>と併用することで型安全な処理が可能
非同期処理のステージ別実行フロー
非同期ハンドラはリクエスト〜応答までの流れを3段階に分けて動作します。
| ステージ | 説明 |
|---|---|
| 受信処理 | リクエストがサーバーに届くと、イベントループでタスクが生成される |
| 処理実行 | awaitによってIO操作(データベースや外部API)を非同期実行する |
| 応答送信 | 処理結果をHttpResponseとしてクライアントに返す |
このフローでは、Tokioランタイムがタスクスケジューリングを行い、Futuresの実行スコープはActix内部で管理されます。
パラメータ抽出方法
非同期処理でもPath、Query、Formからパラメータを取得できます。特にForm解析では非同期IOが発生するので注意が必要です。
PathとQueryの取得
|
1 2 3 4 5 6 7 8 |
async fn user_info( path: web::Path<String>, query: web::Query<SearchParams> ) -> impl Responder { // pathはURLパラメータ、queryはクエリパラメータを取得 HttpResponse::Ok().body(format!("User ID: {}", path.into_inner())) } |
Form解析の非同期処理
|
1 2 3 4 5 6 7 |
async fn register_user( form: web::Form<RegisterData> ) -> impl Responder { // フォーム送信データは非同期でパースされる HttpResponse::Created().body("登録完了") } |
状態管理(app_data)との連携
共有状態を非同期処理で安全にアクセスするには、Arc<Mutex<T>>と併用します。
|
1 2 3 4 5 6 7 8 9 10 11 12 |
struct AppState { counter: u64, } async fn increment_counter( data: web::Data<Arc<Mutex<AppState>>> ) -> impl Responder { let mut state = data.lock().await; state.counter += 1; HttpResponse::Ok().body(format!("カウンター値:{}", state.counter)) } |
注意点:
Arcで共有所有権を実現Mutexで競合アクセスを防ぐ- 非同期ロックは
.awaitが必要
エラー処理と型安全なレスポンス設計
非同期処理中にエラーが発生する場合、Result型とカスタムエラーレスポンスで対応します。
Result型の活用
|
1 2 3 4 5 6 7 8 9 10 |
async fn get_user( id: web::Path<u64> ) -> Result<impl Responder, actix_web::Error> { let user = match fetch_user(id.into_inner()).await { Ok(u) => u, Err(_) => return Err(actix_web::error::ErrorNotFound("ユーザーが見つかりません")), }; Ok(HttpResponse::Ok().json(user)) } |
カスタムエラーレスポンス
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
use serde_json::{json, Value}; #[derive(Debug)] struct CustomError { code: u16, message: String, } impl actix_web::error::ResponseError for CustomError { fn status_code(&self) -> actix_web::http::StatusCode { actix_web::http::StatusCode::from_u16(self.code).unwrap() } fn error_response(&self) -> HttpResponse { HttpResponse::build(self.status_code()) .json(json!({"error": self.message.clone()})) } } |
注意: json!マクロを使用する場合は、以下をCargo.tomlに追加してください:
|
1 2 3 |
[dependencies] serde_json = "1.0" |
記事の要点まとめ
- 非同期ハンドラは
async fn + impl Responderで実装 - パフォーマンス向上のためにTokioランタイムと連携
- 状態管理には
Arc<Mutex<T>>を併用 - エラー処理では
Result<impl Responder, Error>を使う
このようにActix-webの非同期処理は、パフォーマンスとコード安全性を両立させる設計が可能です。実際にコードを書いて動きを確認しながら、Actix-webの非同期処理をマスターしましょう。