2022/10/01
[Rust]axumとdragonflyを使ったWebsocket Chatのサンプル実装
概要
少し週末に時間が取れたので、技術調査を兼ねてWebsocketを使ったChatアプリのサンプル実装を行いました。
以下の2点を試してみたかったというのが主な動機です。
できたものはこちらのgithubリポジトリに公開しています。
axum
弊社ではここ最近はactix-webをずっと使ってきていましたが、
ちょっと"つらみ"な部分もいくつかあったので、その代替となりうるかどうかも含めて実際に使ってみました。
結論から言うと、axumの使用感はほとんどactix-webと変わらないので、無理に移行しなくても良いのかな?と言う気がします。
また、websocket chatに関しては、(特に大規模なら)actix-webの方が実装しやすそうな気さえします。
ただ、以下の点で移行しても良いのかなと思っています。
- やや書きやすくなっている点もある
- 書き方があまり変わらないので、移行しやすい
- tracingとloggerの連携がうまくできればかなり嬉しい
dragonfly
dragonflyはredisを最新の技術で書き換えたものらしく、Redisの25倍のパフォーマンスがあると公式ではうたっています。
Dragonfly reaches x25 performance compared to Redis
また、redisのapi互換があるめ、redis用のscriptがそのまま動くのがかなり魅力的でした。
今回は主にpublish / subscribeを試してみましたが・・
以下のように redis
crateを使って、アクセスすることができました。
Cargo.toml
redis = { version = "0.21.6", features = ["r2d2"] }
r2d2 = "0.8.8"
以下は、publish / subscribe部分のコードで、テストコードも作成しました。
use crate::{Result};
use redis::{Commands, ConnectionLike, FromRedisValue, PubSub, ToRedisArgs};
pub type RedisConnection = redis::Connection;
pub fn publish<K: ToRedisArgs, V: ToRedisArgs>(
conn: &mut RedisConnection,
channel: K,
value: V,
) -> Result<()> {
conn.publish(channel, value).map_err(Into::into)
}
pub fn subscribe<K: ToRedisArgs>(conn: &mut RedisConnection, channel: K) -> Result<PubSub> {
let mut pubsub = conn.as_pubsub();
pubsub.subscribe(channel)?;
Ok(pubsub)
}
#[cfg(test)]
mod test {
use super::*;
use crate::*;
#[test]
#[serial_test::serial]
fn test_publish_subscribe() {
dotenv::dotenv().ok();
let redis_url = std::env::var("REDIS_URL").unwrap_or("redis://localhost:6379/0".to_owned());
let pool = new_pool(redis_url, 2).unwrap();
let channel = "channel1";
// subscribe
let pool1 = pool.clone();
let handle1 = std::thread::spawn(move || {
let mut connection = pool1.get().unwrap();
let mut sub = subscribe(&mut connection, channel).unwrap();
loop {
let msg = sub.get_message().unwrap();
println!("got message: {:?}", msg);
let payload: String = msg.get_payload().unwrap();
if &payload == "fin" {
break;
}
}
});
let pool2 = pool.clone();
let handle2 = std::thread::spawn(move || {
let mut connection = pool2.get().unwrap();
publish(&mut connection, channel, "This is the first message.").unwrap();
publish(&mut connection, channel, "2nd message.").unwrap();
publish(&mut connection, channel, "3rd message.").unwrap();
publish(&mut connection, channel, "fin").unwrap();
});
let _ = handle2.join().unwrap();
let _ = handle1.join().unwrap();
}
}
まとめ
- dragonflyはかなり良さそうなので、今後積極的に採用します。
- axumはもう少し調査をしてみて、良さそうなら移行しようと思います。