---
# System prepended metadata

title: Actix-web
tags: [' Talk', Rust, ' 勉強会']

---

---
title: Actix-web
tags: Rust, Talk, 勉強会
description: Actix-webについて
slideOptions:
    theme: white
    slideNumber: 'c/t'
    center: false
    transition: 'none'
    keyboard: true
---

<style>
/* basic design */
.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6,
.reveal section, .reveal table, .reveal li, .reveal blockquote, .reveal th, .reveal td, .reveal p {
    font-family: 'Meiryo UI', 'Source Sans Pro', Helvetica, sans-serif, 'Helvetica Neue', 'Helvetica', 'Arial', 'Hiragino Sans', 'ヒラギノ角ゴシック', YuGothic, 'Yu Gothic';
    text-align: left;
    line-height: 1.6;
    letter-spacing: normal;
    text-shadow: none;
    word-wrap: break-word;
    color: #444;
}
.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 {font-weight: bold;}
.reveal h1, .reveal h2, .reveal h3 {color: #2980b9;}
.reveal th {background: #DDD;}
.reveal section img {background:none; border:none; box-shadow:none; max-width: 95%; max-height: 95%;}
.reveal blockquote {width: 90%; padding: 0.5vw 3.0vw;}
.reveal table {margin: 1.0vw auto;}
.reveal code {line-height: 1.2;}
.reveal p, .reveal li {padding: 0vw; margin: 0vw;}
.reveal .box {margin: -0.5vw 1.5vw 2.0vw -1.5vw; padding: 0.5vw 1.5vw 0.5vw 1.5vw; background: #EEE; border-radius: 1.5vw;}
/* table design */
.reveal table {background: #f5f5f5;}
.reveal th {background: #444; color: #fff;}
.reveal td {position: relative; transition: all 300ms;}
.reveal tbody:hover td { color: transparent; text-shadow: 0 0 3px #aaa;}
.reveal tbody:hover tr:hover td {color: #444; text-shadow: 0 1px 0 #fff;}
/* blockquote design */
.reveal blockquote {
    width: 90%;
    padding: 0.5vw 0 0.5vw 6.0vw;
    font-style: italic;
    background: #f5f5f5;
}
.reveal blockquote:before{
    position: absolute;
    top: 0.1vw;
    left: 1vw;
    content: "\f10d";
    font-family: FontAwesome;
    color: #2980b9;
    font-size: 3.0vw;
}
/* font size */
.reveal h1 {font-size: 5.0vw;}
.reveal h2 {font-size: 4.0vw;}
.reveal h3 {font-size: 2.8vw;}
.reveal h4 {font-size: 2.6vw;}
.reveal h5 {font-size: 2.4vw;}
.reveal h6 {font-size: 2.2vw;}
.reveal section, .reveal table, .reveal li, .reveal blockquote, .reveal th, .reveal td, .reveal p {font-size: 2.2vw;}
.reveal code {font-size: 1.6vw;}
/* new color */
.red {color: #EE6557;}
.blue {color: #16A6B6;}
/* split slide */
#right {left: -18.33%; text-align: left; float: left; width: 50%; z-index: -10;}
#left  {left:  31.25%; text-align: left; float: left; width: 50%; z-index: -10;}
</style>

<style>
/* specific design */
.reveal h2 {
    padding: 0 1.5vw;
    margin: 0.0vw 0 2.0vw -2.0vw;
    border-left: solid 1.2vw #2980b9;
    border-bottom: solid 0.8vw #d7d7d7;
}
</style>

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

### 2020/9/4 原山和之

---

# リポジトリ

* RustのWeb application frameworkです。
* リポジトリは[こちら](https://github.com/actix/actix-web)
* サンプル
    * https://actix.rs/actix-web/actix_web/
    * https://github.com/actix/examples
* [ベンチマーク結果](https://www.techempower.com/benchmarks/)

---

# 機能

* 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+


---

# アクターモデルについて

* アクターモデル実装はErlang/Elixirや、Scala/JavaにおけるAkkaが有名

* Rust
    * [Actor model by Rust with Actix](https://qiita.com/atsuk/items/6b620484aad019638795)
    * [Tatsuya Kawano](https://qiita.com/tatsuya6502)

* アクターモデルに関して
    * https://www.slideshare.net/TakamasaMitsuji/ss-13626473
    * https://tech-lab.sios.jp/archives/8738

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

---

# 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

```rust
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!")
}
```

```rust
#[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](https://actix.rs/docs/http_server_init/)

---

# HttpServerについて2
main.rs

```rust

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();
}

```

* ##### [チュートリアル](https://actix.rs/docs/server/)

---

# Appについて1
* AppはHttpServerにおける1つのworkerが保持するアプリケーションインスタンス
    * Routing
    * State
    * Middleware

* ##### Appは主にHttpServer作成時にnewメソッドの引数としてAppを作成するFactoryを渡し、HttpServerが各worker threadを立ち上げる際に渡されたFactoryを実行することで作成されます。

* ##### 特筆すべき点としては、各worker threadで動作するAppは全て別のインスタンスであるということです。

---

# Appについて2

* main.rs

```rust

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全体で共有したい情報

```rust

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ファイルを下記に

```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
```

---

# ホットリロード

* main.rs

```rust

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"
```

```rust
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アクセスについて話せたらなと思います。

---

# ツール
* [Postman](https://www.postman.com/downloads/)
* [Postman - Chrom Extention](https://chrome.google.com/webstore/detail/postman-interceptor/aicmkgpgakddgnaphhhpliifpcfhicfo?hl=ja)

# 参考
[Rustでwebサーバハンズオン！ ~actix-web & postgres~ Part1](https://qiita.com/susiyaki/items/ea4a82d15570d5af9a2b)

[Web Application Server by Rust with actix-web](https://qiita.com/atsuk/items/c04fffe4ed0e73a67cd0)

[systemfd](https://github.com/mitsuhiko/systemfd)

---

