目的
以前書いた記事のバージョンアップ版です。
RustのWebフレームワークgothamでPostgreSQLとRedisにアクセスする
以前との違いはasync/awaitに対応して、Gothamのバージョンを0.6にしています。Tokioも1系になっています。
コード
docker-compose
docker-compose.yaml
version: '3.8'
services:
api:
image: rust:buster
working_dir: /apps/api
environment:
- CARGO_BUILD_TARGET_DIR=/tmp/target
- CARGO_TARGET_DIR=/tmp/target
volumes:
- ./apps/api:/apps/api
- rust_target:/tmp/target
- rust_cache:/usr/local/cargo/registry
security_opt:
- seccomp:unconfined
tty: true
ports:
- "7878:7878"
depends_on:
- db
- redis
db:
image: postgres:13-buster
volumes:
- postgres:/var/lib/postgresql/data
ports:
- "5432:5432"
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: app
redis:
image: redis:6.2-buster
command: redis-server --appendonly yes
ports:
- "6379:6379"
volumes:
- redis:/var/lib/redis/data
volumes:
rust_cache:
rust_target:
postgres:
redis:
Rust
Cargo.toml
[package]
name = "api"
version = "0.1.0"
edition = "2018"
[dependencies]
anyhow = "^1"
bb8-postgres = "^0.7"
bb8-redis = "^0.10"
http = "^0.2"
gotham = "^0.6"
gotham_derive = "^0.6"
mime = "^0.3"
serde = { version = "^1", features = ["derive"] }
tokio = { version = "^1", features = ["macros"] }
main.rs
use gotham::{
handler::{SimpleHandlerResult},
helpers::http::response::create_response,
middleware::state::StateMiddleware,
pipeline::{single::single_pipeline, single_middleware},
router::{builder::*, Router},
state::{FromState, State},
};
#[macro_use]
extern crate gotham_derive;
#[derive(Clone, StateData)]
pub struct ConfigData {
pub pg: bb8_postgres::bb8::Pool<
bb8_postgres::PostgresConnectionManager<bb8_postgres::tokio_postgres::NoTls>,
>,
pub redis: bb8_redis::bb8::Pool<bb8_redis::RedisConnectionManager>,
}
impl std::panic::RefUnwindSafe for ConfigData {}
impl ConfigData {
pub async fn new(pg_url: &str, redis_url: &str) -> anyhow::Result<Self> {
let pg_manager = bb8_postgres::PostgresConnectionManager::new(
pg_url.parse().unwrap(),
bb8_postgres::tokio_postgres::NoTls,
);
let pg_pool = bb8_postgres::bb8::Pool::builder().build(pg_manager).await?;
let redis_manager = bb8_redis::RedisConnectionManager::new(redis_url).unwrap();
let redis_pool = bb8_redis::bb8::Pool::builder()
.build(redis_manager)
.await
.unwrap();
Ok(Self {
pg: pg_pool,
redis: redis_pool,
})
}
}
pub async fn say_hello(state: &mut State) -> SimpleHandlerResult {
let content = {
let data = ConfigData::borrow_from(&state);
let mut redis = data.redis.get().await?;
let pg = data.pg.get().await?;
let redis_res: String = bb8_redis::redis::cmd("GET")
.arg("aaa")
.query_async(&mut *redis)
.await
.unwrap_or("".to_string());
let pg_res: String = pg
.query("SELECT $1::TEXT", &[&"予定表〜①ハンカクだ3".to_string()])
.await?
.get(0)
.unwrap()
.get(0);
format!("{}:{}", redis_res, pg_res)
};
let response = create_response(
&state,
gotham::hyper::StatusCode::OK,
mime::TEXT_PLAIN,
content,
);
Ok(response)
}
async fn router(config_data: ConfigData) -> Router {
let middleware = StateMiddleware::new(config_data);
let pipeline = single_middleware(middleware);
let (chain, pipelines) = single_pipeline(pipeline);
build_router(chain, pipelines, |route| {
route.get("/").to_async_borrowing(say_hello);
})
}
#[tokio::main]
async fn main() {
let pg_url = std::env::var("PG_URL").unwrap();
let redis_url = std::env::var("REDIS_URL").unwrap();
let config_data = ConfigData::new(&pg_url, &redis_url).await.unwrap();
let addr = "0.0.0.0:7878";
println!("Listening for requests at http://{}", addr);
let _ = gotham::init_server(addr, router(config_data).await).await;
}
PG_URL=postgresql://user:pass@db/app REDIS_URL=redis://redis:6379 cargo run
おまけ
コネクションプールはbb8, deadpool, r2d2がありますが、実は最初deadpoolで試していたんですが、redisはうまく来ましたがpostgresqlがランタイムが無いみたいなエラーで出て動きませんでした。本来ならdeadpool-postgresを使うだけでdefaultでランタイムが指定されるはずなんですが、うまくいきません。r2d2は非同期をサポートしてなかったのでbb8に行きつきました。