Actix-webについて

  • リポジトリ
  • 機能
  • アクターモデルについて
  • Getting Started
    • Hello worldやってみる
  • HttepSrverについて
  • Appについて
  • Stateについて
  • ホットリロード設定

2020/9/4 原山和之


リポジトリ


機能

  • Supports HTTP/1.x and HTTP/2
  • Streaming and pipelining
  • Keep-alive and slow requests handling
  • Client/server WebSockets support
  • Transparent content compression/decompression (br, gzip, deflate)
  • Powerful request routing
  • Multipart streams
  • Static assets
  • SSL support using OpenSSL or Rustls
  • Middlewares (Logger, Session, CORS, etc)
  • Includes an async HTTP client
  • Supports Actix actor framework
  • Runs on stable Rust 1.42+

アクターモデルについて

ごめんなさい。全部日本語ですが。。。


Getting Started

Hello worldやってみる1

Cargo Newする

$ cargo new hello-world
$ cd hello-world

Tomlファイルに記述

[dependencies]
actix-web = "2.0"
actix-rt = "1.0"

Hello worldやってみる2

main.rs

use actix_web::{web, App, HttpResponse, HttpServer, Responder};

async fn index() -> impl Responder {
    HttpResponse::Ok().body("Hello world!")
}

async fn index2() -> impl Responder {
    HttpResponse::Ok().body("Hello world again!")
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .route("/", web::get().to(index))
            .route("/again", web::get().to(index2))
    })
    .bind("127.0.0.1:8088")?
    .run()
    .await
}


HttpServerについて1

  • HttpServerがTop-Level
  • HttpServerでは主に以下の設定を行うみたいです。
  • App factory
  • hostname
  • worker thread
  • keep-alive
  • backlog
  • bind address
  • SSL
Architecture overview

HttpServerについて2

main.rs


extern crate actix_web;
use actix_web::{http, server, App, HttpRequest};

fn main() {
    server::new(
        || App::new()
            .route("/", http::Method::GET, |_: HttpRequest| "test"))
        .workers(100)
        .backlog(100)
        .keep_alive(server::KeepAlive::Timeout(75))
        .server_hostname("test".to_string())
        .bind("127.0.0.1:8080").unwrap()
        .run();
}


Appについて1

  • AppはHttpServerにおける1つのworkerが保持するアプリケーションインスタンス

    • Routing
    • State
    • Middleware
  • Appは主にHttpServer作成時にnewメソッドの引数としてAppを作成するFactoryを渡し、HttpServerが各worker threadを立ち上げる際に渡されたFactoryを実行することで作成されます。
  • 特筆すべき点としては、各worker threadで動作するAppは全て別のインスタンスであるということです。

Appについて2


use actix_web::{web, App, Responder, HttpServer};

async fn index() -> impl Responder {
    "Hello world!"
}

#[actix_rt::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new().service(
            web::scope("/app").route("/index.html", web::get().to(index)),
        )
    })
    .bind("127.0.0.1:8088")?
    .run()
    .await
}

Stateについて

  • Config情報やConnection pool、Cache map等といったApp全体で共有したい情報

use actix_web::{web, App, HttpServer};
use std::sync::Mutex;

// This struct represents state
struct AppState {
    app_name: String,
}

async fn index(data: web::Data<AppState>) -> String {
    let app_name = &data.app_name; // <- get app_name

    format!("Hello {}!", app_name) // <- response with app_name
}

#[actix_rt::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .data(AppState {
                app_name: String::from("Actix-web"),
            })
            .route("/", web::get().to(index))
    })
    .bind("127.0.0.1:8088")?
    .run()
    .await
}


ホットリロード設定

  • いちいちコンパイルして、サーバを立ち上げ直すのは少々面倒です。

  • 開発時には大変面倒。効率よく!!的な感じです。

  • Tomlファイルを下記に

[dependencies]
actix-web = "2.0"
actix-rt = "1.0"
serde = "1.0.115"
serde_json = "1.0.57"
env_logger = "0.7.1"
listenfd = "0.3.3"
dotenv = "0.15"

cargoのglobalにsystemfd, cargo-watch

$ cargo install systemfd cargo-watch

ホットリロード


use actix_web::{get, App, HttpServer, Responder};
use dotenv::dotenv;
use listenfd::ListenFd;
use std::env;

#[get("/hello")]
async fn hello_world() -> impl Responder {
    format!("Hello World!")
}


#[actix_rt::main]
async fn main() -> std::io::Result<()> {
    dotenv().ok();

    let mut listenfd = ListenFd::from_env();
    let mut server = HttpServer::new(|| {
        App::new()
            .service(hello_world)
    });

    server = match listenfd.take_tcp_listener(0)? {
        Some(listener) => server.listen(listener)?,
        None => {
            let host = env::var("HOST").expect("Please set host in .env");
            let port = env::var("PORT").expect("Please set port in .env");
            server.bind(format!("{}:{}", host, port))?
        }
    };

    server.run().await
}
  • 下記で実行
$ systemfd --no-pid -s http::3000 -- cargo watch -x run

ホットリロード

[dependencies]
listenfd = "0.3"
use actix_web::{web, App, HttpRequest, HttpServer, Responder};
use listenfd::ListenFd;

async fn index(_req: HttpRequest) -> impl Responder {
    "Hello World!"
}

#[actix_rt::main]
async fn main() -> std::io::Result<()> {
    let mut listenfd = ListenFd::from_env();
    let mut server = HttpServer::new(|| App::new().route("/", web::get().to(index)));

    server = if let Some(l) = listenfd.take_tcp_listener(0).unwrap() {
        server.listen(l)?
    } else {
        server.bind("127.0.0.1:3000")?
    };

    server.run().await
}
  • 下記で実行
$ systemfd --no-pid -s http::3000 -- cargo watch -x run

最後に

  • dockerizeを後日完成させたい。Dickerfileの書き方わからない。
  • 次はもうちょっと勉強してMiddleware, DBアクセスについて話せたらなと思います。

ツール

参考

Rustでwebサーバハンズオン! ~actix-web & postgres~ Part1

Web Application Server by Rust with actix-web

systemfd


Select a repo