1
0
mirror of https://github.com/actix/examples synced 2025-06-26 17:17:42 +02:00

restructure folders

This commit is contained in:
Rob Ede
2022-02-18 02:01:48 +00:00
parent 4d8573c3fe
commit cc3d356209
201 changed files with 52 additions and 49 deletions

2
databases/sqlite/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
weather.db
weather.db-*

View File

@ -0,0 +1,16 @@
[package]
name = "async_db"
version = "1.0.0"
edition = "2021"
[dependencies]
actix-web = "4.0.0-rc.2"
env_logger = "0.9"
futures-util = { version = "0.3", default-features = false, features = ["std"] }
log = "0.4"
r2d2 = "0.8"
r2d2_sqlite = "0.19"
rusqlite = "0.26"
serde = { version = "1", features = ["derive"] }
serde_json = "1"

View File

@ -0,0 +1,47 @@
Getting started using databases with Actix Web, asynchronously.
## Usage
### init database sqlite
From the root directory of this project:
```bash
bash db/setup_db.sh
```
This creates a sqlite database, weather.db, in the root.
### server
```bash
# if ubuntu : sudo apt-get install libsqlite3-dev
# if fedora : sudo dnf install libsqlite3x-devel
cargo run (or ``cargo watch -x run``)
# Started http server: 127.0.0.1:8080
```
### web client
[http://127.0.0.1:8080/asyncio_weather](http://127.0.0.1:8080/asyncio_weather)
[http://127.0.0.1:8080/parallel_weather](http://127.0.0.1:8080/parallel_weather)
### sqlite client
```bash
# if ubuntu : sudo apt-get install sqlite3
# if fedora : sudo dnf install sqlite3x
sqlite3 weather.db
sqlite> .tables
sqlite> select * from nyc_weather;
```
## Dependencies
On Ubuntu 19.10:
```
sudo apt install libsqlite3-dev
```

Binary file not shown.

View File

@ -0,0 +1,5 @@
This directory includes weather information obtained from NOAA for NYC Central Park: https://www.ncdc.noaa.gov/cdo-web/
# Setup Instructions
Set up a sqlite3 database by executing the setup_db.sh file: `bash sqlite_db.sh`

28
databases/sqlite/db/db.sql Executable file
View File

@ -0,0 +1,28 @@
CREATE TABLE nyc_weather(
STATION TEXT,
NAME TEXT,
DATE TEXT,
ACMH DOUBLE,
AWND DOUBLE,
FMTM DOUBLE,
PGTM DOUBLE,
PRCP DOUBLE,
PSUN DOUBLE,
SNOW DOUBLE,
SNWD DOUBLE,
TAVG DOUBLE,
TMAX DOUBLE,
TMIN DOUBLE,
TSUN DOUBLE,
WDF1 DOUBLE,
WDF2 DOUBLE,
WDF5 DOUBLE,
WDFG DOUBLE,
WDFM DOUBLE,
WSF1 DOUBLE,
WSF2 DOUBLE,
WSF5 DOUBLE,
WSFG DOUBLE,
WSFM DOUBLE
);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,4 @@
#!/usr/bin/env bash
cd $(dirname "$0")
sqlite3 ../weather.db < db.sql
sqlite3 -csv ../weather.db ".import nyc_centralpark_weather.csv nyc_weather"

123
databases/sqlite/src/db.rs Normal file
View File

@ -0,0 +1,123 @@
use actix_web::{error, web, Error};
use rusqlite::Statement;
use serde::{Deserialize, Serialize};
use std::{thread::sleep, time::Duration};
pub type Pool = r2d2::Pool<r2d2_sqlite::SqliteConnectionManager>;
pub type Connection = r2d2::PooledConnection<r2d2_sqlite::SqliteConnectionManager>;
type WeatherAggResult = Result<Vec<WeatherAgg>, rusqlite::Error>;
#[derive(Debug, Serialize, Deserialize)]
pub enum WeatherAgg {
AnnualAgg { year: i32, total: f64 },
MonthAgg { year: i32, month: i32, total: f64 },
}
#[allow(clippy::enum_variant_names)]
pub enum Queries {
GetTopTenHottestYears,
GetTopTenColdestYears,
GetTopTenHottestMonths,
GetTopTenColdestMonths,
}
pub async fn execute(pool: &Pool, query: Queries) -> Result<Vec<WeatherAgg>, Error> {
let pool = pool.clone();
let conn = web::block(move || pool.get())
.await?
.map_err(error::ErrorInternalServerError)?;
web::block(move || {
// simulate an expensive query, see comments at top of main.rs
sleep(Duration::from_secs(2));
match query {
Queries::GetTopTenHottestYears => get_hottest_years(conn),
Queries::GetTopTenColdestYears => get_coldest_years(conn),
Queries::GetTopTenHottestMonths => get_hottest_months(conn),
Queries::GetTopTenColdestMonths => get_coldest_months(conn),
}
})
.await?
.map_err(error::ErrorInternalServerError)
}
fn get_hottest_years(conn: Connection) -> WeatherAggResult {
let stmt = conn.prepare(
"
SELECT cast(strftime('%Y', date) as int) as theyear,
sum(tmax) as total
FROM nyc_weather
WHERE tmax <> 'TMAX'
GROUP BY theyear
ORDER BY total DESC LIMIT 10",
)?;
get_rows_as_annual_agg(stmt)
}
fn get_coldest_years(conn: Connection) -> WeatherAggResult {
let stmt = conn.prepare(
"
SELECT cast(strftime('%Y', date) as int) as theyear,
sum(tmax) as total
FROM nyc_weather
WHERE tmax <> 'TMAX'
GROUP BY theyear
ORDER BY total ASC LIMIT 10",
)?;
get_rows_as_annual_agg(stmt)
}
fn get_rows_as_annual_agg(mut statement: Statement) -> WeatherAggResult {
statement
.query_map([], |row| {
Ok(WeatherAgg::AnnualAgg {
year: row.get(0)?,
total: row.get(1)?,
})
})
.and_then(Iterator::collect)
}
fn get_hottest_months(conn: Connection) -> WeatherAggResult {
let stmt = conn.prepare(
"SELECT cast(strftime('%Y', date) as int) as theyear,
cast(strftime('%m', date) as int) as themonth,
sum(tmax) as total
FROM nyc_weather
WHERE tmax <> 'TMAX'
GROUP BY theyear, themonth
ORDER BY total DESC LIMIT 10",
)?;
get_rows_as_month_agg(stmt)
}
fn get_coldest_months(conn: Connection) -> WeatherAggResult {
let stmt = conn.prepare(
"SELECT cast(strftime('%Y', date) as int) as theyear,
cast(strftime('%m', date) as int) as themonth,
sum(tmax) as total
FROM nyc_weather
WHERE tmax <> 'TMAX'
GROUP BY theyear, themonth
ORDER BY total ASC LIMIT 10",
)?;
get_rows_as_month_agg(stmt)
}
fn get_rows_as_month_agg(mut statement: Statement) -> WeatherAggResult {
statement
.query_map([], |row| {
Ok(WeatherAgg::MonthAgg {
year: row.get(0)?,
month: row.get(1)?,
total: row.get(2)?,
})
})
.and_then(Iterator::collect)
}

View File

@ -0,0 +1,78 @@
//! Actix Web Asynchronous Database Example
//!
//! This project illustrates expensive and blocking database requests that runs
//! in a thread-pool using `web::block` with two examples:
//!
//! 1. An asynchronous handler that executes 4 queries in *sequential order*,
//! collecting the results and returning them as a single serialized json object
//!
//! 2. An asynchronous handler that executes 4 queries in *parallel*,
//! collecting the results and returning them as a single serialized json object
//!
//! Note: The use of sleep(Duration::from_secs(2)); in db.rs is to make performance
//! improvement with parallelism more obvious.
use std::io;
use actix_web::{middleware, web, App, Error as AWError, HttpResponse, HttpServer};
use futures_util::future::join_all;
use r2d2_sqlite::{self, SqliteConnectionManager};
mod db;
use db::{Pool, Queries};
/// Version 1: Calls 4 queries in sequential order, as an asynchronous handler
async fn asyncio_weather(db: web::Data<Pool>) -> Result<HttpResponse, AWError> {
let result = vec![
db::execute(&db, Queries::GetTopTenHottestYears).await?,
db::execute(&db, Queries::GetTopTenColdestYears).await?,
db::execute(&db, Queries::GetTopTenHottestMonths).await?,
db::execute(&db, Queries::GetTopTenColdestMonths).await?,
];
Ok(HttpResponse::Ok().json(result))
}
/// Version 2: Calls 4 queries in parallel, as an asynchronous handler
/// Returning Error types turn into None values in the response
async fn parallel_weather(db: web::Data<Pool>) -> Result<HttpResponse, AWError> {
let fut_result = vec![
Box::pin(db::execute(&db, Queries::GetTopTenHottestYears)),
Box::pin(db::execute(&db, Queries::GetTopTenColdestYears)),
Box::pin(db::execute(&db, Queries::GetTopTenHottestMonths)),
Box::pin(db::execute(&db, Queries::GetTopTenColdestMonths)),
];
let result: Result<Vec<_>, _> = join_all(fut_result).await.into_iter().collect();
Ok(HttpResponse::Ok().json(result.map_err(AWError::from)?))
}
#[actix_web::main]
async fn main() -> io::Result<()> {
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
// connect to SQLite DB
let manager = SqliteConnectionManager::file("weather.db");
let pool = Pool::new(manager).unwrap();
log::info!("starting HTTP server at http://localhost:8080");
// start HTTP server
HttpServer::new(move || {
App::new()
// store db pool as Data object
.app_data(web::Data::new(pool.clone()))
.wrap(middleware::Logger::default())
.service(
web::resource("/asyncio_weather").route(web::get().to(asyncio_weather)),
)
.service(
web::resource("/parallel_weather")
.route(web::get().to(parallel_weather)),
)
})
.bind(("127.0.0.1", 8080))?
.workers(2)
.run()
.await
}