2022-08-28 18:39:28 +01:00
|
|
|
use actix_web::{get, web::Data, App, HttpServer};
|
2020-06-22 21:16:25 +08:00
|
|
|
use redis_tang::{Builder, Pool, RedisManager};
|
|
|
|
|
2020-09-12 16:49:45 +01:00
|
|
|
#[actix_web::main]
|
2020-06-22 21:16:25 +08:00
|
|
|
async fn main() -> std::io::Result<()> {
|
2020-06-28 06:15:34 +08:00
|
|
|
let redis_url =
|
|
|
|
std::env::var("REDIS_URL").unwrap_or_else(|_| String::from("redis://127.0.0.1"));
|
|
|
|
|
2020-06-22 21:16:25 +08:00
|
|
|
let num_cpus = num_cpus::get();
|
|
|
|
|
2020-06-28 06:15:34 +08:00
|
|
|
// a shared redis pool for work load comparison.
|
|
|
|
let pool = pool_builder(num_cpus, redis_url.as_str())
|
|
|
|
.await
|
|
|
|
.expect("fail to build pool");
|
2020-09-12 16:49:45 +01:00
|
|
|
|
2022-02-02 15:13:08 +00:00
|
|
|
let pool = Data::new(RedisWrapper(pool));
|
2020-06-22 21:16:25 +08:00
|
|
|
|
|
|
|
HttpServer::new(move || {
|
2020-06-28 06:15:34 +08:00
|
|
|
let redis_url = redis_url.clone();
|
|
|
|
|
2020-06-22 21:16:25 +08:00
|
|
|
App::new()
|
2022-02-02 15:13:08 +00:00
|
|
|
.app_data(pool.clone())
|
2020-06-22 21:16:25 +08:00
|
|
|
// a dummy data_factory implementation
|
|
|
|
.data_factory(|| {
|
|
|
|
/*
|
2020-06-28 06:15:34 +08:00
|
|
|
App::data_factory would accept a future as return type and poll the future when
|
|
|
|
App is initialized.
|
2020-06-22 21:16:25 +08:00
|
|
|
|
2020-06-28 06:15:34 +08:00
|
|
|
The Output of the future must be Result<T, E> and T will be the transformed to
|
|
|
|
App::Data<T> that can be extracted from handler/request.
|
2020-06-22 21:16:25 +08:00
|
|
|
(The E will only be used to trigger a log::error.)
|
|
|
|
|
2020-06-28 06:15:34 +08:00
|
|
|
This data is bound to worker thread and you get an instance of it for every
|
|
|
|
worker of the HttpServer.(hence the name data_factory)
|
|
|
|
*. It is NOT shared between workers
|
|
|
|
(unless the underlying data is a smart pointer like Arc<T>).
|
2020-06-22 21:16:25 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
async {
|
|
|
|
// 123usize would be transformed into Data<usize>
|
|
|
|
Ok::<usize, ()>(123)
|
|
|
|
}
|
|
|
|
})
|
2020-06-28 06:15:34 +08:00
|
|
|
// a data_factory redis pool for work load comparison.
|
|
|
|
.data_factory(move || pool_builder(1, redis_url.clone()))
|
|
|
|
.service(pool_shared_prebuilt)
|
|
|
|
.service(pool_local)
|
2020-06-22 21:16:25 +08:00
|
|
|
})
|
2022-02-17 20:22:36 +00:00
|
|
|
.bind(("127.0.0.1", 8080))?
|
2020-06-22 21:16:25 +08:00
|
|
|
.run()
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
|
2020-06-28 06:15:34 +08:00
|
|
|
/*
|
|
|
|
This pool is shared between workers. We have all redis connections spawned tasks on main thread
|
|
|
|
therefore it puts too much pressure on one thread.
|
|
|
|
*. This is the case for redis::aio::MultiplexedConnection and it may not apply to other async
|
|
|
|
redis connection type.
|
|
|
|
*/
|
|
|
|
#[get("/pool")]
|
|
|
|
async fn pool_shared_prebuilt(pool: Data<RedisWrapper>) -> &'static str {
|
|
|
|
ping(&pool.as_ref().0).await
|
2020-06-22 21:16:25 +08:00
|
|
|
}
|
|
|
|
|
2020-06-28 06:15:34 +08:00
|
|
|
/*
|
|
|
|
This pool is built with App::data_factory and we have 2 connections fixed for every worker.
|
|
|
|
It's evenly distributed and have no cross workers synchronization.
|
|
|
|
*/
|
|
|
|
#[get("/pool2")]
|
2020-06-22 21:16:25 +08:00
|
|
|
async fn pool_local(data: Data<usize>, pool: Data<Pool<RedisManager>>) -> &'static str {
|
|
|
|
assert_eq!(data.get_ref(), &123);
|
|
|
|
|
2020-06-28 06:15:34 +08:00
|
|
|
ping(pool.as_ref()).await
|
2020-06-22 21:16:25 +08:00
|
|
|
}
|
|
|
|
|
2020-06-28 06:15:34 +08:00
|
|
|
// boiler plate for redis pool
|
2020-06-22 21:16:25 +08:00
|
|
|
#[derive(Clone)]
|
2020-06-28 06:15:34 +08:00
|
|
|
struct RedisWrapper(Pool<RedisManager>);
|
2020-06-22 21:16:25 +08:00
|
|
|
|
2020-06-28 06:15:34 +08:00
|
|
|
async fn pool_builder(
|
|
|
|
num_cpus: usize,
|
|
|
|
redis_url: impl redis::IntoConnectionInfo,
|
|
|
|
) -> Result<Pool<RedisManager>, ()> {
|
|
|
|
let mgr = RedisManager::new(redis_url);
|
2020-06-22 21:16:25 +08:00
|
|
|
Builder::new()
|
|
|
|
.always_check(false)
|
|
|
|
.idle_timeout(None)
|
|
|
|
.max_lifetime(None)
|
|
|
|
.min_idle(num_cpus * 2)
|
|
|
|
.max_size(num_cpus * 2)
|
|
|
|
.build(mgr)
|
|
|
|
.await
|
|
|
|
.map_err(|_| ())
|
|
|
|
}
|
2020-06-28 06:15:34 +08:00
|
|
|
|
|
|
|
async fn ping(pool: &Pool<RedisManager>) -> &'static str {
|
|
|
|
let mut client = pool.get().await.unwrap().clone();
|
|
|
|
|
|
|
|
redis::cmd("PING")
|
|
|
|
.query_async::<_, ()>(&mut client)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
"Done"
|
|
|
|
}
|