RustでAPIサーバを
作り始めた話
Shinjuku.rs #3 @FORCIA
2019/3/12
ゆで卵
自己紹介
- 名前: ゆで卵
- Twitter: @takayukib
- Github: yudetamago
- フリーランスのブロックチェーンエンジニア
- TokenPocket Inc., LayerX Inc.
経緯と今日の内容
- 某バックエンド(APIサーバ+DB)を作ることになった
どうせ自分しかメンテしないしRustで作ろう- とりあえず使うライブラリは決めた
- なんか色々分からなかったりしたのでライブラリのコード読んでる←イマココ
WAFどれ使う
- 活発に開発されている
- 低レイヤーすぎない (✗ hyper)
- 情報源多いほうが良い
- Rust stable versionで使える (✗ Rocket)
- ORMやConnection Poolと連携しやすい
- (出来れば非同期に対応してるやつ)
- (出来ればフルスタックすぎないやつ)
- (出来れば枯れてるやつがいいけどたぶんそんなものは無い)
使うやつ
- WAF: actix_web
- ORM: diesel
- Connection Pool: r2d2
参考文献
迷ったところ・
はまったところの
軽いコードリーディング
// Option 1: passed as a parameter to a handler function
fn index((params, info): (Path<(String, String,)>, Json<MyInfo>)) -> HttpResponse {
...
}
// Option 2: accessed by calling extract() on the Extractor
use actix_web::FromRequest;
fn index(req: &HttpRequest) -> HttpResponse {
let params = Path::<(String, String)>::extract(req);
let info = Json::<MyInfo>::extract(req);
...
}
https://actix.rs/docs/extractors/
途中に型?🤔
// Expression paths ///////////////////////////////////////////////////////////////
a::b::c // reference to a function `c` in module `a::b`
a::<T1, T2> // the function `a` instantiated with type arguments `T1`, `T2`
Vec::<T>::new // reference to the function `new` associated with `Vec<T>`
<Vec<T> as SomeTrait>::some_fn
// reference to the function `some_fn` associated with `SomeTrait`,
// as implemented by `Vec<T>`
T::size_of // the function `size_of` associated with the type or trait `T`
<T>::size_of // the function `size_of` associated with `T` _viewed as a type_
<T as SizeOf>::size_of
// the function `size_of` associated with `T`'s impl of `SizeOf`
https://github.com/rust-lang/rfcs/blob/master/text/0195-associated-items.md#referencing-associated-items
- 前半の:: 型パラメータ
- 後半の:: structのfnへの参照
// handler.rs
pub trait FromRequest<S>: Sized {
...
fn from_request(req: &HttpRequest<S>, cfg: &Self::Config) -> Self::Result;
...
fn extract(req: &HttpRequest<S>) -> Self::Result {
Self::from_request(req, &Self::Config::default())
}
}
// extractor.rs
pub struct Path<T> {
inner: T,
}
impl<T, S> FromRequest<S> for Path<T>
where
T: DeserializeOwned,
{
...
#[inline]
fn from_request(req: &HttpRequest<S>, cfg: &Self::Config) -> Self::Result {
let req = req.clone();
let req2 = req.clone();
let err = Rc::clone(&cfg.ehandler);
de::Deserialize::deserialize(PathDeserializer::new(&req, cfg.decode))
.map_err(move |e| (*err)(e, &req2))
.map(|inner| Path { inner })
}
}
https://github.com/actix/actix-web/blob/cc7f6b5eef4d9e2f67265f421e8d1e39fdf70168/src/extractor.rs
https://github.com/actix/actix-web/blob/cc7f6b5eef4d9e2f67265f421e8d1e39fdf70168/src/handler.rs
https://github.com/actix/examples/blob/4d26988edfc15b79ccd2edabb955cbcc975e361e/diesel/src/main.rs
...
let manager = ConnectionManager::<SqliteConnection>::new("test.db");
let pool = r2d2::Pool::builder()
.build(manager)
.expect("Failed to create pool.");
....
https://github.com/outersky/r2d2-mysql/tree/616b70b2aa27fac557777b242c52925d0a9896eb
...
let manager = r2d2_mysql::MysqlConnectionManager::new(db_url).unwrap();
let pool = Arc::new(r2d2::Pool::new(config, manager).unwrap());
...
Arc::new必要なのか🤔
// config.rs
pub struct Builder<M>
where
M: ManageConnection,
{
...
}
impl<M> Builder<M>
where
M: ManageConnection,
{
...
pub fn build(self, manager: M) -> Result<Pool<M>, Error> {
let pool = self.build_unchecked(manager);
pool.wait_for_initialization()?;
Ok(pool)
}
...
}
// lib.rs
pub struct Pool<M: ManageConnection>(Arc<SharedPool<M>>);
最初から付いてる
https://github.com/sfackler/r2d2/blob/4b50b30b9dbd030f399e9f725dd1659e72e2f20f/src/lib.rs
https://github.com/sfackler/r2d2/blob/4b50b30b9dbd030f399e9f725dd1659e72e2f20f/src/config.rs
ご清聴
ありがとうございました
RustでAPIサーバを作り始めた話
By yudetamago
RustでAPIサーバを作り始めた話
Shinjuku.rs #3
- 2,869