<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
Redis一直是網路生態系統的重要組成部分,它經常用作快取、訊息代理或簡單地用作資料儲存。
在這篇文章中,我們將演示如何在一個Rust web應用程式中使用Redis。
我們將探索兩種種使用Redis的方法:
使用同步連線池
使用非同步連線池
對於同步池,我們使用基於r2d2庫的r2d2-redis。我們在非同步解決方案中使用mobc,還有許多其他非同步連線池,如deadpool和bb8,它們都以類似的方式工作。
話不多說,讓我們開始吧!
新建一個專案:
cargo new rust-redis-web-example
在Cargo.toml中加入依賴:
[dependencies]</code> <code>tokio = { version = "1.19", features = ["full"] }</code> <code>warp = "0.3.2"</code> <code>redis = "0.21"</code> <code>r2d2_redis = "0.14"</code> <code>mobc-redis = "0.7"</code> <code>mobc = "0.7"</code> <code>thiserror = "1.0"
首先,讓我們設定一些共用型別,在main.rs中:
type WebResult= std::result::Result;</code> <code>type Result= std::result::Result;</code> <code>const REDIS_CON_STRING: &str = "redis://127.0.0.1/";
定義這兩個Result型別是為了節省一些輸入,並表示內部Errors (Result)和外部Errors (WebResult)。
接下來,定義這個內部error型別併為其實現Reject,以便它可以處理從程式返回的HTTP錯誤。
#[derive(Error, Debug)]</code> <code>pub enum Error {</code> <code> #[error("mobc error: {0}")]</code> <code> MobcError(#[from] MobcError),</code> <code> #[error("r2d2 error: {0}")]</code> <code> R2D2Error(#[from] R2D2Error),</code> <code>}</code> <code>#[derive(Error, Debug)]</code> <code>pub enum MobcError {</code> <code> #[error("could not get redis connection from pool : {0}")]</code> <code> RedisPoolError(mobc::Error),</code> <code> #[error("error parsing string from redis result: {0}")]</code> <code> RedisTypeError(mobc_redis::redis::RedisError),</code> <code> #[error("error executing redis command: {0}")]</code> <code> RedisCMDError(mobc_redis::redis::RedisError),</code> <code> #[error("error creating Redis client: {0}")]</code> <code> RedisClientError(mobc_redis::redis::RedisError),</code> <code>}</code> <code>#[derive(Error, Debug)]</code> <code>pub enum R2D2Error {</code> <code> #[error("could not get redis connection from pool : {0}")]</code> <code> RedisPoolError(r2d2_redis::r2d2::Error),</code> <code> #[error("error parsing string from redis result: {0}")]</code> <code> RedisTypeError(r2d2_redis::redis::RedisError),</code> <code> #[error("error executing redis command: {0}")]</code> <code> RedisCMDError(r2d2_redis::redis::RedisError),</code> <code> #[error("error creating Redis client: {0}")]</code> <code> RedisClientError(r2d2_redis::redis::RedisError),</code> <code>}</code> <code>impl warp::reject::Reject for Error {}
上面定義了通用的錯誤型別和我們將實現的每一種使用Redis方法的錯誤型別。錯誤本身只是處理連線、池的建立和命令執行等 錯誤。
r2d2 crate是第一個被廣泛使用的連線池,它現在仍然被廣泛使用。Redis的連線池是r2d2-redis crate。
在src目錄下建立r2d2_pool.rs檔案,因為我們現在使用的是連線池,所以這個池的建立也需要在r2d2模組中處理。
use crate::{R2D2Error::*, Result, REDIS_CON_STRING};</code> <code>use r2d2_redis::redis::{Commands, FromRedisValue};</code> <code>use r2d2_redis::{r2d2, RedisConnectionManager};</code> <code>use std::time::Duration;</code> <code>pub type R2D2Pool = r2d2::Pool;</code> <code>pub type R2D2Con = r2d2::PooledConnection;</code> <code>const CACHE_POOL_MAX_OPEN: u32 = 16;</code> <code>const CACHE_POOL_MIN_IDLE: u32 = 8;</code> <code>const CACHE_POOL_TIMEOUT_SECONDS: u64 = 1;</code> <code>const CACHE_POOL_EXPIRE_SECONDS: u64 = 60;</code> <code>pub fn connect() -> Result> {</code> <code> let manager = RedisConnectionManager::new(REDIS_CON_STRING).map_err(RedisClientError)?;</code> <code> r2d2::Pool::builder()</code> <code> .max_size(CACHE_POOL_MAX_OPEN)</code> <code> .max_lifetime(Some(Duration::from_secs(CACHE_POOL_EXPIRE_SECONDS)))</code> <code> .min_idle(Some(CACHE_POOL_MIN_IDLE))</code> <code> .build(manager)</code> <code> .map_err(|e| RedisPoolError(e).into())</code> <code>}
定義一些常數來設定池,如開啟和空閒連線,連線超時和連線的生命週期,池本身是使用RedisConnectionManager建立的,傳遞給它的引數是redis連線字串。
不要太擔心設定值,大多數連線池都有一些預設值,這些預設值將適用於基本應用程式。
我們需要一種方法來獲得連線池,然後向Redis設定和獲取值。
pub fn get_con(pool: &R2D2Pool) -> Result{</code> <code> pool.get_timeout(Duration::from_secs(CACHE_POOL_TIMEOUT_SECONDS))</code> <code> .map_err(|e| {</code> <code> eprintln!("error connecting to redis: {}", e);</code> <code> RedisPoolError(e).into()</code> <code> })</code> <code>}</code> <code>pub fn set_str(pool: &R2D2Pool, key: &str, value: &str, ttl_seconds: usize) -> Result<()> {</code> <code> let mut con = get_con(&pool)?;</code> <code> con.set(key, value).map_err(RedisCMDError)?;</code> <code> if ttl_seconds > 0 {</code> <code> con.expire(key, ttl_seconds).map_err(RedisCMDError)?;</code> <code> }</code> <code> Ok(())</code> <code>}</code> <code>pub fn get_str(pool: &R2D2Pool, key: &str) -> Result{</code> <code> let mut con = get_con(&pool)?;</code> <code> let value = con.get(key).map_err(RedisCMDError)?;</code> <code> FromRedisValue::from_redis_value(&value).map_err(|e| RedisTypeError(e).into())</code> <code>}
我們嘗試從池中獲取連線,並設定超時時間。在set_str和get_str中,每次呼叫這些函數時都會呼叫get_con。
在src目錄下建立r2d2_pool.rs檔案,讓我們定義設定並建立連線池。
use crate::{MobcError::*, Result, REDIS_CON_STRING};</code> <code>use mobc::{Connection, Pool};</code> <code>use mobc_redis::redis::{AsyncCommands, FromRedisValue};</code> <code>use mobc_redis::{redis, RedisConnectionManager};</code> <code>use std::time::Duration;</code> <code>pub type MobcPool = Pool;</code> <code>pub type MobcCon = Connection;</code> <code>const CACHE_POOL_MAX_OPEN: u64 = 16;</code> <code>const CACHE_POOL_MAX_IDLE: u64 = 8;</code> <code>const CACHE_POOL_TIMEOUT_SECONDS: u64 = 1;</code> <code>const CACHE_POOL_EXPIRE_SECONDS: u64 = 60;</code> <code>pub async fn connect() -> Result{</code> <code> let client = redis::Client::open(REDIS_CON_STRING).map_err(RedisClientError)?;</code> <code> let manager = RedisConnectionManager::new(client);</code> <code> Ok(Pool::builder()</code> <code> .get_timeout(Some(Duration::from_secs(CACHE_POOL_TIMEOUT_SECONDS)))</code> <code> .max_open(CACHE_POOL_MAX_OPEN)</code> <code> .max_idle(CACHE_POOL_MAX_IDLE)</code> <code> .max_lifetime(Some(Duration::from_secs(CACHE_POOL_EXPIRE_SECONDS)))</code> <code> .build(manager))</code> <code>}
這和r2d2非常相似,這不是巧合;許多連線池庫都從r2d2出色的API中獲得了靈感。
async fn get_con(pool: &MobcPool) -> Result{</code> <code> pool.get().await.map_err(|e| {</code> <code> eprintln!("error connecting to redis: {}", e);</code> <code> RedisPoolError(e).into()</code> <code> })</code> <code>}</code> <code>pub async fn set_str(pool: &MobcPool, key: &str, value: &str, ttl_seconds: usize) -> Result<()> {</code> <code> let mut con = get_con(&pool).await?;</code> <code> con.set(key, value).await.map_err(RedisCMDError)?;</code> <code> if ttl_seconds > 0 {</code> <code> con.expire(key, ttl_seconds).await.map_err(RedisCMDError)?;</code> <code> }</code> <code> Ok(())</code> <code>}</code> <code>pub async fn get_str(pool: &MobcPool, key: &str) -> Result{</code> <code> let mut con = get_con(&pool).await?;</code> <code> let value = con.get(key).await.map_err(RedisCMDError)?;</code> <code> FromRedisValue::from_redis_value(&value).map_err(|e| RedisTypeError(e).into())</code> <code>}
現在看起來應該很熟悉了,傳入池並在開始時獲取連線,但這一次採用非同步方式,使用async和await。
下一步就是把它們結合到一個warp web應用中,修改main.rs:
use std::convert::Infallible;</code> <code>use mobc_pool::MobcPool;</code> <code>use r2d2_pool::R2D2Pool;</code> <code>use thiserror::Error;</code> <code>use warp::{Rejection, Filter, Reply};</code> <code>mod r2d2_pool;</code> <code>mod mobc_pool;</code> <code>type WebResult= std::result::Result;</code> <code>type Result= std::result::Result;</code> <code>const REDIS_CON_STRING: &str = "redis://127.0.0.1/";</code> <code>#[tokio::main]</code> <code>async fn main() {</code> <code> let mobc_pool = mobc_pool::connect().await.expect("can create mobc pool");</code> <code> let r2d2_pool = r2d2_pool::connect().expect("can create r2d2 pool");</code> <code> let mobc_route = warp::path!("mobc")</code> <code> .and(with_mobc_pool(mobc_pool.clone()))</code> <code> .and_then(mobc_handler);</code> <code> let r2d2_route = warp::path!("r2d2")</code> <code> .and(with_r2d2_pool(r2d2_pool.clone()))</code> <code> .and_then(r2d2_handler);</code> <code> let routes = mobc_route.or(r2d2_route);</code> <code> warp::serve(routes).run(([0, 0, 0, 0], 8080)).await;</code> <code>}</code> <code>fn with_mobc_pool(</code> <code> pool: MobcPool,</code> <code>) -> impl Filter+ Clone {</code> <code> warp::any().map(move || pool.clone())</code> <code>}</code> <code>fn with_r2d2_pool(</code> <code> pool: R2D2Pool,</code> <code>) -> impl Filter+ Clone {</code> <code> warp::any().map(move || pool.clone())</code> <code>}</code> <code>async fn mobc_handler(pool: MobcPool) -> WebResult{</code> <code> mobc_pool::set_str(&pool, "mobc_hello", "mobc_world", 60)</code> <code> .await</code> <code> .map_err(|e| warp::reject::custom(e))?;</code> <code> let value = mobc_pool::get_str(&pool, "mobc_hello")</code> <code> .await</code> <code> .map_err(|e| warp::reject::custom(e))?;</code> <code> Ok(value)</code> <code>}</code> <code>async fn r2d2_handler(pool: R2D2Pool) -> WebResult{</code> <code> r2d2_pool::set_str(&pool, "r2d2_hello", "r2d2_world", 60)</code> <code> .map_err(|e| warp::reject::custom(e))?;</code> <code> let value = r2d2_pool::get_str(&pool, "r2d2_hello").map_err(|e| warp::reject::custom(e))?;</code> <code> Ok(value)</code> <code>}
用Docker啟動一個本地Redis範例:
docker run -p 6379:6379 redis
接下來,執行 cargo run。
使用curl測試:
curl http://localhost:8080/r2d2</code> <code>curl http://localhost:8080/mobc
到此這篇關於在Rust web服務中使用Redis的文章就介紹到這了,更多相關Rust 使用Redis內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45