0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內(nèi)不再提示

文盤Rust--r2d2實現(xiàn)redis連接池

jf_wN0SrCdH ? 來源:京東云開發(fā)者 ? 作者:賈世聞 ? 2022-12-12 10:32 ? 次閱讀

我們在開發(fā)應用后端系統(tǒng)的時候經(jīng)常要和各種數(shù)據(jù)庫、緩存等資源打交道。這一期,我們聊聊如何訪問redis 并將資源池化。

在一個應用后端程序訪問redis主要要做的工作有兩個,單例和池化。

在后端應用集成redis,我們主要用到以下幾個crate:[once_cell](https://github.com/matklad/once_cell)、[redis-rs](https://github.com/redis-rs/redis-rs)、[r2d2](https://github.com/sfackler/r2d2).once_cell 實現(xiàn)單例;redis-rs 是 redis的 rust 驅(qū)動;r2d2 是一個池化連接的工具包。本期代碼均出現(xiàn)在[fullstack-rs](https://github.com/jiashiwen/fullstack-rs)項目中。[fullstack-rs](https://github.com/jiashiwen/fullstack-rs)是我新開的一個實驗性項目,目標是做一個類似[gin-vue-admin](https://github.com/flipped-aurora/gin-vue-admin)的集成開發(fā)框架。

redis資源的定義主要是在https://github.com/jiashiwen/fullstack-rs/blob/main/backend/src/resources/redis_resource.rs 中實現(xiàn)的。

一、redis-rs 封裝

在實際開發(fā)中,我們面對的redis資源可能是單實例也有可能是集群,在這里我們對redis-rs進行了簡單封裝,便于適應這兩種情況。

```rust
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Clone)]
#[serde(rename_all = "lowercase")]
pub struct RedisInstance {
    #[serde(default = "RedisInstance::urls_default")]
    pub urls: Vec,
    #[serde(default = "RedisInstance::password_default")]
    pub password: String,
    #[serde(default = "RedisInstance::instance_type_default")]
    pub instance_type: InstanceType,
}
```
RedisInstance,定義redis資源的描述,與配置文件相對應。詳細的配置描述可以參考 https://github.com/jiashiwen/fullstack-rs/blob/main/backend/src/configure/config_global.rs 文件中 RedisConfig 和 RedisPool 兩個 struct 描述。
```rust
#[derive(Clone)]
pub enum RedisClient {
    Single(redis::Client),
    Cluster(redis::ClusterClient),
}




impl RedisClient {
    pub fn get_redis_connection(&self) -> RedisResult {
        return match self {
            RedisClient::Single(s) => {
                let conn = s.get_connection()?;
                Ok(RedisConnection::new(conn)))
            }
            RedisClient::Cluster(c) => {
                let conn = c.get_connection()?;
                Ok(RedisConnection::new(conn)))
            }
        };
    }
}




pub enum RedisConnection {
    Single(Box),
    Cluster(Box),
}




impl RedisConnection {
    pub fn is_open(&self) -> bool {
        return match self {
            RedisConnection::Single(sc) => sc.is_open(),
            RedisConnection::Cluster(cc) => cc.is_open(),
        };
    }




    pub fn query(&mut self, cmd: &redis::Cmd) -> RedisResult {
        return match self {
            RedisConnection::Single(sc) => match sc.as_mut().req_command(cmd) {
                Ok(val) => from_redis_value(&val),
                Err(e) => Err(e),
            },
            RedisConnection::Cluster(cc) => match cc.req_command(cmd) {
                Ok(val) => from_redis_value(&val),
                Err(e) => Err(e),
            },
        };
    }
}
```

RedisClient 和 RedisConnection 對redis 的鏈接進行了封裝,用來實現(xiàn)統(tǒng)一的調(diào)用接口。

二、基于 r2d2 實現(xiàn) redis 連接池

以上,基本完成的reids資源的準備工作,下面來實現(xiàn)一個redis鏈接池。

```rust
#[derive(Clone)]
pub struct RedisConnectionManager {
    pub redis_client: RedisClient,
}




impl r2d2::ManageConnection for RedisConnectionManager {
    type Connection = RedisConnection;
    type Error = RedisError;




    fn connect(&self) -> Result {
        let conn = self.redis_client.get_redis_connection()?;
        Ok(conn)
    }




    fn is_valid(&self, conn: &mut RedisConnection) -> Result<(), Self::Error> {
        match conn {
            RedisConnection::Single(sc) => {
                redis::cmd("PING").query(sc)?;
            }
            RedisConnection::Cluster(cc) => {
                redis::cmd("PING").query(cc)?;
            }
        }
        Ok(())
    }




    fn has_broken(&self, conn: &mut RedisConnection) -> bool {
        !conn.is_open()
    }
}
```
利用 r2d2 來實現(xiàn)連接池需要實現(xiàn) r2d2::ManageConnection trait。connect 函數(shù)獲取連接;is_valid 函數(shù)校驗連通性;has_broken 判斷連接是否崩潰不可用。
```Rust
pub fn gen_redis_conn_pool() -> Result> {
    let config = get_config()?;
    let redis_client = config.redis.instance.to_redis_client()?;
    let manager = RedisConnectionManager { redis_client };
    let pool = r2d2::Pool::builder()
        .max_size(config.redis.pool.max_size as u32)
        .min_idle(Some(config.redis.pool.mini_idle as u32))
        .connection_timeout(Duration::from_secs(
            config.redis.pool.connection_timeout as u64,
        ))
        .build(manager)?;
    Ok(pool)
}
```

gen_redis_conn_pool 函數(shù)用來生成一個 redis 的連接池,根據(jù)配置文件來指定連接池的最大連接數(shù),最小閑置連接以及連接超時時長。

三、連接池單例實現(xiàn)

在后端開發(fā)中,對于單一資源一般采取單例模式避免重復產(chǎn)生實例的開銷。下面來聊一聊如果構建一個全局的 redis 資源。

這一部分代碼在https://github.com/jiashiwen/fullstack-rs/blob/main/backend/src/resources/init_resources.rs 文件中。

```rust
pub static GLOBAL_REDIS_POOL: OnceCell> = OnceCell::new();
```

利用 OnceCell 構建全局靜態(tài)變量。

```rust
fn init_global_redis() {
    GLOBAL_REDIS_POOL.get_or_init(|| {
        let pool = match gen_redis_conn_pool() {
            Ok(it) => it,
            Err(err) => panic!("{}", err.to_string()),
        };
        pool
    });
}
```

init_global_redis 函數(shù),用來初始化 GLOBAL_REDIS_POOL 全局靜態(tài)變量。在一般的后端程序中,資源是強依賴,所以,初始化簡單粗暴,要么成功要么 panic。

四、資源調(diào)用

準備好 redis 資源后,我們聊聊如何調(diào)用。

調(diào)用例子在這里https://github.com/jiashiwen/fullstack-rs/blob/main/backend/src/httpserver/service/service_redis.rs

```rust
pub fn put(kv: KV) -> Result<()> {
    let conn = GLOBAL_REDIS_POOL.get();
    return match conn {
        Some(c) => {
            c.get()?
                .query(redis::cmd("set").arg(kv.Key).arg(kv.Value))?;
            Ok(())
        }
        None => Err(anyhow!("redis pool not init")),
    };
}
```

https://github.com/jiashiwen/fullstack-rs/tree/main/backend 這個工程里有從http入口開始到寫入redis的完整流程,http server 不在本文討論之列,就不贅述了,有興趣的同學可以去github看看。

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 數(shù)據(jù)庫

    關注

    7

    文章

    3712

    瀏覽量

    64025
  • 資源
    +關注

    關注

    0

    文章

    59

    瀏覽量

    17731
  • Redis
    +關注

    關注

    0

    文章

    368

    瀏覽量

    10780
  • Rust
    +關注

    關注

    1

    文章

    226

    瀏覽量

    6497

原文標題:文盤Rust -- r2d2 實現(xiàn)redis連接池

文章出處:【微信號:Rust語言中文社區(qū),微信公眾號:Rust語言中文社區(qū)】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    如何使用Rust連接Redis

    Redis是一款快速、開源、鍵值存儲數(shù)據(jù)庫,被廣泛應用于緩存、發(fā)布/訂閱系統(tǒng)、定時任務等場景中。Rust提供了很多Redis的客戶端庫,本教程將會介紹如何使用Rust
    的頭像 發(fā)表于 09-19 16:22 ?1968次閱讀

    淺析c3p0連接池

    【JavaWeb】c3p0連接池與MySQL
    發(fā)表于 09-11 11:37

    關于數(shù)據(jù)庫連接池總結

    數(shù)據(jù)庫連接池小結
    發(fā)表于 04-30 08:07

    數(shù)據(jù)庫連接池C3p0的使用方法

    數(shù)據(jù)庫連接池C3p0的使用
    發(fā)表于 06-04 17:31

    java原生程序redis連接怎么選擇

    java原生程序redis連接連接池連接和短連接)選擇問題
    發(fā)表于 06-10 16:33

    什么是數(shù)據(jù)庫連接池

    什么是數(shù)據(jù)庫連接池       數(shù)據(jù)庫連接是一種關鍵的有限的昂貴的資源,這一點在多用戶的網(wǎng)頁應用程序中體現(xiàn)得尤
    發(fā)表于 06-17 07:42 ?1239次閱讀

    c3p0-0.9.1.2 c3p0連接池jar包

    c3p0連接池jar包.......................................
    發(fā)表于 02-25 11:54 ?0次下載

    關于 Redis連接池解析

    一、關于連接池 一個數(shù)據(jù)庫服務器只擁有有限的資源,并且如果你沒有充分使用這些資源,你可以通過使用更多的連接來提高吞吐量。一旦所有的資源都在使用,那么你就不能通過增加更多的連接來提高吞吐量。事實上
    發(fā)表于 10-12 16:02 ?0次下載

    連接池工作原理

    連接池技術的核心思想是連接復用,通過建立一個數(shù)據(jù)庫連接池以及一套連接使用、分配和管理策略,使得該連接池中的
    的頭像 發(fā)表于 03-22 16:16 ?7571次閱讀

    數(shù)據(jù)庫連接池的優(yōu)點

    本視頻主要詳細介紹了數(shù)據(jù)庫連接池的優(yōu)點,分別是資源重用、更快的系統(tǒng)響應速度、統(tǒng)一的連接管理,避免數(shù)據(jù)庫連接泄漏、新的資源分配手段。
    的頭像 發(fā)表于 03-22 16:24 ?1.1w次閱讀

    數(shù)據(jù)庫連接池的設置怎么確定大小

    數(shù)據(jù)庫連接池的配置是開發(fā)者們常常搞出坑的地方,在配置數(shù)據(jù)庫連接池時,有幾個可以說是和直覺背道而馳的原則需要明確。
    的頭像 發(fā)表于 05-04 14:23 ?2641次閱讀
    數(shù)據(jù)庫<b class='flag-5'>連接池</b>的設置怎么確定大小

    Rust語言中r2d2基礎用法

    r2d2Rust語言的一個連接池模塊,可以用于管理和復用數(shù)據(jù)庫連接。它可以與多種數(shù)據(jù)庫進行交互,包括MySQL、PostgreSQL、SQLite等等。使用
    的頭像 發(fā)表于 09-19 16:25 ?2659次閱讀

    了解連接池、線程、內(nèi)存、異步請求

    可被重復使用像常見的線程、內(nèi)存、連接池、對象都具有以上的共同特點。 連接池 什么是數(shù)據(jù)庫連接池
    的頭像 發(fā)表于 11-09 14:44 ?869次閱讀
    了解<b class='flag-5'>連接池</b>、線程<b class='flag-5'>池</b>、內(nèi)存<b class='flag-5'>池</b>、異步請求<b class='flag-5'>池</b>

    MySQL與Redis數(shù)據(jù)庫連接池應用

    一、概念 數(shù)據(jù)庫連接池(Connection pooling)是程序啟動時建立足夠的數(shù)據(jù)庫連接,并將這些連接組成一個連接池,由程序動態(tài)地對池中的連接
    的頭像 發(fā)表于 11-10 16:40 ?415次閱讀
    MySQL與<b class='flag-5'>Redis</b>數(shù)據(jù)庫<b class='flag-5'>連接池</b>應用

    Java redis鎖怎么實現(xiàn)

    在Java中實現(xiàn)Redis鎖涉及到以下幾個方面:Redis的安裝配置、Redis連接池的使用、Redis
    的頭像 發(fā)表于 12-04 10:47 ?910次閱讀